View Javadoc
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         StringBuffer sb = new StringBuffer();
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 }