KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > util > Base64Codec


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.cayenne.util;
21
22 /**
23  * Provides Base64 encoding and decoding as defined by RFC 2045.
24  * <p>
25  * <i>This codec is based on Apache commons.codec implementation, copyright The Apache
26  * Software Foundation.</i>
27  * </p>
28  *
29  * @since 1.2
30  * @author Andrus Adamchik
31  */

32 public class Base64Codec {
33
34     /**
35      * Chunk size per RFC 2045 section 6.8.
36      * <p>
37      * The {@value} character limit does not count the trailing CRLF, but counts all other
38      * characters, including any equal signs.
39      * </p>
40      *
41      * @see <a HREF="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
42      */

43     static final int CHUNK_SIZE = 76;
44
45     /**
46      * Chunk separator per RFC 2045 section 2.1.
47      *
48      * @see <a HREF="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
49      */

50     static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
51
52     /**
53      * The base length.
54      */

55     static final int BASELENGTH = 255;
56
57     /**
58      * Lookup length.
59      */

60     static final int LOOKUPLENGTH = 64;
61
62     /**
63      * Used to calculate the number of bits in a byte.
64      */

65     static final int EIGHTBIT = 8;
66
67     /**
68      * Used when encoding something which has fewer than 24 bits.
69      */

70     static final int SIXTEENBIT = 16;
71
72     /**
73      * Used to determine how many bits data contains.
74      */

75     static final int TWENTYFOURBITGROUP = 24;
76
77     /**
78      * Used to get the number of Quadruples.
79      */

80     static final int FOURBYTE = 4;
81
82     /**
83      * Used to test the sign of a byte.
84      */

85     static final int SIGN = -128;
86
87     /**
88      * Byte used to pad output.
89      */

90     static final byte PAD = (byte) '=';
91
92     // Create arrays to hold the base64 characters and a
93
// lookup for base64 chars
94
private static byte[] base64Alphabet = new byte[BASELENGTH];
95     private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
96
97     // Populating the lookup and character arrays
98
static {
99         for (int i = 0; i < BASELENGTH; i++) {
100             base64Alphabet[i] = (byte) -1;
101         }
102         for (int i = 'Z'; i >= 'A'; i--) {
103             base64Alphabet[i] = (byte) (i - 'A');
104         }
105         for (int i = 'z'; i >= 'a'; i--) {
106             base64Alphabet[i] = (byte) (i - 'a' + 26);
107         }
108         for (int i = '9'; i >= '0'; i--) {
109             base64Alphabet[i] = (byte) (i - '0' + 52);
110         }
111
112         base64Alphabet['+'] = 62;
113         base64Alphabet['/'] = 63;
114
115         for (int i = 0; i <= 25; i++) {
116             lookUpBase64Alphabet[i] = (byte) ('A' + i);
117         }
118
119         for (int i = 26, j = 0; i <= 51; i++, j++) {
120             lookUpBase64Alphabet[i] = (byte) ('a' + j);
121         }
122
123         for (int i = 52, j = 0; i <= 61; i++, j++) {
124             lookUpBase64Alphabet[i] = (byte) ('0' + j);
125         }
126
127         lookUpBase64Alphabet[62] = (byte) '+';
128         lookUpBase64Alphabet[63] = (byte) '/';
129     }
130
131     private static boolean isBase64(byte octect) {
132         if (octect == PAD) {
133             return true;
134         }
135         else if (base64Alphabet[octect] == -1) {
136             return false;
137         }
138         else {
139             return true;
140         }
141     }
142
143     /**
144      * Tests a given byte array to see if it contains only valid characters within the
145      * Base64 alphabet.
146      *
147      * @param arrayOctect byte array to test
148      * @return true if all bytes are valid characters in the Base64 alphabet or if the
149      * byte array is empty; false, otherwise
150      */

151     public static boolean isArrayByteBase64(byte[] arrayOctect) {
152
153         arrayOctect = discardWhitespace(arrayOctect);
154
155         int length = arrayOctect.length;
156         if (length == 0) {
157             // shouldn't a 0 length array be valid base64 data?
158
// return false;
159
return true;
160         }
161         for (int i = 0; i < length; i++) {
162             if (!isBase64(arrayOctect[i])) {
163                 return false;
164             }
165         }
166         return true;
167     }
168
169     /**
170      * Encodes binary data using the base64 algorithm but does not chunk the output.
171      *
172      * @param binaryData binary data to encode
173      * @return Base64 characters
174      */

175     public static byte[] encodeBase64(byte[] binaryData) {
176         return encodeBase64(binaryData, false);
177     }
178
179     /**
180      * Encodes binary data using the base64 algorithm and chunks the encoded output into
181      * 76 character blocks
182      *
183      * @param binaryData binary data to encode
184      * @return Base64 characters chunked in 76 character blocks
185      */

186     public static byte[] encodeBase64Chunked(byte[] binaryData) {
187         return encodeBase64(binaryData, true);
188     }
189
190     /**
191      * Encodes binary data using the base64 algorithm, optionally chunking the output into
192      * 76 character blocks.
193      *
194      * @param binaryData Array containing binary data to encode.
195      * @param isChunked if isChunked is true this encoder will chunk the base64 output
196      * into 76 character blocks
197      * @return Base64-encoded data.
198      */

199     public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
200         int lengthDataBits = binaryData.length * EIGHTBIT;
201         int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
202         int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
203         byte encodedData[] = null;
204         int encodedDataLength = 0;
205         int nbrChunks = 0;
206
207         if (fewerThan24bits != 0) {
208             // data not divisible by 24 bit
209
encodedDataLength = (numberTriplets + 1) * 4;
210         }
211         else {
212             // 16 or 8 bit
213
encodedDataLength = numberTriplets * 4;
214         }
215
216         // If the output is to be "chunked" into 76 character sections,
217
// for compliance with RFC 2045 MIME, then it is important to
218
// allow for extra length to account for the separator(s)
219
if (isChunked) {
220
221             nbrChunks = (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math
222                     .ceil((float) encodedDataLength / CHUNK_SIZE));
223             encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
224         }
225
226         encodedData = new byte[encodedDataLength];
227
228         byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
229
230         int encodedIndex = 0;
231         int dataIndex = 0;
232         int i = 0;
233         int nextSeparatorIndex = CHUNK_SIZE;
234         int chunksSoFar = 0;
235
236         // log.debug("number of triplets = " + numberTriplets);
237
for (i = 0; i < numberTriplets; i++) {
238             dataIndex = i * 3;
239             b1 = binaryData[dataIndex];
240             b2 = binaryData[dataIndex + 1];
241             b3 = binaryData[dataIndex + 2];
242
243             // log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
244

245             l = (byte) (b2 & 0x0f);
246             k = (byte) (b1 & 0x03);
247
248             byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
249             byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
250             byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
251
252             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
253             // log.debug( "val2 = " + val2 );
254
// log.debug( "k4 = " + (k<<4) );
255
// log.debug( "vak = " + (val2 | (k<<4)) );
256
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
257             encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2) | val3];
258             encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
259
260             encodedIndex += 4;
261
262             // If we are chunking, let's put a chunk separator down.
263
if (isChunked) {
264                 // this assumes that CHUNK_SIZE % 4 == 0
265
if (encodedIndex == nextSeparatorIndex) {
266                     System.arraycopy(
267                             CHUNK_SEPARATOR,
268                             0,
269                             encodedData,
270                             encodedIndex,
271                             CHUNK_SEPARATOR.length);
272                     chunksSoFar++;
273                     nextSeparatorIndex = (CHUNK_SIZE * (chunksSoFar + 1))
274                             + (chunksSoFar * CHUNK_SEPARATOR.length);
275                     encodedIndex += CHUNK_SEPARATOR.length;
276                 }
277             }
278         }
279
280         // form integral number of 6-bit groups
281
dataIndex = i * 3;
282
283         if (fewerThan24bits == EIGHTBIT) {
284             b1 = binaryData[dataIndex];
285             k = (byte) (b1 & 0x03);
286             // log.debug("b1=" + b1);
287
// log.debug("b1<<2 = " + (b1>>2) );
288
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
289             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
290             encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
291             encodedData[encodedIndex + 2] = PAD;
292             encodedData[encodedIndex + 3] = PAD;
293         }
294         else if (fewerThan24bits == SIXTEENBIT) {
295
296             b1 = binaryData[dataIndex];
297             b2 = binaryData[dataIndex + 1];
298             l = (byte) (b2 & 0x0f);
299             k = (byte) (b1 & 0x03);
300
301             byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
302             byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
303
304             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
305             encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
306             encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
307             encodedData[encodedIndex + 3] = PAD;
308         }
309
310         if (isChunked) {
311             // we also add a separator to the end of the final chunk.
312
if (chunksSoFar < nbrChunks) {
313                 System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedDataLength
314                         - CHUNK_SEPARATOR.length, CHUNK_SEPARATOR.length);
315             }
316         }
317
318         return encodedData;
319     }
320
321     /**
322      * Decodes Base64 data into octects
323      *
324      * @param base64Data Byte array containing Base64 data
325      * @return Array containing decoded data.
326      */

327     public static byte[] decodeBase64(byte[] base64Data) {
328         // RFC 2045 requires that we discard ALL non-Base64 characters
329
base64Data = discardNonBase64(base64Data);
330
331         // handle the edge case, so we don't have to worry about it later
332
if (base64Data.length == 0) {
333             return new byte[0];
334         }
335
336         int numberQuadruple = base64Data.length / FOURBYTE;
337         byte decodedData[] = null;
338         byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
339
340         // Throw away anything not in base64Data
341

342         int encodedIndex = 0;
343         int dataIndex = 0;
344         {
345             // this sizes the output array properly - rlw
346
int lastData = base64Data.length;
347             // ignore the '=' padding
348
while (base64Data[lastData - 1] == PAD) {
349                 if (--lastData == 0) {
350                     return new byte[0];
351                 }
352             }
353             decodedData = new byte[lastData - numberQuadruple];
354         }
355
356         for (int i = 0; i < numberQuadruple; i++) {
357             dataIndex = i * 4;
358             marker0 = base64Data[dataIndex + 2];
359             marker1 = base64Data[dataIndex + 3];
360
361             b1 = base64Alphabet[base64Data[dataIndex]];
362             b2 = base64Alphabet[base64Data[dataIndex + 1]];
363
364             if (marker0 != PAD && marker1 != PAD) {
365                 // No PAD e.g 3cQl
366
b3 = base64Alphabet[marker0];
367                 b4 = base64Alphabet[marker1];
368
369                 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
370                 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
371                 decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
372             }
373             else if (marker0 == PAD) {
374                 // Two PAD e.g. 3c[Pad][Pad]
375
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
376             }
377             else if (marker1 == PAD) {
378                 // One PAD e.g. 3cQ[Pad]
379
b3 = base64Alphabet[marker0];
380
381                 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
382                 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
383             }
384             encodedIndex += 3;
385         }
386         return decodedData;
387     }
388
389     /**
390      * Discards any whitespace from a base-64 encoded block.
391      *
392      * @param data The base-64 encoded data to discard the whitespace from.
393      * @return The data, less whitespace (see RFC 2045).
394      */

395     static byte[] discardWhitespace(byte[] data) {
396         byte groomedData[] = new byte[data.length];
397         int bytesCopied = 0;
398
399         for (int i = 0; i < data.length; i++) {
400             switch (data[i]) {
401                 case (byte) ' ':
402                 case (byte) '\n':
403                 case (byte) '\r':
404                 case (byte) '\t':
405                     break;
406                 default:
407                     groomedData[bytesCopied++] = data[i];
408             }
409         }
410
411         byte packedData[] = new byte[bytesCopied];
412
413         System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
414
415         return packedData;
416     }
417
418     /**
419      * Discards any characters outside of the base64 alphabet, per the requirements on
420      * page 25 of RFC 2045 - "Any characters outside of the base64 alphabet are to be
421      * ignored in base64 encoded data."
422      *
423      * @param data The base-64 encoded data to groom
424      * @return The data, less non-base64 characters (see RFC 2045).
425      */

426     static byte[] discardNonBase64(byte[] data) {
427         byte groomedData[] = new byte[data.length];
428         int bytesCopied = 0;
429
430         for (int i = 0; i < data.length; i++) {
431             if (isBase64(data[i])) {
432                 groomedData[bytesCopied++] = data[i];
433             }
434         }
435
436         byte packedData[] = new byte[bytesCopied];
437         System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
438         return packedData;
439     }
440 }
441
Popular Tags