|
1 /* |
|
2 * reserved comment block |
|
3 * DO NOT REMOVE OR ALTER! |
|
4 */ |
|
5 /** |
|
6 * Licensed to the Apache Software Foundation (ASF) under one |
|
7 * or more contributor license agreements. See the NOTICE file |
|
8 * distributed with this work for additional information |
|
9 * regarding copyright ownership. The ASF licenses this file |
|
10 * to you under the Apache License, Version 2.0 (the |
|
11 * "License"); you may not use this file except in compliance |
|
12 * with the License. You may obtain a copy of the License at |
|
13 * |
|
14 * http://www.apache.org/licenses/LICENSE-2.0 |
|
15 * |
|
16 * Unless required by applicable law or agreed to in writing, |
|
17 * software distributed under the License is distributed on an |
|
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
19 * KIND, either express or implied. See the License for the |
|
20 * specific language governing permissions and limitations |
|
21 * under the License. |
|
22 */ |
|
23 package com.sun.org.apache.xml.internal.security.utils; |
|
24 |
|
25 import java.io.BufferedReader; |
|
26 import java.io.IOException; |
|
27 import java.io.InputStream; |
|
28 import java.io.OutputStream; |
|
29 import java.math.BigInteger; |
|
30 |
|
31 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; |
|
32 import org.w3c.dom.Document; |
|
33 import org.w3c.dom.Element; |
|
34 import org.w3c.dom.Node; |
|
35 import org.w3c.dom.Text; |
|
36 |
|
37 /** |
|
38 * Implementation of MIME's Base64 encoding and decoding conversions. |
|
39 * Optimized code. (raw version taken from oreilly.jonathan.util, |
|
40 * and currently org.apache.xerces.ds.util.Base64) |
|
41 * |
|
42 * @author Raul Benito(Of the xerces copy, and little adaptations). |
|
43 * @author Anli Shundi |
|
44 * @author Christian Geuer-Pollmann |
|
45 * @see <A HREF="ftp://ftp.isi.edu/in-notes/rfc2045.txt">RFC 2045</A> |
|
46 * @see com.sun.org.apache.xml.internal.security.transforms.implementations.TransformBase64Decode |
|
47 */ |
|
48 public class Base64 { |
|
49 |
|
50 /** Field BASE64DEFAULTLENGTH */ |
|
51 public static final int BASE64DEFAULTLENGTH = 76; |
|
52 |
|
53 private static final int BASELENGTH = 255; |
|
54 private static final int LOOKUPLENGTH = 64; |
|
55 private static final int TWENTYFOURBITGROUP = 24; |
|
56 private static final int EIGHTBIT = 8; |
|
57 private static final int SIXTEENBIT = 16; |
|
58 private static final int FOURBYTE = 4; |
|
59 private static final int SIGN = -128; |
|
60 private static final char PAD = '='; |
|
61 private static final byte [] base64Alphabet = new byte[BASELENGTH]; |
|
62 private static final char [] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; |
|
63 |
|
64 static { |
|
65 for (int i = 0; i < BASELENGTH; i++) { |
|
66 base64Alphabet[i] = -1; |
|
67 } |
|
68 for (int i = 'Z'; i >= 'A'; i--) { |
|
69 base64Alphabet[i] = (byte) (i - 'A'); |
|
70 } |
|
71 for (int i = 'z'; i>= 'a'; i--) { |
|
72 base64Alphabet[i] = (byte) (i - 'a' + 26); |
|
73 } |
|
74 |
|
75 for (int i = '9'; i >= '0'; i--) { |
|
76 base64Alphabet[i] = (byte) (i - '0' + 52); |
|
77 } |
|
78 |
|
79 base64Alphabet['+'] = 62; |
|
80 base64Alphabet['/'] = 63; |
|
81 |
|
82 for (int i = 0; i <= 25; i++) { |
|
83 lookUpBase64Alphabet[i] = (char)('A' + i); |
|
84 } |
|
85 |
|
86 for (int i = 26, j = 0; i <= 51; i++, j++) { |
|
87 lookUpBase64Alphabet[i] = (char)('a' + j); |
|
88 } |
|
89 |
|
90 for (int i = 52, j = 0; i <= 61; i++, j++) { |
|
91 lookUpBase64Alphabet[i] = (char)('0' + j); |
|
92 } |
|
93 lookUpBase64Alphabet[62] = '+'; |
|
94 lookUpBase64Alphabet[63] = '/'; |
|
95 } |
|
96 |
|
97 private Base64() { |
|
98 // we don't allow instantiation |
|
99 } |
|
100 |
|
101 /** |
|
102 * Returns a byte-array representation of a <code>{@link BigInteger}<code>. |
|
103 * No sign-bit is output. |
|
104 * |
|
105 * <b>N.B.:</B> <code>{@link BigInteger}<code>'s toByteArray |
|
106 * returns eventually longer arrays because of the leading sign-bit. |
|
107 * |
|
108 * @param big <code>BigInteger<code> to be converted |
|
109 * @param bitlen <code>int<code> the desired length in bits of the representation |
|
110 * @return a byte array with <code>bitlen</code> bits of <code>big</code> |
|
111 */ |
|
112 static final byte[] getBytes(BigInteger big, int bitlen) { |
|
113 |
|
114 //round bitlen |
|
115 bitlen = ((bitlen + 7) >> 3) << 3; |
|
116 |
|
117 if (bitlen < big.bitLength()) { |
|
118 throw new IllegalArgumentException(I18n.translate("utils.Base64.IllegalBitlength")); |
|
119 } |
|
120 |
|
121 byte[] bigBytes = big.toByteArray(); |
|
122 |
|
123 if (((big.bitLength() % 8) != 0) |
|
124 && (((big.bitLength() / 8) + 1) == (bitlen / 8))) { |
|
125 return bigBytes; |
|
126 } |
|
127 |
|
128 // some copying needed |
|
129 int startSrc = 0; // no need to skip anything |
|
130 int bigLen = bigBytes.length; //valid length of the string |
|
131 |
|
132 if ((big.bitLength() % 8) == 0) { // correct values |
|
133 startSrc = 1; // skip sign bit |
|
134 |
|
135 bigLen--; // valid length of the string |
|
136 } |
|
137 |
|
138 int startDst = bitlen / 8 - bigLen; //pad with leading nulls |
|
139 byte[] resizedBytes = new byte[bitlen / 8]; |
|
140 |
|
141 System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, bigLen); |
|
142 |
|
143 return resizedBytes; |
|
144 } |
|
145 |
|
146 /** |
|
147 * Encode in Base64 the given <code>{@link BigInteger}<code>. |
|
148 * |
|
149 * @param big |
|
150 * @return String with Base64 encoding |
|
151 */ |
|
152 public static final String encode(BigInteger big) { |
|
153 return encode(getBytes(big, big.bitLength())); |
|
154 } |
|
155 |
|
156 /** |
|
157 * Returns a byte-array representation of a <code>{@link BigInteger}<code>. |
|
158 * No sign-bit is output. |
|
159 * |
|
160 * <b>N.B.:</B> <code>{@link BigInteger}<code>'s toByteArray |
|
161 * returns eventually longer arrays because of the leading sign-bit. |
|
162 * |
|
163 * @param big <code>BigInteger<code> to be converted |
|
164 * @param bitlen <code>int<code> the desired length in bits of the representation |
|
165 * @return a byte array with <code>bitlen</code> bits of <code>big</code> |
|
166 */ |
|
167 public static final byte[] encode(BigInteger big, int bitlen) { |
|
168 |
|
169 //round bitlen |
|
170 bitlen = ((bitlen + 7) >> 3) << 3; |
|
171 |
|
172 if (bitlen < big.bitLength()) { |
|
173 throw new IllegalArgumentException(I18n.translate("utils.Base64.IllegalBitlength")); |
|
174 } |
|
175 |
|
176 byte[] bigBytes = big.toByteArray(); |
|
177 |
|
178 if (((big.bitLength() % 8) != 0) |
|
179 && (((big.bitLength() / 8) + 1) == (bitlen / 8))) { |
|
180 return bigBytes; |
|
181 } |
|
182 |
|
183 // some copying needed |
|
184 int startSrc = 0; // no need to skip anything |
|
185 int bigLen = bigBytes.length; //valid length of the string |
|
186 |
|
187 if ((big.bitLength() % 8) == 0) { // correct values |
|
188 startSrc = 1; // skip sign bit |
|
189 |
|
190 bigLen--; // valid length of the string |
|
191 } |
|
192 |
|
193 int startDst = bitlen / 8 - bigLen; //pad with leading nulls |
|
194 byte[] resizedBytes = new byte[bitlen / 8]; |
|
195 |
|
196 System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, bigLen); |
|
197 |
|
198 return resizedBytes; |
|
199 } |
|
200 |
|
201 /** |
|
202 * Method decodeBigIntegerFromElement |
|
203 * |
|
204 * @param element |
|
205 * @return the biginteger obtained from the node |
|
206 * @throws Base64DecodingException |
|
207 */ |
|
208 public static final BigInteger decodeBigIntegerFromElement(Element element) |
|
209 throws Base64DecodingException { |
|
210 return new BigInteger(1, Base64.decode(element)); |
|
211 } |
|
212 |
|
213 /** |
|
214 * Method decodeBigIntegerFromText |
|
215 * |
|
216 * @param text |
|
217 * @return the biginter obtained from the text node |
|
218 * @throws Base64DecodingException |
|
219 */ |
|
220 public static final BigInteger decodeBigIntegerFromText(Text text) |
|
221 throws Base64DecodingException { |
|
222 return new BigInteger(1, Base64.decode(text.getData())); |
|
223 } |
|
224 |
|
225 /** |
|
226 * This method takes an (empty) Element and a BigInteger and adds the |
|
227 * base64 encoded BigInteger to the Element. |
|
228 * |
|
229 * @param element |
|
230 * @param biginteger |
|
231 */ |
|
232 public static final void fillElementWithBigInteger(Element element, BigInteger biginteger) { |
|
233 |
|
234 String encodedInt = encode(biginteger); |
|
235 |
|
236 if (!XMLUtils.ignoreLineBreaks() && encodedInt.length() > BASE64DEFAULTLENGTH) { |
|
237 encodedInt = "\n" + encodedInt + "\n"; |
|
238 } |
|
239 |
|
240 Document doc = element.getOwnerDocument(); |
|
241 Text text = doc.createTextNode(encodedInt); |
|
242 |
|
243 element.appendChild(text); |
|
244 } |
|
245 |
|
246 /** |
|
247 * Method decode |
|
248 * |
|
249 * Takes the <CODE>Text</CODE> children of the Element and interprets |
|
250 * them as input for the <CODE>Base64.decode()</CODE> function. |
|
251 * |
|
252 * @param element |
|
253 * @return the byte obtained of the decoding the element |
|
254 * $todo$ not tested yet |
|
255 * @throws Base64DecodingException |
|
256 */ |
|
257 public static final byte[] decode(Element element) throws Base64DecodingException { |
|
258 |
|
259 Node sibling = element.getFirstChild(); |
|
260 StringBuilder sb = new StringBuilder(); |
|
261 |
|
262 while (sibling != null) { |
|
263 if (sibling.getNodeType() == Node.TEXT_NODE) { |
|
264 Text t = (Text) sibling; |
|
265 |
|
266 sb.append(t.getData()); |
|
267 } |
|
268 sibling = sibling.getNextSibling(); |
|
269 } |
|
270 |
|
271 return decode(sb.toString()); |
|
272 } |
|
273 |
|
274 /** |
|
275 * Method encodeToElement |
|
276 * |
|
277 * @param doc |
|
278 * @param localName |
|
279 * @param bytes |
|
280 * @return an Element with the base64 encoded in the text. |
|
281 * |
|
282 */ |
|
283 public static final Element encodeToElement(Document doc, String localName, byte[] bytes) { |
|
284 Element el = XMLUtils.createElementInSignatureSpace(doc, localName); |
|
285 Text text = doc.createTextNode(encode(bytes)); |
|
286 |
|
287 el.appendChild(text); |
|
288 |
|
289 return el; |
|
290 } |
|
291 |
|
292 /** |
|
293 * Method decode |
|
294 * |
|
295 * @param base64 |
|
296 * @return the UTF bytes of the base64 |
|
297 * @throws Base64DecodingException |
|
298 * |
|
299 */ |
|
300 public static final byte[] decode(byte[] base64) throws Base64DecodingException { |
|
301 return decodeInternal(base64, -1); |
|
302 } |
|
303 |
|
304 /** |
|
305 * Encode a byte array and fold lines at the standard 76th character unless |
|
306 * ignore line breaks property is set. |
|
307 * |
|
308 * @param binaryData <code>byte[]<code> to be base64 encoded |
|
309 * @return the <code>String<code> with encoded data |
|
310 */ |
|
311 public static final String encode(byte[] binaryData) { |
|
312 return XMLUtils.ignoreLineBreaks() |
|
313 ? encode(binaryData, Integer.MAX_VALUE) |
|
314 : encode(binaryData, BASE64DEFAULTLENGTH); |
|
315 } |
|
316 |
|
317 /** |
|
318 * Base64 decode the lines from the reader and return an InputStream |
|
319 * with the bytes. |
|
320 * |
|
321 * @param reader |
|
322 * @return InputStream with the decoded bytes |
|
323 * @exception IOException passes what the reader throws |
|
324 * @throws IOException |
|
325 * @throws Base64DecodingException |
|
326 */ |
|
327 public static final byte[] decode(BufferedReader reader) |
|
328 throws IOException, Base64DecodingException { |
|
329 |
|
330 byte[] retBytes = null; |
|
331 UnsyncByteArrayOutputStream baos = null; |
|
332 try { |
|
333 baos = new UnsyncByteArrayOutputStream(); |
|
334 String line; |
|
335 |
|
336 while (null != (line = reader.readLine())) { |
|
337 byte[] bytes = decode(line); |
|
338 baos.write(bytes); |
|
339 } |
|
340 retBytes = baos.toByteArray(); |
|
341 } finally { |
|
342 baos.close(); |
|
343 } |
|
344 |
|
345 return retBytes; |
|
346 } |
|
347 |
|
348 protected static final boolean isWhiteSpace(byte octect) { |
|
349 return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); |
|
350 } |
|
351 |
|
352 protected static final boolean isPad(byte octect) { |
|
353 return (octect == PAD); |
|
354 } |
|
355 |
|
356 /** |
|
357 * Encodes hex octets into Base64 |
|
358 * |
|
359 * @param binaryData Array containing binaryData |
|
360 * @return Encoded Base64 array |
|
361 */ |
|
362 /** |
|
363 * Encode a byte array in Base64 format and return an optionally |
|
364 * wrapped line. |
|
365 * |
|
366 * @param binaryData <code>byte[]</code> data to be encoded |
|
367 * @param length <code>int<code> length of wrapped lines; No wrapping if less than 4. |
|
368 * @return a <code>String</code> with encoded data |
|
369 */ |
|
370 public static final String encode(byte[] binaryData,int length) { |
|
371 if (length < 4) { |
|
372 length = Integer.MAX_VALUE; |
|
373 } |
|
374 |
|
375 if (binaryData == null) { |
|
376 return null; |
|
377 } |
|
378 |
|
379 int lengthDataBits = binaryData.length * EIGHTBIT; |
|
380 if (lengthDataBits == 0) { |
|
381 return ""; |
|
382 } |
|
383 |
|
384 int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; |
|
385 int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; |
|
386 int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; |
|
387 int quartesPerLine = length / 4; |
|
388 int numberLines = (numberQuartet - 1) / quartesPerLine; |
|
389 char encodedData[] = null; |
|
390 |
|
391 encodedData = new char[numberQuartet * 4 + numberLines]; |
|
392 |
|
393 byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; |
|
394 int encodedIndex = 0; |
|
395 int dataIndex = 0; |
|
396 int i = 0; |
|
397 |
|
398 for (int line = 0; line < numberLines; line++) { |
|
399 for (int quartet = 0; quartet < 19; quartet++) { |
|
400 b1 = binaryData[dataIndex++]; |
|
401 b2 = binaryData[dataIndex++]; |
|
402 b3 = binaryData[dataIndex++]; |
|
403 |
|
404 l = (byte)(b2 & 0x0f); |
|
405 k = (byte)(b1 & 0x03); |
|
406 |
|
407 byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2): (byte)((b1) >> 2 ^ 0xc0); |
|
408 |
|
409 byte val2 = ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); |
|
410 byte val3 = ((b3 & SIGN) == 0) ? (byte)(b3 >> 6) : (byte)((b3) >> 6 ^ 0xfc); |
|
411 |
|
412 |
|
413 encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
|
414 encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
|
415 encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; |
|
416 encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; |
|
417 |
|
418 i++; |
|
419 } |
|
420 encodedData[encodedIndex++] = 0xa; |
|
421 } |
|
422 |
|
423 for (; i < numberTriplets; i++) { |
|
424 b1 = binaryData[dataIndex++]; |
|
425 b2 = binaryData[dataIndex++]; |
|
426 b3 = binaryData[dataIndex++]; |
|
427 |
|
428 l = (byte)(b2 & 0x0f); |
|
429 k = (byte)(b1 & 0x03); |
|
430 |
|
431 byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); |
|
432 |
|
433 byte val2 = ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); |
|
434 byte val3 = ((b3 & SIGN) == 0) ? (byte)(b3 >> 6) : (byte)((b3) >> 6 ^ 0xfc); |
|
435 |
|
436 |
|
437 encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
|
438 encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
|
439 encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; |
|
440 encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; |
|
441 } |
|
442 |
|
443 // form integral number of 6-bit groups |
|
444 if (fewerThan24bits == EIGHTBIT) { |
|
445 b1 = binaryData[dataIndex]; |
|
446 k = (byte) (b1 &0x03); |
|
447 byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2):(byte)((b1) >> 2 ^ 0xc0); |
|
448 encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
|
449 encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; |
|
450 encodedData[encodedIndex++] = PAD; |
|
451 encodedData[encodedIndex++] = PAD; |
|
452 } else if (fewerThan24bits == SIXTEENBIT) { |
|
453 b1 = binaryData[dataIndex]; |
|
454 b2 = binaryData[dataIndex +1 ]; |
|
455 l = ( byte ) (b2 & 0x0f); |
|
456 k = ( byte ) (b1 & 0x03); |
|
457 |
|
458 byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); |
|
459 byte val2 = ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); |
|
460 |
|
461 encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
|
462 encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
|
463 encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; |
|
464 encodedData[encodedIndex++] = PAD; |
|
465 } |
|
466 |
|
467 //encodedData[encodedIndex] = 0xa; |
|
468 |
|
469 return new String(encodedData); |
|
470 } |
|
471 |
|
472 /** |
|
473 * Decodes Base64 data into octets |
|
474 * |
|
475 * @param encoded String containing base64 encoded data |
|
476 * @return byte array containing the decoded data |
|
477 * @throws Base64DecodingException if there is a problem decoding the data |
|
478 */ |
|
479 public static final byte[] decode(String encoded) throws Base64DecodingException { |
|
480 if (encoded == null) { |
|
481 return null; |
|
482 } |
|
483 byte[] bytes = new byte[encoded.length()]; |
|
484 int len = getBytesInternal(encoded, bytes); |
|
485 return decodeInternal(bytes, len); |
|
486 } |
|
487 |
|
488 protected static final int getBytesInternal(String s, byte[] result) { |
|
489 int length = s.length(); |
|
490 |
|
491 int newSize = 0; |
|
492 for (int i = 0; i < length; i++) { |
|
493 byte dataS = (byte)s.charAt(i); |
|
494 if (!isWhiteSpace(dataS)) { |
|
495 result[newSize++] = dataS; |
|
496 } |
|
497 } |
|
498 return newSize; |
|
499 } |
|
500 |
|
501 protected static final byte[] decodeInternal(byte[] base64Data, int len) |
|
502 throws Base64DecodingException { |
|
503 // remove white spaces |
|
504 if (len == -1) { |
|
505 len = removeWhiteSpace(base64Data); |
|
506 } |
|
507 |
|
508 if (len % FOURBYTE != 0) { |
|
509 throw new Base64DecodingException("decoding.divisible.four"); |
|
510 //should be divisible by four |
|
511 } |
|
512 |
|
513 int numberQuadruple = (len / FOURBYTE); |
|
514 |
|
515 if (numberQuadruple == 0) { |
|
516 return new byte[0]; |
|
517 } |
|
518 |
|
519 byte decodedData[] = null; |
|
520 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; |
|
521 |
|
522 int i = 0; |
|
523 int encodedIndex = 0; |
|
524 int dataIndex = 0; |
|
525 |
|
526 //decodedData = new byte[ (numberQuadruple)*3]; |
|
527 dataIndex = (numberQuadruple - 1) * 4; |
|
528 encodedIndex = (numberQuadruple - 1) * 3; |
|
529 //first last bits. |
|
530 b1 = base64Alphabet[base64Data[dataIndex++]]; |
|
531 b2 = base64Alphabet[base64Data[dataIndex++]]; |
|
532 if ((b1==-1) || (b2==-1)) { |
|
533 //if found "no data" just return null |
|
534 throw new Base64DecodingException("decoding.general"); |
|
535 } |
|
536 |
|
537 |
|
538 byte d3, d4; |
|
539 b3 = base64Alphabet[d3 = base64Data[dataIndex++]]; |
|
540 b4 = base64Alphabet[d4 = base64Data[dataIndex++]]; |
|
541 if ((b3 == -1) || (b4 == -1) ) { |
|
542 //Check if they are PAD characters |
|
543 if (isPad(d3) && isPad(d4)) { //Two PAD e.g. 3c[Pad][Pad] |
|
544 if ((b2 & 0xf) != 0) { //last 4 bits should be zero |
|
545 throw new Base64DecodingException("decoding.general"); |
|
546 } |
|
547 decodedData = new byte[encodedIndex + 1]; |
|
548 decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4) ; |
|
549 } else if (!isPad(d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad] |
|
550 if ((b3 & 0x3) != 0) { //last 2 bits should be zero |
|
551 throw new Base64DecodingException("decoding.general"); |
|
552 } |
|
553 decodedData = new byte[encodedIndex + 2]; |
|
554 decodedData[encodedIndex++] = (byte)(b1 << 2 | b2 >> 4); |
|
555 decodedData[encodedIndex] = (byte)(((b2 & 0xf) << 4) |((b3 >> 2) & 0xf)); |
|
556 } else { |
|
557 //an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data |
|
558 throw new Base64DecodingException("decoding.general"); |
|
559 } |
|
560 } else { |
|
561 //No PAD e.g 3cQl |
|
562 decodedData = new byte[encodedIndex+3]; |
|
563 decodedData[encodedIndex++] = (byte)(b1 << 2 | b2 >> 4) ; |
|
564 decodedData[encodedIndex++] = (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
|
565 decodedData[encodedIndex++] = (byte)(b3 << 6 | b4); |
|
566 } |
|
567 encodedIndex = 0; |
|
568 dataIndex = 0; |
|
569 //the begin |
|
570 for (i = numberQuadruple - 1; i > 0; i--) { |
|
571 b1 = base64Alphabet[base64Data[dataIndex++]]; |
|
572 b2 = base64Alphabet[base64Data[dataIndex++]]; |
|
573 b3 = base64Alphabet[base64Data[dataIndex++]]; |
|
574 b4 = base64Alphabet[base64Data[dataIndex++]]; |
|
575 |
|
576 if ((b1 == -1) || |
|
577 (b2 == -1) || |
|
578 (b3 == -1) || |
|
579 (b4 == -1)) { |
|
580 //if found "no data" just return null |
|
581 throw new Base64DecodingException("decoding.general"); |
|
582 } |
|
583 |
|
584 decodedData[encodedIndex++] = (byte)(b1 << 2 | b2 >> 4) ; |
|
585 decodedData[encodedIndex++] = (byte)(((b2 & 0xf) << 4) |((b3 >> 2) & 0xf)); |
|
586 decodedData[encodedIndex++] = (byte)(b3 << 6 | b4 ); |
|
587 } |
|
588 return decodedData; |
|
589 } |
|
590 |
|
591 /** |
|
592 * Decodes Base64 data into outputstream |
|
593 * |
|
594 * @param base64Data String containing Base64 data |
|
595 * @param os the outputstream |
|
596 * @throws IOException |
|
597 * @throws Base64DecodingException |
|
598 */ |
|
599 public static final void decode(String base64Data, OutputStream os) |
|
600 throws Base64DecodingException, IOException { |
|
601 byte[] bytes = new byte[base64Data.length()]; |
|
602 int len = getBytesInternal(base64Data, bytes); |
|
603 decode(bytes,os,len); |
|
604 } |
|
605 |
|
606 /** |
|
607 * Decodes Base64 data into outputstream |
|
608 * |
|
609 * @param base64Data Byte array containing Base64 data |
|
610 * @param os the outputstream |
|
611 * @throws IOException |
|
612 * @throws Base64DecodingException |
|
613 */ |
|
614 public static final void decode(byte[] base64Data, OutputStream os) |
|
615 throws Base64DecodingException, IOException { |
|
616 decode(base64Data,os,-1); |
|
617 } |
|
618 |
|
619 protected static final void decode(byte[] base64Data, OutputStream os, int len) |
|
620 throws Base64DecodingException, IOException { |
|
621 // remove white spaces |
|
622 if (len == -1) { |
|
623 len = removeWhiteSpace(base64Data); |
|
624 } |
|
625 |
|
626 if (len % FOURBYTE != 0) { |
|
627 throw new Base64DecodingException("decoding.divisible.four"); |
|
628 //should be divisible by four |
|
629 } |
|
630 |
|
631 int numberQuadruple = (len / FOURBYTE); |
|
632 |
|
633 if (numberQuadruple == 0) { |
|
634 return; |
|
635 } |
|
636 |
|
637 //byte decodedData[] = null; |
|
638 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; |
|
639 |
|
640 int i = 0; |
|
641 int dataIndex = 0; |
|
642 |
|
643 //the begin |
|
644 for (i=numberQuadruple - 1; i > 0; i--) { |
|
645 b1 = base64Alphabet[base64Data[dataIndex++]]; |
|
646 b2 = base64Alphabet[base64Data[dataIndex++]]; |
|
647 b3 = base64Alphabet[base64Data[dataIndex++]]; |
|
648 b4 = base64Alphabet[base64Data[dataIndex++]]; |
|
649 if ((b1 == -1) || |
|
650 (b2 == -1) || |
|
651 (b3 == -1) || |
|
652 (b4 == -1) ) { |
|
653 //if found "no data" just return null |
|
654 throw new Base64DecodingException("decoding.general"); |
|
655 } |
|
656 |
|
657 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
658 os.write((byte)(((b2 & 0xf) << 4 ) | ((b3 >> 2) & 0xf))); |
|
659 os.write( (byte)(b3 << 6 | b4)); |
|
660 } |
|
661 b1 = base64Alphabet[base64Data[dataIndex++]]; |
|
662 b2 = base64Alphabet[base64Data[dataIndex++]]; |
|
663 |
|
664 // first last bits. |
|
665 if ((b1 == -1) || (b2 == -1) ) { |
|
666 //if found "no data" just return null |
|
667 throw new Base64DecodingException("decoding.general"); |
|
668 } |
|
669 |
|
670 byte d3, d4; |
|
671 b3 = base64Alphabet[d3 = base64Data[dataIndex++]]; |
|
672 b4 = base64Alphabet[d4 = base64Data[dataIndex++]]; |
|
673 if ((b3 == -1 ) || (b4 == -1) ) { //Check if they are PAD characters |
|
674 if (isPad(d3) && isPad(d4)) { //Two PAD e.g. 3c[Pad][Pad] |
|
675 if ((b2 & 0xf) != 0) { //last 4 bits should be zero |
|
676 throw new Base64DecodingException("decoding.general"); |
|
677 } |
|
678 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
679 } else if (!isPad(d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad] |
|
680 if ((b3 & 0x3 ) != 0) { //last 2 bits should be zero |
|
681 throw new Base64DecodingException("decoding.general"); |
|
682 } |
|
683 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
684 os.write((byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf))); |
|
685 } else { |
|
686 //an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data |
|
687 throw new Base64DecodingException("decoding.general"); |
|
688 } |
|
689 } else { |
|
690 //No PAD e.g 3cQl |
|
691 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
692 os.write( (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf))); |
|
693 os.write((byte)(b3 << 6 | b4)); |
|
694 } |
|
695 } |
|
696 |
|
697 /** |
|
698 * Decodes Base64 data into outputstream |
|
699 * |
|
700 * @param is containing Base64 data |
|
701 * @param os the outputstream |
|
702 * @throws IOException |
|
703 * @throws Base64DecodingException |
|
704 */ |
|
705 public static final void decode(InputStream is, OutputStream os) |
|
706 throws Base64DecodingException, IOException { |
|
707 //byte decodedData[] = null; |
|
708 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; |
|
709 |
|
710 int index=0; |
|
711 byte[] data = new byte[4]; |
|
712 int read; |
|
713 //the begin |
|
714 while ((read = is.read()) > 0) { |
|
715 byte readed = (byte)read; |
|
716 if (isWhiteSpace(readed)) { |
|
717 continue; |
|
718 } |
|
719 if (isPad(readed)) { |
|
720 data[index++] = readed; |
|
721 if (index == 3) { |
|
722 data[index++] = (byte)is.read(); |
|
723 } |
|
724 break; |
|
725 } |
|
726 |
|
727 if ((data[index++] = readed) == -1) { |
|
728 //if found "no data" just return null |
|
729 throw new Base64DecodingException("decoding.general"); |
|
730 } |
|
731 |
|
732 if (index != 4) { |
|
733 continue; |
|
734 } |
|
735 index = 0; |
|
736 b1 = base64Alphabet[data[0]]; |
|
737 b2 = base64Alphabet[data[1]]; |
|
738 b3 = base64Alphabet[data[2]]; |
|
739 b4 = base64Alphabet[data[3]]; |
|
740 |
|
741 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
742 os.write((byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf))); |
|
743 os.write((byte)(b3 << 6 | b4)); |
|
744 } |
|
745 |
|
746 byte d1 = data[0], d2 = data[1], d3 = data[2], d4 = data[3]; |
|
747 b1 = base64Alphabet[d1]; |
|
748 b2 = base64Alphabet[d2]; |
|
749 b3 = base64Alphabet[d3]; |
|
750 b4 = base64Alphabet[d4]; |
|
751 if ((b3 == -1) || (b4 == -1)) { //Check if they are PAD characters |
|
752 if (isPad(d3) && isPad(d4)) { //Two PAD e.g. 3c[Pad][Pad] |
|
753 if ((b2 & 0xf) != 0) { //last 4 bits should be zero |
|
754 throw new Base64DecodingException("decoding.general"); |
|
755 } |
|
756 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
757 } else if (!isPad(d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad] |
|
758 b3 = base64Alphabet[d3]; |
|
759 if ((b3 & 0x3) != 0) { //last 2 bits should be zero |
|
760 throw new Base64DecodingException("decoding.general"); |
|
761 } |
|
762 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
763 os.write((byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf))); |
|
764 } else { |
|
765 //an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data |
|
766 throw new Base64DecodingException("decoding.general"); |
|
767 } |
|
768 } else { |
|
769 //No PAD e.g 3cQl |
|
770 os.write((byte)(b1 << 2 | b2 >> 4)); |
|
771 os.write((byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf))); |
|
772 os.write((byte)(b3 << 6 | b4)); |
|
773 } |
|
774 } |
|
775 |
|
776 /** |
|
777 * remove WhiteSpace from MIME containing encoded Base64 data. |
|
778 * |
|
779 * @param data the byte array of base64 data (with WS) |
|
780 * @return the new length |
|
781 */ |
|
782 protected static final int removeWhiteSpace(byte[] data) { |
|
783 if (data == null) { |
|
784 return 0; |
|
785 } |
|
786 |
|
787 // count characters that's not whitespace |
|
788 int newSize = 0; |
|
789 int len = data.length; |
|
790 for (int i = 0; i < len; i++) { |
|
791 byte dataS = data[i]; |
|
792 if (!isWhiteSpace(dataS)) { |
|
793 data[newSize++] = dataS; |
|
794 } |
|
795 } |
|
796 return newSize; |
|
797 } |
|
798 } |