Source code: mill/tools/Base64.java
1 /*
2 * The Apache Software License, Version 1.1
3 *
4 *
5 * Copyright (c) 1999 The Apache Software Foundation. All rights
6 * reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Apache Software Foundation (http://www.apache.org/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Xerces" and "Apache Software Foundation" must
28 * not be used to endorse or promote products derived from this
29 * software without prior written permission. For written
30 * permission, please contact apache@apache.org.
31 *
32 * 5. Products derived from this software may not be called "Apache",
33 * nor may "Apache" appear in their name, without prior written
34 * permission of the Apache Software Foundation.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 * ====================================================================
49 *
50 * This software consists of voluntary contributions made by many
51 * individuals on behalf of the Apache Software Foundation and was
52 * originally based on software copyright (c) 1999, International
53 * Business Machines, Inc., http://www.apache.org. For more
54 * information on the Apache Software Foundation, please see
55 * <http://www.apache.org/>.
56 */
57
58 package mill.tools;
59
60 import java.lang.*;
61
62
63 /**
64 * This class provides encode/decode for RFC 2045 Base64 as
65 * defined by RFC 2045, N. Freed and N. Borenstein.
66 * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
67 * Part One: Format of Internet Message Bodies. Reference
68 * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
69 * This class is used by XML Schema binary format validation
70 *
71 * This implementation does not encode/decode streaming
72 * data. You need the data that you will encode/decode
73 * already on a byte arrray.
74 *
75 * @author Jeffrey Rodriguez
76 * @author Sandy Gao
77 * @version $Id: Base64.java,v 1.8 2001/05/29 22:19:01 neilg Exp $
78 */
79 public final class Base64 {
80 static private final int BASELENGTH = 255;
81 static private final int LOOKUPLENGTH = 64;
82 static private final int TWENTYFOURBITGROUP = 24;
83 static private final int EIGHTBIT = 8;
84 static private final int SIXTEENBIT = 16;
85 static private final int SIXBIT = 6;
86 static private final int FOURBYTE = 4;
87 static private final int SIGN = -128;
88 static private final byte PAD = ( byte ) '=';
89 static private final boolean fDebug = false;
90 static private byte [] base64Alphabet = new byte[BASELENGTH];
91 static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
92
93
94 static {
95
96 for (int i = 0; i<BASELENGTH; i++) {
97 base64Alphabet[i] = -1;
98 }
99 for (int i = 'Z'; i >= 'A'; i--) {
100 base64Alphabet[i] = (byte) (i-'A');
101 }
102 for (int i = 'z'; i>= 'a'; i--) {
103 base64Alphabet[i] = (byte) ( i-'a' + 26);
104 }
105
106 for (int i = '9'; i >= '0'; i--) {
107 base64Alphabet[i] = (byte) (i-'0' + 52);
108 }
109
110 base64Alphabet['+'] = 62;
111 base64Alphabet['/'] = 63;
112
113 for (int i = 0; i<=25; i++)
114 lookUpBase64Alphabet[i] = (byte) ('A'+i );
115
116 for (int i = 26, j = 0; i<=51; i++, j++)
117 lookUpBase64Alphabet[i] = (byte) ('a'+ j );
118
119 for (int i = 52, j = 0; i<=61; i++, j++)
120 lookUpBase64Alphabet[i] = (byte) ('0' + j );
121 lookUpBase64Alphabet[62] = (byte) '+';
122 lookUpBase64Alphabet[63] = (byte) '/';
123
124 }
125
126 protected static boolean isWhiteSpace (byte octect) {
127 return(octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
128 }
129
130 protected static boolean isPad (byte octect) {
131 return(octect == PAD);
132 }
133
134 protected static boolean isData (byte octect) {
135 return( base64Alphabet[octect] != -1);
136 }
137
138 public static boolean isBase64( String isValidString ) {
139 if (isValidString == null)
140 return false;
141 return( isArrayByteBase64( isValidString.getBytes()));
142 }
143
144
145 public static boolean isBase64( byte octect ) {
146 return( isWhiteSpace(octect) || isPad(octect) || isData(octect));
147 }
148
149
150 /**
151 * remove WhiteSpace from MIME containing encoded Base64
152 * data.
153 * e.g.
154 * " sdffferererrereresfsdfsdfsdff\n\r
155 * iiiiiiiiierejrlkwjerklwjerwerwr==\n\r"
156 *
157 * @param data
158 * @return
159 */
160 public static synchronized byte[] removeWhiteSpace( byte[] data ) {
161 if (data == null)
162 return null;
163
164 int newSize = 0;
165 int len = data.length;
166 int i =0;
167 for (; i<len; i++) {
168 if (!isWhiteSpace( data[i] ))
169 newSize++;
170 }
171
172 if (newSize == len)
173 return data;//return input array since no whiteSpace
174
175
176 byte[] arrayWithoutSpaces = new byte[newSize];//Allocate new array without whiteSpace
177
178 int j = 0;
179 for (i=0;i<len;i++) {
180 if (isWhiteSpace( data[i] ))
181 continue;
182 else
183 arrayWithoutSpaces[j++] = data[i];//copy non-WhiteSpace
184 }
185 return arrayWithoutSpaces;
186
187 }
188
189 public static synchronized boolean isArrayByteBase64( byte[] arrayOctect ) {
190 return(getDecodedDataLength(arrayOctect) >= 0);
191 }
192
193 /**
194 * Encodes hex octects into Base64
195 *
196 * @param binaryData Array containing binaryData
197 * @return Encoded Base64 array
198 */
199 public static synchronized byte[] encode( byte[] binaryData ) {
200 if (binaryData == null)
201 return null;
202
203 int lengthDataBits = binaryData.length*EIGHTBIT;
204 int fewerThan24bits = lengthDataBits%TWENTYFOURBITGROUP;
205 int numberTriplets = lengthDataBits/TWENTYFOURBITGROUP;
206 byte encodedData[] = null;
207
208
209 if (fewerThan24bits != 0) //data not divisible by 24 bit
210 encodedData = new byte[ (numberTriplets + 1 )*4 ];
211 else // 16 or 8 bit
212 encodedData = new byte[ numberTriplets*4 ];
213
214 byte k=0, l=0, b1=0,b2=0,b3=0;
215
216 int encodedIndex = 0;
217 int dataIndex = 0;
218 int i = 0;
219 if (fDebug) {
220 System.out.println("number of triplets = " + numberTriplets );
221 }
222 for (i = 0; i<numberTriplets; i++) {
223
224 dataIndex = i*3;
225 b1 = binaryData[dataIndex];
226 b2 = binaryData[dataIndex + 1];
227 b3 = binaryData[dataIndex + 2];
228
229 if (fDebug) {
230 System.out.println( "b1= " + b1 +", b2= " + b2 + ", b3= " + b3 );
231 }
232
233 l = (byte)(b2 & 0x0f);
234 k = (byte)(b1 & 0x03);
235
236 encodedIndex = i*4;
237 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
238
239 byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
240 byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);
241
242 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
243 if (fDebug) {
244 System.out.println( "val2 = " + val2 );
245 System.out.println( "k4 = " + (k<<4));
246 System.out.println( "vak = " + (val2 | (k<<4)));
247 }
248
249 encodedData[encodedIndex+1] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
250 encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) | val3 ];
251 encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
252 }
253
254 // form integral number of 6-bit groups
255 dataIndex = i*3;
256 encodedIndex = i*4;
257 if (fewerThan24bits == EIGHTBIT) {
258 b1 = binaryData[dataIndex];
259 k = (byte) ( b1 &0x03 );
260 if (fDebug) {
261 System.out.println("b1=" + b1);
262 System.out.println("b1<<2 = " + (b1>>2) );
263 }
264 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
265 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
266 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
267 encodedData[encodedIndex + 2] = PAD;
268 encodedData[encodedIndex + 3] = PAD;
269 } else if (fewerThan24bits == SIXTEENBIT) {
270
271 b1 = binaryData[dataIndex];
272 b2 = binaryData[dataIndex +1 ];
273 l = ( byte ) ( b2 &0x0f );
274 k = ( byte ) ( b1 &0x03 );
275
276 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
277 byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
278
279 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
280 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
281 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
282 encodedData[encodedIndex + 3] = PAD;
283 }
284 return encodedData;
285 }
286
287 /**
288 * Decodes Base64 data into octects
289 *
290 * @param binaryData Byte array containing Base64 data
291 * @return Array containind decoded data.
292 */
293 public static synchronized byte[] decode( byte[] base64Data ) {
294
295 if (base64Data == null)
296 return null;
297
298 byte[] normalizedBase64Data = removeWhiteSpace( base64Data );
299
300 if (normalizedBase64Data.length%FOURBYTE != 0) {
301 return null;//should be divisible by four
302 }
303
304 int numberQuadruple = (normalizedBase64Data.length/FOURBYTE );
305
306 if (numberQuadruple == 0)
307 return new byte[0];
308
309 byte decodedData[] = null;
310 byte b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
311 byte d1=0,d2=0,d3=0,d4=0;
312
313
314
315 // Throw away anything not in normalizedBase64Data
316 // Adjust size
317 int i = 0;
318 int encodedIndex = 0;
319 int dataIndex = 0;
320 decodedData = new byte[ (numberQuadruple)*3];
321
322 for (; i<numberQuadruple-1; i++) {
323
324 if (!isData( (d1 = normalizedBase64Data[dataIndex++]) )||
325 !isData( (d2 = normalizedBase64Data[dataIndex++]) )||
326 !isData( (d3 = normalizedBase64Data[dataIndex++]) )||
327 !isData( (d4 = normalizedBase64Data[dataIndex++]) ))
328 return null;//if found "no data" just return null
329
330 b1 = base64Alphabet[d1];
331 b2 = base64Alphabet[d2];
332 b3 = base64Alphabet[d3];
333 b4 = base64Alphabet[d4];
334
335 decodedData[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ) ;
336 decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
337 decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
338 }
339
340
341 if (!isData( (d1 = normalizedBase64Data[dataIndex++]) ) ||
342 !isData( (d2 = normalizedBase64Data[dataIndex++]) )) {
343 return null;//if found "no data" just return null
344 }
345
346
347 b1 = base64Alphabet[d1];
348 b2 = base64Alphabet[d2];
349
350 d3 = normalizedBase64Data[dataIndex++];
351 d4 = normalizedBase64Data[dataIndex++];
352 if (!isData( (d3 ) ) ||
353 !isData( (d4 ) )) {//Check if they are PAD characters
354 if (isPad( d3 ) && isPad( d4)) { //Two PAD e.g. 3c[Pad][Pad]
355 if ((b2 & 0xf) != 0)//last 4 bits should be zero
356 return null;
357 byte[] tmp = new byte[ i*3 + 1 ];
358 System.arraycopy( decodedData, 0, tmp, 0, i*3 );
359 tmp[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
360 return tmp;
361 } else if (!isPad( d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad]
362 b3 = base64Alphabet[ d3 ];
363 if ((b3 & 0x3 ) != 0)//last 2 bits should be zero
364 return null;
365 byte[] tmp = new byte[ i*3 + 2 ];
366 System.arraycopy( decodedData, 0, tmp, 0, i*3 );
367 tmp[encodedIndex++] = (byte)( b1 <<2 | b2>>4 );
368 tmp[encodedIndex] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
369 return tmp;
370 } else {
371 return null;//an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data
372 }
373 } else { //No PAD e.g 3cQl
374 b3 = base64Alphabet[ d3 ];
375 b4 = base64Alphabet[ d4 ];
376 decodedData[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ) ;
377 decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
378 decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
379
380 }
381
382 return decodedData;
383 }
384
385
386 /**
387 * returns length of decoded data given an
388 * array containing encoded data.
389 * WhiteSpace removing is done if data array not
390 * valid.
391 *
392 * @param base64Data
393 * @return a -1 would be return if not
394 */
395 static public synchronized int getDecodedDataLength( byte[] base64Data ) {
396
397 if (base64Data == null)
398 return -1;
399
400 if (base64Data.length == 0)
401 return 0;
402
403 //byte[] normalizedBase64Data = removeWhiteSpace( base64Data );//Remove any whiteSpace
404 byte[] decodedData = null;
405
406 if ((decodedData = decode( base64Data ) ) == null)//decode could return a null byte array
407 return -1;
408
409 return decodedData.length;
410 }
411 }