1 /*
2 * Copyright 2005 Ralf Joachim
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.castor.core.util;
17
18 import java.io.ByteArrayOutputStream;
19
20 /**
21 * Class decodes a Base64 encoded string back into the original byte representation
22 * that can be read as byte array.
23 *
24 * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
25 * @version $Revision: 6907 $ $Date: 2005-08-05 13:58:36 -0600 (Fri, 05 Aug 2005) $
26 * @since 0.9.9
27 */
28 public final class Base64Decoder {
29 /** Mask buffer to or with first sextet. */
30 private static final int SEXTET_1_MASK = 0x03FFFF;
31
32 /** Mask buffer to or with second sextet. */
33 private static final int SEXTET_2_MASK = 0xFC0FFF;
34
35 /** Mask buffer to or with third sextet. */
36 private static final int SEXTET_3_MASK = 0xFFF03F;
37
38 /** Mask buffer to or with forth sextet. */
39 private static final int SEXTET_4_MASK = 0xFFFFC0;
40
41 /** Number of bits to shift for one sextet. */
42 private static final int SHIFT_1_SEXTET = 6;
43
44 /** Number of bits to shift for two sextet. */
45 private static final int SHIFT_2_SEXTET = 12;
46
47 /** Number of bits to shift for three sextet. */
48 private static final int SHIFT_3_SEXTET = 18;
49
50 /** Second sextets in buffer. */
51 private static final int SEXTET_2 = 2;
52
53 /** Third sextets in buffer. */
54 private static final int SEXTET_3 = 3;
55
56 /** Forth sextets in buffer. */
57 private static final int SEXTET_4 = 4;
58
59 /** Mask an octet. */
60 private static final int OCTET_MASK = 0xFF;
61
62 /** Number of bits to shift for one octet. */
63 private static final int SHIFT_1_OCTET = 8;
64
65 /** Number of bits to shift for two octet. */
66 private static final int SHIFT_2_OCTET = 16;
67
68 /** White space character (out of range 0 - 63). */
69 private static final byte SPC = 127;
70
71 /** Padding character (out of range 0 - 63). */
72 private static final byte PAD = 64;
73
74 /** Array to translate base64 characters into sextet byte values. */
75 private static final byte[] MAP = {
76 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 00-07
77 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 08-0F
78 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 10-17
79 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 18-1F
80 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 20-27
81 SPC, SPC, SPC, 62, SPC, SPC, SPC, 63, // 28-2F ' + /'
82 52, 53, 54, 55, 56, 57, 58, 59, // 30-37 '01234567'
83 60, 61, SPC, SPC, SPC, PAD, SPC, SPC, // 38-3F '89 = '
84 SPC, 0, 1, 2, 3, 4, 5, 6, // 40-47 ' ABCDEFG'
85 7, 8, 9, 10, 11, 12, 13, 14, // 48-4F 'HIJKLMNO'
86 15, 16, 17, 18, 19, 20, 21, 22, // 50-57 'PQRSTUVW'
87 23, 24, 25, SPC, SPC, SPC, SPC, SPC, // 58-5F 'XYZ '
88 SPC, 26, 27, 28, 29, 30, 31, 32, // 60-67 ' abcdefg'
89 33, 34, 35, 36, 37, 38, 39, 40, // 68-6F 'hijklmno'
90 41, 42, 43, 44, 45, 46, 47, 48, // 70-77 'pqrstuvw'
91 49, 50, 51, SPC, SPC, SPC, SPC, SPC, // 78-7F 'xyz '
92 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 80-87
93 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 88-8F
94 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 90-97
95 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // 98-9F
96 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // A0-A7
97 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // A8-AF
98 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // B0-B7
99 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // B8-BF
100 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // C0-C7
101 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // C8-CF
102 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // D0-D7
103 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // D8-DF
104 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // E0-E7
105 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // E8-EF
106 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // F0-F7
107 SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, // F8-FF
108 };
109
110 /** 24-bit buffer to translate 4 sextets into 3 octets. */
111 private int _buffer = 0;
112
113 /** Number of octets in buffer. */
114 private int _sextets = 0;
115
116 /** Stream buffer for decoded octets waiting to be read. */
117 private ByteArrayOutputStream _stream = new ByteArrayOutputStream();
118
119 /**
120 * Decode given string into a decoded byte array.
121 *
122 * @param str Base64 String to be decoded.
123 * @return All decoded octets as byte array.
124 */
125 public static byte[] decode(final String str) {
126 Base64Decoder dec = new Base64Decoder();
127 dec.translate(str);
128 return dec.getByteArray();
129 }
130
131 /**
132 * Construct a default Base64Decoder waiting on calls to its translate()
133 * method.
134 */
135 public Base64Decoder() { }
136
137 /**
138 * Translate every base64 character from given string into a sextet byte value
139 * by using above translation array. The sextets are then shiftet into an buffer
140 * until the buffer contains 4 sextets which are then decoded into 3 octets.
141 * The translate and decode process is continued until all characters of given
142 * string are evaluated. If there are remaing sextets in the buffer they also
143 * will be converted into octets at the end. All the converted octets are added
144 * to the list for later read.
145 *
146 * @param string Base64 String to be decoded.
147 */
148 public void translate(final String string) {
149 int len = string.length();
150 int index = 0;
151 int data = MAP[string.charAt(index)];
152 while ((index < len) && (data != PAD)) {
153 if (data != SPC) {
154 if (_sextets == 0) {
155 _buffer = (_buffer & SEXTET_1_MASK) | (data << SHIFT_3_SEXTET);
156 } else if (_sextets == 1) {
157 _buffer = (_buffer & SEXTET_2_MASK) | (data << SHIFT_2_SEXTET);
158 } else if (_sextets == 2) {
159 _buffer = (_buffer & SEXTET_3_MASK) | (data << SHIFT_1_SEXTET);
160 } else {
161 _buffer = (_buffer & SEXTET_4_MASK) | data;
162 }
163
164 if ((++_sextets) == SEXTET_4) { decode(); }
165 }
166
167 if (++index < len) { data = MAP[string.charAt(index)]; }
168 }
169
170 if (_sextets > 0) { decodeWithPadding(); }
171 }
172
173 /**
174 * Decode 3 octets from buffer and add them to list of octets to read.
175 */
176 private void decode() {
177 _stream.write((byte) ((_buffer >> SHIFT_2_OCTET) & OCTET_MASK)); // octet 1
178 _stream.write((byte) ((_buffer >> SHIFT_1_OCTET) & OCTET_MASK)); // octet 2
179 _stream.write((byte) (_buffer & OCTET_MASK)); // octet 3
180 _buffer = 0;
181 _sextets = 0;
182 }
183
184 /**
185 * Decode the remaining octets from buffer and add them to list of octets to read.
186 */
187 private void decodeWithPadding() {
188 if (_sextets >= SEXTET_2) { // octet 1
189 _stream.write((byte) ((_buffer >> SHIFT_2_OCTET) & OCTET_MASK));
190 }
191 if (_sextets >= SEXTET_3) { // octet 2
192 _stream.write((byte) ((_buffer >> SHIFT_1_OCTET) & OCTET_MASK));
193 }
194 if (_sextets >= SEXTET_4) { // octet 3
195 _stream.write((byte) (_buffer & OCTET_MASK));
196 }
197 _buffer = 0;
198 _sextets = 0;
199 }
200
201 /**
202 * Get all decoded octets as byte array.
203 *
204 * @return All decoded octets as byte array.
205 */
206 public byte[] getByteArray() {
207 return _stream.toByteArray();
208 }
209 }