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 long lengthDataBits = ((long) binaryData.length) * ((long) EIGHTBIT); 380 if (lengthDataBits == 0L) { 381 return ""; 382 } 383 384 long fewerThan24bits = lengthDataBits % ((long) TWENTYFOURBITGROUP); 385 int numberTriplets = (int) (lengthDataBits / TWENTYFOURBITGROUP); 386 int numberQuartet = fewerThan24bits != 0L ? 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 }