1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.util;
21
22 import java.security.InvalidParameterException;
23
24 /**
25 * Provides Base64 encoding and decoding as defined by RFC 2045.
26 *
27 * <p>This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
28 * from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
29 * Format of Internet Message Bodies</cite> by Freed and Borenstein.</p>
30 *
31 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
32 *
33 * This class was
34 * @author Apache Software Foundation commons codec (http://commons.apache.org/codec/)
35 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
36 */
37 public class Base64 {
38
39 /**
40 * Chunk size per RFC 2045 section 6.8.
41 *
42 * <p>The {@value} character limit does not count the trailing CRLF, but counts
43 * all other characters, including any equal signs.</p>
44 *
45 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
46 */
47 static final int CHUNK_SIZE = 76;
48
49 /**
50 * Chunk separator per RFC 2045 section 2.1.
51 *
52 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
53 */
54 static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
55
56 /**
57 * The base length.
58 */
59 static final int BASELENGTH = 255;
60
61 /**
62 * Lookup length.
63 */
64 static final int LOOKUPLENGTH = 64;
65
66 /**
67 * Used to calculate the number of bits in a byte.
68 */
69 static final int EIGHTBIT = 8;
70
71 /**
72 * Used when encoding something which has fewer than 24 bits.
73 */
74 static final int SIXTEENBIT = 16;
75
76 /**
77 * Used to determine how many bits data contains.
78 */
79 static final int TWENTYFOURBITGROUP = 24;
80
81 /**
82 * Used to get the number of Quadruples.
83 */
84 static final int FOURBYTE = 4;
85
86 /**
87 * Used to test the sign of a byte.
88 */
89 static final int SIGN = -128;
90
91 /**
92 * Byte used to pad output.
93 */
94 static final byte PAD = (byte) '=';
95
96 // Create arrays to hold the base64 characters and a
97 // lookup for base64 chars
98 private static byte[] base64Alphabet = new byte[BASELENGTH];
99
100 private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
101
102 // Populating the lookup and character arrays
103 static {
104 for (int i = 0; i < BASELENGTH; i++) {
105 base64Alphabet[i] = (byte) -1;
106 }
107 for (int i = 'Z'; i >= 'A'; i--) {
108 base64Alphabet[i] = (byte) (i - 'A');
109 }
110 for (int i = 'z'; i >= 'a'; i--) {
111 base64Alphabet[i] = (byte) (i - 'a' + 26);
112 }
113 for (int i = '9'; i >= '0'; i--) {
114 base64Alphabet[i] = (byte) (i - '0' + 52);
115 }
116
117 base64Alphabet['+'] = 62;
118 base64Alphabet['/'] = 63;
119
120 for (int i = 0; i <= 25; i++) {
121 lookUpBase64Alphabet[i] = (byte) ('A' + i);
122 }
123
124 for (int i = 26, j = 0; i <= 51; i++, j++) {
125 lookUpBase64Alphabet[i] = (byte) ('a' + j);
126 }
127
128 for (int i = 52, j = 0; i <= 61; i++, j++) {
129 lookUpBase64Alphabet[i] = (byte) ('0' + j);
130 }
131
132 lookUpBase64Alphabet[62] = (byte) '+';
133 lookUpBase64Alphabet[63] = (byte) '/';
134 }
135
136 private static boolean isBase64(byte octect) {
137 if (octect == PAD) {
138 return true;
139 } else if (base64Alphabet[octect] == -1) {
140 return false;
141 } else {
142 return true;
143 }
144 }
145
146 /**
147 * Tests a given byte array to see if it contains
148 * only valid characters within the Base64 alphabet.
149 *
150 * @param arrayOctect byte array to test
151 * @return true if all bytes are valid characters in the Base64
152 * alphabet or if the byte array is empty; false, otherwise
153 */
154 public static boolean isArrayByteBase64(byte[] arrayOctect) {
155
156 arrayOctect = discardWhitespace(arrayOctect);
157
158 int length = arrayOctect.length;
159 if (length == 0) {
160 // shouldn't a 0 length array be valid base64 data?
161 // return false;
162 return true;
163 }
164 for (int i = 0; i < length; i++) {
165 if (!isBase64(arrayOctect[i])) {
166 return false;
167 }
168 }
169 return true;
170 }
171
172 /**
173 * Encodes binary data using the base64 algorithm but
174 * does not chunk the output.
175 *
176 * @param binaryData binary data to encode
177 * @return Base64 characters
178 */
179 public static byte[] encodeBase64(byte[] binaryData) {
180 return encodeBase64(binaryData, false);
181 }
182
183 /**
184 * Encodes binary data using the base64 algorithm and chunks
185 * the encoded output into 76 character blocks
186 *
187 * @param binaryData binary data to encode
188 * @return Base64 characters chunked in 76 character blocks
189 */
190 public static byte[] encodeBase64Chunked(byte[] binaryData) {
191 return encodeBase64(binaryData, true);
192 }
193
194 /**
195 * Decodes an Object using the base64 algorithm. This method
196 * is provided in order to satisfy the requirements of the
197 * Decoder interface, and will throw a DecoderException if the
198 * supplied object is not of type byte[].
199 *
200 * @param pObject Object to decode
201 * @return An object (of type byte[]) containing the
202 * binary data which corresponds to the byte[] supplied.
203 * @throws InvalidParameterException if the parameter supplied is not
204 * of type byte[]
205 */
206 public Object decode(Object pObject) {
207 if (!(pObject instanceof byte[])) {
208 throw new InvalidParameterException("Parameter supplied to Base64 decode is not a byte[]");
209 }
210 return decode((byte[]) pObject);
211 }
212
213 /**
214 * Decodes a byte[] containing containing
215 * characters in the Base64 alphabet.
216 *
217 * @param pArray A byte array containing Base64 character data
218 * @return a byte array containing binary data
219 */
220 public byte[] decode(byte[] pArray) {
221 return decodeBase64(pArray);
222 }
223
224 /**
225 * Encodes binary data using the base64 algorithm, optionally
226 * chunking the output into 76 character blocks.
227 *
228 * @param binaryData Array containing binary data to encode.
229 * @param isChunked if isChunked is true this encoder will chunk
230 * the base64 output into 76 character blocks
231 * @return Base64-encoded data.
232 */
233 public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
234 int lengthDataBits = binaryData.length * EIGHTBIT;
235 int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
236 int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
237 byte encodedData[] = null;
238 int encodedDataLength = 0;
239 int nbrChunks = 0;
240
241 if (fewerThan24bits != 0) {
242 //data not divisible by 24 bit
243 encodedDataLength = (numberTriplets + 1) * 4;
244 } else {
245 // 16 or 8 bit
246 encodedDataLength = numberTriplets * 4;
247 }
248
249 // If the output is to be "chunked" into 76 character sections,
250 // for compliance with RFC 2045 MIME, then it is important to
251 // allow for extra length to account for the separator(s)
252 if (isChunked) {
253
254 nbrChunks = (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
255 encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
256 }
257
258 encodedData = new byte[encodedDataLength];
259
260 byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
261
262 int encodedIndex = 0;
263 int dataIndex = 0;
264 int i = 0;
265 int nextSeparatorIndex = CHUNK_SIZE;
266 int chunksSoFar = 0;
267
268 //log.debug("number of triplets = " + numberTriplets);
269 for (i = 0; i < numberTriplets; i++) {
270 dataIndex = i * 3;
271 b1 = binaryData[dataIndex];
272 b2 = binaryData[dataIndex + 1];
273 b3 = binaryData[dataIndex + 2];
274
275 //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
276
277 l = (byte) (b2 & 0x0f);
278 k = (byte) (b1 & 0x03);
279
280 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
281 byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
282 byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
283
284 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
285 //log.debug( "val2 = " + val2 );
286 //log.debug( "k4 = " + (k<<4) );
287 //log.debug( "vak = " + (val2 | (k<<4)) );
288 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
289 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2) | val3];
290 encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
291
292 encodedIndex += 4;
293
294 // If we are chunking, let's put a chunk separator down.
295 if (isChunked) {
296 // this assumes that CHUNK_SIZE % 4 == 0
297 if (encodedIndex == nextSeparatorIndex) {
298 System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedIndex, CHUNK_SEPARATOR.length);
299 chunksSoFar++;
300 nextSeparatorIndex = (CHUNK_SIZE * (chunksSoFar + 1)) + (chunksSoFar * CHUNK_SEPARATOR.length);
301 encodedIndex += CHUNK_SEPARATOR.length;
302 }
303 }
304 }
305
306 // form integral number of 6-bit groups
307 dataIndex = i * 3;
308
309 if (fewerThan24bits == EIGHTBIT) {
310 b1 = binaryData[dataIndex];
311 k = (byte) (b1 & 0x03);
312 //log.debug("b1=" + b1);
313 //log.debug("b1<<2 = " + (b1>>2) );
314 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
315 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
316 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
317 encodedData[encodedIndex + 2] = PAD;
318 encodedData[encodedIndex + 3] = PAD;
319 } else if (fewerThan24bits == SIXTEENBIT) {
320
321 b1 = binaryData[dataIndex];
322 b2 = binaryData[dataIndex + 1];
323 l = (byte) (b2 & 0x0f);
324 k = (byte) (b1 & 0x03);
325
326 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
327 byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
328
329 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
330 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
331 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
332 encodedData[encodedIndex + 3] = PAD;
333 }
334
335 if (isChunked) {
336 // we also add a separator to the end of the final chunk.
337 if (chunksSoFar < nbrChunks) {
338 System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedDataLength - CHUNK_SEPARATOR.length,
339 CHUNK_SEPARATOR.length);
340 }
341 }
342
343 return encodedData;
344 }
345
346 /**
347 * Decodes Base64 data into octects
348 *
349 * @param base64Data Byte array containing Base64 data
350 * @return Array containing decoded data.
351 */
352 public static byte[] decodeBase64(byte[] base64Data) {
353 // RFC 2045 requires that we discard ALL non-Base64 characters
354 base64Data = discardNonBase64(base64Data);
355
356 // handle the edge case, so we don't have to worry about it later
357 if (base64Data.length == 0) {
358 return new byte[0];
359 }
360
361 int numberQuadruple = base64Data.length / FOURBYTE;
362 byte decodedData[] = null;
363 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
364
365 // Throw away anything not in base64Data
366
367 int encodedIndex = 0;
368 int dataIndex = 0;
369 {
370 // this sizes the output array properly - rlw
371 int lastData = base64Data.length;
372 // ignore the '=' padding
373 while (base64Data[lastData - 1] == PAD) {
374 if (--lastData == 0) {
375 return new byte[0];
376 }
377 }
378 decodedData = new byte[lastData - numberQuadruple];
379 }
380
381 for (int i = 0; i < numberQuadruple; i++) {
382 dataIndex = i * 4;
383 marker0 = base64Data[dataIndex + 2];
384 marker1 = base64Data[dataIndex + 3];
385
386 b1 = base64Alphabet[base64Data[dataIndex]];
387 b2 = base64Alphabet[base64Data[dataIndex + 1]];
388
389 if (marker0 != PAD && marker1 != PAD) {
390 //No PAD e.g 3cQl
391 b3 = base64Alphabet[marker0];
392 b4 = base64Alphabet[marker1];
393
394 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
395 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
396 decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
397 } else if (marker0 == PAD) {
398 //Two PAD e.g. 3c[Pad][Pad]
399 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
400 } else if (marker1 == PAD) {
401 //One PAD e.g. 3cQ[Pad]
402 b3 = base64Alphabet[marker0];
403
404 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
405 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
406 }
407 encodedIndex += 3;
408 }
409 return decodedData;
410 }
411
412 /**
413 * Discards any whitespace from a base-64 encoded block.
414 *
415 * @param data The base-64 encoded data to discard the whitespace
416 * from.
417 * @return The data, less whitespace (see RFC 2045).
418 */
419 static byte[] discardWhitespace(byte[] data) {
420 byte groomedData[] = new byte[data.length];
421 int bytesCopied = 0;
422
423 for (int i = 0; i < data.length; i++) {
424 switch (data[i]) {
425 case (byte) ' ':
426 case (byte) '\n':
427 case (byte) '\r':
428 case (byte) '\t':
429 break;
430 default:
431 groomedData[bytesCopied++] = data[i];
432 }
433 }
434
435 byte packedData[] = new byte[bytesCopied];
436
437 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
438
439 return packedData;
440 }
441
442 /**
443 * Discards any characters outside of the base64 alphabet, per
444 * the requirements on page 25 of RFC 2045 - "Any characters
445 * outside of the base64 alphabet are to be ignored in base64
446 * encoded data."
447 *
448 * @param data The base-64 encoded data to groom
449 * @return The data, less non-base64 characters (see RFC 2045).
450 */
451 static byte[] discardNonBase64(byte[] data) {
452 byte groomedData[] = new byte[data.length];
453 int bytesCopied = 0;
454
455 for (int i = 0; i < data.length; i++) {
456 if (isBase64(data[i])) {
457 groomedData[bytesCopied++] = data[i];
458 }
459 }
460
461 byte packedData[] = new byte[bytesCopied];
462
463 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
464
465 return packedData;
466 }
467
468 // Implementation of the Encoder Interface
469
470 /**
471 * Encodes an Object using the base64 algorithm. This method
472 * is provided in order to satisfy the requirements of the
473 * Encoder interface, and will throw an EncoderException if the
474 * supplied object is not of type byte[].
475 *
476 * @param pObject Object to encode
477 * @return An object (of type byte[]) containing the
478 * base64 encoded data which corresponds to the byte[] supplied.
479 * @throws InvalidParameterException if the parameter supplied is not
480 * of type byte[]
481 */
482 public Object encode(Object pObject) {
483 if (!(pObject instanceof byte[])) {
484 throw new InvalidParameterException("Parameter supplied to Base64 encode is not a byte[]");
485 }
486 return encode((byte[]) pObject);
487 }
488
489 /**
490 * Encodes a byte[] containing binary data, into a byte[] containing
491 * characters in the Base64 alphabet.
492 *
493 * @param pArray a byte array containing binary data
494 * @return A byte array containing only Base64 character data
495 */
496 public byte[] encode(byte[] pArray) {
497 return encodeBase64(pArray, false);
498 }
499
500 }