1 /* Copyright 2004 The Apache Software Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.apache.xmlbeans.impl.util;
17
18 import java.io.UnsupportedEncodingException;
19 /**
20 * This class provides encode/decode for RFC 2045 Base64 as
21 * defined by RFC 2045, N. Freed and N. Borenstein.
22 * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
23 * Part One: Format of Internet Message Bodies. Reference
24 * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
25 * This class is used by XML Schema binary format validation
26 *
27 * This implementation does not encode/decode streaming
28 * data. You need the data that you will encode/decode
29 * already on a byte arrray.
30 *
31 * @author Jeffrey Rodriguez
32 * @author Sandy Gao
33 * @version $Id: Base64.java 111285 2004-12-08 16:54:26Z cezar $
34 */
35 public final class Base64 {
36
37 static private final int BASELENGTH = 255;
38 static private final int LOOKUPLENGTH = 64;
39 static private final int TWENTYFOURBITGROUP = 24;
40 static private final int EIGHTBIT = 8;
41 static private final int SIXTEENBIT = 16;
42 //static private final int SIXBIT = 6;
43 static private final int FOURBYTE = 4;
44 static private final int SIGN = -128;
45 static private final byte PAD = ( byte ) '=';
46 static private final boolean fDebug = false;
47 static private byte [] base64Alphabet = new byte[BASELENGTH];
48 static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
49
50 static {
51
52 for (int i = 0; i<BASELENGTH; i++) {
53 base64Alphabet[i] = -1;
54 }
55 for (int i = 'Z'; i >= 'A'; i--) {
56 base64Alphabet[i] = (byte) (i-'A');
57 }
58 for (int i = 'z'; i>= 'a'; i--) {
59 base64Alphabet[i] = (byte) ( i-'a' + 26);
60 }
61
62 for (int i = '9'; i >= '0'; i--) {
63 base64Alphabet[i] = (byte) (i-'0' + 52);
64 }
65
66 base64Alphabet['+'] = 62;
67 base64Alphabet['/'] = 63;
68
69 for (int i = 0; i<=25; i++)
70 lookUpBase64Alphabet[i] = (byte) ('A'+i );
71
72 for (int i = 26, j = 0; i<=51; i++, j++)
73 lookUpBase64Alphabet[i] = (byte) ('a'+ j );
74
75 for (int i = 52, j = 0; i<=61; i++, j++)
76 lookUpBase64Alphabet[i] = (byte) ('0' + j );
77 lookUpBase64Alphabet[62] = (byte) '+';
78 lookUpBase64Alphabet[63] = (byte) '/';
79
80 }
81
82 protected static boolean isWhiteSpace(byte octect) {
83 return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
84 }
85
86 protected static boolean isPad(byte octect) {
87 return (octect == PAD);
88 }
89
90 protected static boolean isData(byte octect) {
91 return (base64Alphabet[octect] != -1);
92 }
93
94 protected static boolean isBase64(byte octect) {
95 return (isWhiteSpace(octect) || isPad(octect) || isData(octect));
96 }
97
98 /**
99 * Encodes hex octects into Base64
100 *
101 * @param binaryData Array containing binaryData
102 * @return Encoded Base64 array
103 */
104 public static byte[] encode(byte[] binaryData) {
105
106 if (binaryData == null)
107 return null;
108
109 int lengthDataBits = binaryData.length*EIGHTBIT;
110 int fewerThan24bits = lengthDataBits%TWENTYFOURBITGROUP;
111 int numberTriplets = lengthDataBits/TWENTYFOURBITGROUP;
112 byte encodedData[] = null;
113
114 if (fewerThan24bits != 0) //data not divisible by 24 bit
115 encodedData = new byte[ (numberTriplets + 1 )*4 ];
116 else // 16 or 8 bit
117 encodedData = new byte[ numberTriplets*4 ];
118
119 byte k=0, l=0, b1=0,b2=0,b3=0;
120
121 int encodedIndex = 0;
122 int dataIndex = 0;
123 int i = 0;
124 if (fDebug) {
125 System.out.println("number of triplets = " + numberTriplets );
126 }
127 for (i = 0; i<numberTriplets; i++) {
128
129 dataIndex = i*3;
130 b1 = binaryData[dataIndex];
131 b2 = binaryData[dataIndex + 1];
132 b3 = binaryData[dataIndex + 2];
133
134 if (fDebug) {
135 System.out.println( "b1= " + b1 +", b2= " + b2 + ", b3= " + b3 );
136 }
137
138 l = (byte)(b2 & 0x0f);
139 k = (byte)(b1 & 0x03);
140
141 encodedIndex = i*4;
142 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
143
144 byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
145 byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);
146
147 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
148 if (fDebug) {
149 System.out.println( "val2 = " + val2 );
150 System.out.println( "k4 = " + (k<<4));
151 System.out.println( "vak = " + (val2 | (k<<4)));
152 }
153
154 encodedData[encodedIndex+1] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
155 encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) | val3 ];
156 encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
157 }
158
159 // form integral number of 6-bit groups
160 dataIndex = i*3;
161 encodedIndex = i*4;
162 if (fewerThan24bits == EIGHTBIT) {
163 b1 = binaryData[dataIndex];
164 k = (byte) ( b1 &0x03 );
165 if (fDebug) {
166 System.out.println("b1=" + b1);
167 System.out.println("b1<<2 = " + (b1>>2) );
168 }
169 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
170 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
171 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
172 encodedData[encodedIndex + 2] = PAD;
173 encodedData[encodedIndex + 3] = PAD;
174 } else if (fewerThan24bits == SIXTEENBIT) {
175
176 b1 = binaryData[dataIndex];
177 b2 = binaryData[dataIndex +1 ];
178 l = ( byte ) ( b2 &0x0f );
179 k = ( byte ) ( b1 &0x03 );
180
181 byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
182 byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
183
184 encodedData[encodedIndex] = lookUpBase64Alphabet[ val1 ];
185 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ val2 | ( k<<4 )];
186 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
187 encodedData[encodedIndex + 3] = PAD;
188 }
189
190 return encodedData;
191 }
192
193 /**
194 * Decodes Base64 data into octects
195 *
196 * @param base64Data Byte array containing Base64 data
197 * @return Array containind decoded data.
198 */
199 public static byte[] decode(byte[] base64Data) {
200
201 if (base64Data == null)
202 return null;
203
204 // remove white spaces
205 base64Data = removeWhiteSpace(base64Data);
206
207 if (base64Data.length%FOURBYTE != 0) {
208 return null;//should be divisible by four
209 }
210
211 int numberQuadruple = (base64Data.length/FOURBYTE );
212
213 if (numberQuadruple == 0)
214 return new byte[0];
215
216 byte decodedData[] = null;
217 byte b1=0,b2=0,b3=0, b4=0;//, marker0=0, marker1=0;
218 byte d1=0,d2=0,d3=0,d4=0;
219
220 // Throw away anything not in normalizedBase64Data
221 // Adjust size
222 int i = 0;
223 int encodedIndex = 0;
224 int dataIndex = 0;
225 decodedData = new byte[ (numberQuadruple)*3];
226
227 for (; i<numberQuadruple-1; i++) {
228
229 if (!isData( (d1 = base64Data[dataIndex++]) )||
230 !isData( (d2 = base64Data[dataIndex++]) )||
231 !isData( (d3 = base64Data[dataIndex++]) )||
232 !isData( (d4 = base64Data[dataIndex++]) ))
233 return null;//if found "no data" just return null
234
235 b1 = base64Alphabet[d1];
236 b2 = base64Alphabet[d2];
237 b3 = base64Alphabet[d3];
238 b4 = base64Alphabet[d4];
239
240 decodedData[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ) ;
241 decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
242 decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
243 }
244
245 if (!isData( (d1 = base64Data[dataIndex++]) ) ||
246 !isData( (d2 = base64Data[dataIndex++]) )) {
247 return null;//if found "no data" just return null
248 }
249
250 b1 = base64Alphabet[d1];
251 b2 = base64Alphabet[d2];
252
253 d3 = base64Data[dataIndex++];
254 d4 = base64Data[dataIndex++];
255 if (!isData( (d3 ) ) ||
256 !isData( (d4 ) )) {//Check if they are PAD characters
257 if (isPad( d3 ) && isPad( d4)) { //Two PAD e.g. 3c[Pad][Pad]
258 if ((b2 & 0xf) != 0)//last 4 bits should be zero
259 return null;
260 byte[] tmp = new byte[ i*3 + 1 ];
261 System.arraycopy( decodedData, 0, tmp, 0, i*3 );
262 tmp[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
263 return tmp;
264 } else if (!isPad( d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad]
265 b3 = base64Alphabet[ d3 ];
266 if ((b3 & 0x3 ) != 0)//last 2 bits should be zero
267 return null;
268 byte[] tmp = new byte[ i*3 + 2 ];
269 System.arraycopy( decodedData, 0, tmp, 0, i*3 );
270 tmp[encodedIndex++] = (byte)( b1 <<2 | b2>>4 );
271 tmp[encodedIndex] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
272 return tmp;
273 } else {
274 return null;//an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data
275 }
276 } else { //No PAD e.g 3cQl
277 b3 = base64Alphabet[ d3 ];
278 b4 = base64Alphabet[ d4 ];
279 decodedData[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ) ;
280 decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
281 decodedData[encodedIndex++] = (byte)( b3<<6 | b4 );
282
283 }
284
285 return decodedData;
286 }
287
288 // /**
289 // * Decodes Base64 data into octects
290 // *
291 // * @param base64Data String containing Base64 data
292 // * @return string containing decoded data.
293 // */
294 // public static String decode(String base64Data) {
295 // if (base64Data == null)
296 // return null;
297 //
298 // byte[] decoded = null;
299 // try {
300 // decoded = decode(base64Data.getBytes("utf-8"));
301 // }
302 // catch(UnsupportedEncodingException e) {
303 // }
304 // finally {
305 // return decoded == null ? null : new String(decoded);
306 // }
307 // }
308 //
309 // /**
310 // * Encodes octects (using utf-8) into Base64 data
311 // *
312 // * @param binaryData String containing Hex data
313 // * @return string containing decoded data.
314 // */
315 // public static String encode(String binaryData) {
316 // if (binaryData == null)
317 // return null;
318 //
319 // byte[] encoded = null;
320 // try {
321 // encoded = encode(binaryData.getBytes("utf-8"));
322 // }
323 // catch(UnsupportedEncodingException e) {}
324 // finally {
325 // return encoded == null ? null : new String(encoded);
326 // }
327 // }
328
329 /**
330 * remove WhiteSpace from MIME containing encoded Base64 data.
331 *
332 * @param data the byte array of base64 data (with WS)
333 * @return the byte array of base64 data (without WS)
334 */
335 protected static byte[] removeWhiteSpace(byte[] data) {
336 if (data == null)
337 return null;
338
339 // count characters that's not whitespace
340 int newSize = 0;
341 int len = data.length;
342 for (int i = 0; i < len; i++) {
343 if (!isWhiteSpace(data[i]))
344 newSize++;
345 }
346
347 // if no whitespace, just return the input array
348 if (newSize == len)
349 return data;
350
351 // create the array to return
352 byte[] newArray = new byte[newSize];
353
354 int j = 0;
355 for (int i = 0; i < len; i++) {
356 if (!isWhiteSpace(data[i]))
357 newArray[j++] = data[i];
358 }
359 return newArray;
360 }
361 }