• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.email.codec.binary;
19 
20 import org.apache.commons.codec.BinaryDecoder;
21 import org.apache.commons.codec.BinaryEncoder;
22 import org.apache.commons.codec.DecoderException;
23 import org.apache.commons.codec.EncoderException;
24 
25 import java.io.UnsupportedEncodingException;
26 import java.math.BigInteger;
27 
28 /**
29  * Provides Base64 encoding and decoding as defined by RFC 2045.
30  *
31  * <p>
32  * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
33  * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
34  * </p>
35  *
36  * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
37  * @author Apache Software Foundation
38  * @since 1.0-dev
39  * @version $Id$
40  */
41 public class Base64 implements BinaryEncoder, BinaryDecoder {
42     /**
43      * Chunk size per RFC 2045 section 6.8.
44      *
45      * <p>
46      * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
47      * equal signs.
48      * </p>
49      *
50      * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
51      */
52     static final int CHUNK_SIZE = 76;
53 
54     /**
55      * Chunk separator per RFC 2045 section 2.1.
56      *
57      * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
58      */
59     static final byte[] CHUNK_SEPARATOR = {'\r','\n'};
60 
61     /**
62      * This array is a lookup table that translates 6-bit positive integer
63      * index values into their "Base64 Alphabet" equivalents as specified
64      * in Table 1 of RFC 2045.
65      *
66      * Thanks to "commons" project in ws.apache.org for this code.
67      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
68      */
69     private static final byte[] intToBase64 = {
70             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
71             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
72             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
73             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
74             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
75     };
76 
77     /**
78      * Byte used to pad output.
79      */
80     private static final byte PAD = '=';
81 
82     /**
83      * This array is a lookup table that translates unicode characters
84      * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
85      * into their 6-bit positive integer equivalents.  Characters that
86      * are not in the Base64 alphabet but fall within the bounds of the
87      * array are translated to -1.
88      *
89      * Thanks to "commons" project in ws.apache.org for this code.
90      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
91      */
92     private static final byte[] base64ToInt = {
93             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
94             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
95             -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
96             55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
97             5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
98             24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
99             35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
100     };
101 
102     /** Mask used to extract 6 bits, used when encoding */
103     private static final int MASK_6BITS = 0x3f;
104 
105     /** Mask used to extract 8 bits, used in decoding base64 bytes */
106     private static final int MASK_8BITS = 0xff;
107 
108     // The static final fields above are used for the original static byte[] methods on Base64.
109     // The private member fields below are used with the new streaming approach, which requires
110     // some state be preserved between calls of encode() and decode().
111 
112 
113     /**
114      * Line length for encoding.  Not used when decoding.  A value of zero or less implies
115      * no chunking of the base64 encoded data.
116      */
117     private final int lineLength;
118 
119     /**
120      * Line separator for encoding.  Not used when decoding.  Only used if lineLength > 0.
121      */
122     private final byte[] lineSeparator;
123 
124     /**
125      * Convenience variable to help us determine when our buffer is going to run out of
126      * room and needs resizing.  <code>decodeSize = 3 + lineSeparator.length;</code>
127      */
128     private final int decodeSize;
129 
130     /**
131      * Convenience variable to help us determine when our buffer is going to run out of
132      * room and needs resizing.  <code>encodeSize = 4 + lineSeparator.length;</code>
133      */
134     private final int encodeSize;
135 
136     /**
137      * Buffer for streaming.
138      */
139     private byte[] buf;
140 
141     /**
142      * Position where next character should be written in the buffer.
143      */
144     private int pos;
145 
146     /**
147      * Position where next character should be read from the buffer.
148      */
149     private int readPos;
150 
151     /**
152      * Variable tracks how many characters have been written to the current line.
153      * Only used when encoding.  We use it to make sure each encoded line never
154      * goes beyond lineLength (if lineLength > 0).
155      */
156     private int currentLinePos;
157 
158     /**
159      * Writes to the buffer only occur after every 3 reads when encoding, an
160      * every 4 reads when decoding.  This variable helps track that.
161      */
162     private int modulus;
163 
164     /**
165      * Boolean flag to indicate the EOF has been reached.  Once EOF has been
166      * reached, this Base64 object becomes useless, and must be thrown away.
167      */
168     private boolean eof;
169 
170     /**
171      * Place holder for the 3 bytes we're dealing with for our base64 logic.
172      * Bitwise operations store and extract the base64 encoding or decoding from
173      * this variable.
174      */
175     private int x;
176 
177     /**
178      * Default constructor:  lineLength is 76, and the lineSeparator is CRLF
179      * when encoding, and all forms can be decoded.
180      */
Base64()181     public Base64() {
182         this(CHUNK_SIZE, CHUNK_SEPARATOR);
183     }
184 
185     /**
186      * <p>
187      * Consumer can use this constructor to choose a different lineLength
188      * when encoding (lineSeparator is still CRLF).  All forms of data can
189      * be decoded.
190      * </p><p>
191      * Note:  lineLengths that aren't multiples of 4 will still essentially
192      * end up being multiples of 4 in the encoded data.
193      * </p>
194      *
195      * @param lineLength each line of encoded data will be at most this long
196      * (rounded up to nearest multiple of 4).
197      * If lineLength <= 0, then the output will not be divided into lines (chunks).
198      * Ignored when decoding.
199      */
Base64(int lineLength)200     public Base64(int lineLength) {
201         this(lineLength, CHUNK_SEPARATOR);
202     }
203 
204     /**
205      * <p>
206      * Consumer can use this constructor to choose a different lineLength
207      * and lineSeparator when encoding.  All forms of data can
208      * be decoded.
209      * </p><p>
210      * Note:  lineLengths that aren't multiples of 4 will still essentially
211      * end up being multiples of 4 in the encoded data.
212      * </p>
213      * @param lineLength    Each line of encoded data will be at most this long
214      *                      (rounded up to nearest multiple of 4).  Ignored when decoding.
215      *                      If <= 0, then output will not be divided into lines (chunks).
216      * @param lineSeparator Each line of encoded data will end with this
217      *                      sequence of bytes.
218      *                      If lineLength <= 0, then the lineSeparator is not used.
219      * @throws IllegalArgumentException The provided lineSeparator included
220      *                                  some base64 characters.  That's not going to work!
221      */
Base64(int lineLength, byte[] lineSeparator)222     public Base64(int lineLength, byte[] lineSeparator) {
223         this.lineLength = lineLength;
224         this.lineSeparator = new byte[lineSeparator.length];
225         System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
226         if (lineLength > 0) {
227             this.encodeSize = 4 + lineSeparator.length;
228         } else {
229             this.encodeSize = 4;
230         }
231         this.decodeSize = encodeSize - 1;
232         if (containsBase64Byte(lineSeparator)) {
233             String sep;
234             try {
235                 sep = new String(lineSeparator, "UTF-8");
236             } catch (UnsupportedEncodingException uee) {
237                 sep = new String(lineSeparator);
238             }
239             throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
240         }
241     }
242 
243     /**
244      * Returns true if this Base64 object has buffered data for reading.
245      *
246      * @return true if there is Base64 object still available for reading.
247      */
hasData()248     boolean hasData() { return buf != null; }
249 
250     /**
251      * Returns the amount of buffered data available for reading.
252      *
253      * @return The amount of buffered data available for reading.
254      */
avail()255     int avail() { return buf != null ? pos - readPos : 0; }
256 
257     /** Doubles our buffer. */
resizeBuf()258     private void resizeBuf() {
259         if (buf == null) {
260             buf = new byte[8192];
261             pos = 0;
262             readPos = 0;
263         } else {
264             byte[] b = new byte[buf.length * 2];
265             System.arraycopy(buf, 0, b, 0, buf.length);
266             buf = b;
267         }
268     }
269 
270     /**
271      * Extracts buffered data into the provided byte[] array, starting
272      * at position bPos, up to a maximum of bAvail bytes.  Returns how
273      * many bytes were actually extracted.
274      *
275      * @param b      byte[] array to extract the buffered data into.
276      * @param bPos   position in byte[] array to start extraction at.
277      * @param bAvail amount of bytes we're allowed to extract.  We may extract
278      *               fewer (if fewer are available).
279      * @return The number of bytes successfully extracted into the provided
280      *         byte[] array.
281      */
readResults(byte[] b, int bPos, int bAvail)282     int readResults(byte[] b, int bPos, int bAvail) {
283         if (buf != null) {
284             int len = Math.min(avail(), bAvail);
285             if (buf != b) {
286                 System.arraycopy(buf, readPos, b, bPos, len);
287                 readPos += len;
288                 if (readPos >= pos) {
289                     buf = null;
290                 }
291             } else {
292                 // Re-using the original consumer's output array is only
293                 // allowed for one round.
294                 buf = null;
295             }
296             return len;
297         } else {
298             return eof ? -1 : 0;
299         }
300     }
301 
302     /**
303      * Small optimization where we try to buffer directly to the consumer's
304      * output array for one round (if consumer calls this method first!) instead
305      * of starting our own buffer.
306      *
307      * @param out byte[] array to buffer directly to.
308      * @param outPos Position to start buffering into.
309      * @param outAvail Amount of bytes available for direct buffering.
310      */
setInitialBuffer(byte[] out, int outPos, int outAvail)311     void setInitialBuffer(byte[] out, int outPos, int outAvail) {
312         // We can re-use consumer's original output array under
313         // special circumstances, saving on some System.arraycopy().
314         if (out != null && out.length == outAvail) {
315             buf = out;
316             pos = outPos;
317             readPos = outPos;
318         }
319     }
320 
321     /**
322      * <p>
323      * Encodes all of the provided data, starting at inPos, for inAvail bytes.
324      * Must be called at least twice:  once with the data to encode, and once
325      * with inAvail set to "-1" to alert encoder that EOF has been reached,
326      * so flush last remaining bytes (if not multiple of 3).
327      * </p><p>
328      * Thanks to "commons" project in ws.apache.org for the bitwise operations,
329      * and general approach.
330      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
331      * </p>
332      *
333      * @param in byte[] array of binary data to base64 encode.
334      * @param inPos Position to start reading data from.
335      * @param inAvail Amount of bytes available from input for encoding.
336      */
encode(byte[] in, int inPos, int inAvail)337     void encode(byte[] in, int inPos, int inAvail) {
338         if (eof) {
339             return;
340         }
341 
342         // inAvail < 0 is how we're informed of EOF in the underlying data we're
343         // encoding.
344         if (inAvail < 0) {
345             eof = true;
346             if (buf == null || buf.length - pos < encodeSize) {
347                 resizeBuf();
348             }
349             switch (modulus) {
350                 case 1:
351                     buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
352                     buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
353                     buf[pos++] = PAD;
354                     buf[pos++] = PAD;
355                     break;
356 
357                 case 2:
358                     buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
359                     buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
360                     buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
361                     buf[pos++] = PAD;
362                     break;
363             }
364             if (lineLength > 0) {
365                 System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
366                 pos += lineSeparator.length;
367             }
368         } else {
369             for (int i = 0; i < inAvail; i++) {
370                 if (buf == null || buf.length - pos < encodeSize) {
371                     resizeBuf();
372                 }
373                 modulus = (++modulus) % 3;
374                 int b = in[inPos++];
375                 if (b < 0) { b += 256; }
376                 x = (x << 8) + b;
377                 if (0 == modulus) {
378                     buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
379                     buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
380                     buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
381                     buf[pos++] = intToBase64[x & MASK_6BITS];
382                     currentLinePos += 4;
383                     if (lineLength > 0 && lineLength <= currentLinePos) {
384                         System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
385                         pos += lineSeparator.length;
386                         currentLinePos = 0;
387                     }
388                 }
389             }
390         }
391     }
392 
393     /**
394      * <p>
395      * Decodes all of the provided data, starting at inPos, for inAvail bytes.
396      * Should be called at least twice:  once with the data to decode, and once
397      * with inAvail set to "-1" to alert decoder that EOF has been reached.
398      * The "-1" call is not necessary when decoding, but it doesn't hurt, either.
399      * </p><p>
400      * Ignores all non-base64 characters.  This is how chunked (e.g. 76 character)
401      * data is handled, since CR and LF are silently ignored, but has implications
402      * for other bytes, too.  This method subscribes to the garbage-in, garbage-out
403      * philosophy:  it will not check the provided data for validity.
404      * </p><p>
405      * Thanks to "commons" project in ws.apache.org for the bitwise operations,
406      * and general approach.
407      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
408      * </p>
409 
410      * @param in byte[] array of ascii data to base64 decode.
411      * @param inPos Position to start reading data from.
412      * @param inAvail Amount of bytes available from input for encoding.
413      */
decode(byte[] in, int inPos, int inAvail)414     void decode(byte[] in, int inPos, int inAvail) {
415         if (eof) {
416             return;
417         }
418         if (inAvail < 0) {
419             eof = true;
420         }
421         for (int i = 0; i < inAvail; i++) {
422             if (buf == null || buf.length - pos < decodeSize) {
423                 resizeBuf();
424             }
425             byte b = in[inPos++];
426             if (b == PAD) {
427                 x = x << 6;
428                 switch (modulus) {
429                     case 2:
430                         x = x << 6;
431                         buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
432                         break;
433                     case 3:
434                         buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
435                         buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
436                         break;
437                 }
438                 // WE'RE DONE!!!!
439                 eof = true;
440                 return;
441             } else {
442                 if (b >= 0 && b < base64ToInt.length) {
443                     int result = base64ToInt[b];
444                     if (result >= 0) {
445                         modulus = (++modulus) % 4;
446                         x = (x << 6) + result;
447                         if (modulus == 0) {
448                             buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
449                             buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
450                             buf[pos++] = (byte) (x & MASK_8BITS);
451                         }
452                     }
453                 }
454             }
455         }
456     }
457 
458     /**
459      * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
460      *
461      * @param octet
462      *            The value to test
463      * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
464      */
isBase64(byte octet)465     public static boolean isBase64(byte octet) {
466         return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
467     }
468 
469     /**
470      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
471      * Currently the method treats whitespace as valid.
472      *
473      * @param arrayOctet
474      *            byte array to test
475      * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is
476      *         empty; false, otherwise
477      */
isArrayByteBase64(byte[] arrayOctet)478     public static boolean isArrayByteBase64(byte[] arrayOctet) {
479         for (int i = 0; i < arrayOctet.length; i++) {
480             if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
481                 return false;
482             }
483         }
484         return true;
485     }
486 
487     /*
488      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
489      *
490      * @param arrayOctet
491      *            byte array to test
492      * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
493      */
containsBase64Byte(byte[] arrayOctet)494     private static boolean containsBase64Byte(byte[] arrayOctet) {
495         for (int i = 0; i < arrayOctet.length; i++) {
496             if (isBase64(arrayOctet[i])) {
497                 return true;
498             }
499         }
500         return false;
501     }
502 
503     /**
504      * Encodes binary data using the base64 algorithm but does not chunk the output.
505      *
506      * @param binaryData
507      *            binary data to encode
508      * @return Base64 characters
509      */
encodeBase64(byte[] binaryData)510     public static byte[] encodeBase64(byte[] binaryData) {
511         return encodeBase64(binaryData, false);
512     }
513 
514     /**
515      * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
516      *
517      * @param binaryData
518      *            binary data to encode
519      * @return Base64 characters chunked in 76 character blocks
520      */
encodeBase64Chunked(byte[] binaryData)521     public static byte[] encodeBase64Chunked(byte[] binaryData) {
522         return encodeBase64(binaryData, true);
523     }
524 
525     /**
526      * Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
527      * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[].
528      *
529      * @param pObject
530      *            Object to decode
531      * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] supplied.
532      * @throws DecoderException
533      *             if the parameter supplied is not of type byte[]
534      */
decode(Object pObject)535     public Object decode(Object pObject) throws DecoderException {
536         if (!(pObject instanceof byte[])) {
537             throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
538         }
539         return decode((byte[]) pObject);
540     }
541 
542     /**
543      * Decodes a byte[] containing containing characters in the Base64 alphabet.
544      *
545      * @param pArray
546      *            A byte array containing Base64 character data
547      * @return a byte array containing binary data
548      */
decode(byte[] pArray)549     public byte[] decode(byte[] pArray) {
550         return decodeBase64(pArray);
551     }
552 
553     /**
554      * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
555      *
556      * @param binaryData
557      *            Array containing binary data to encode.
558      * @param isChunked
559      *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
560      * @return Base64-encoded data.
561      * @throws IllegalArgumentException
562      *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
563      */
encodeBase64(byte[] binaryData, boolean isChunked)564     public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
565         if (binaryData == null || binaryData.length == 0) {
566             return binaryData;
567         }
568         Base64 b64 = isChunked ? new Base64() : new Base64(0);
569 
570         long len = (binaryData.length * 4) / 3;
571         long mod = len % 4;
572         if (mod != 0) {
573             len += 4 - mod;
574         }
575         // If chunked, add space for one CHUNK_SEPARATOR per chunk.  (Technically, these are chunk
576         // terminators, because even a single chunk message has one.)
577         //
578         //  User length     Encoded length      Rounded up by 4     Num chunks     Final buf len
579         //      56              74                  76                  1               78
580         //      57              76                  76                  1               78
581         //      58              77                  80                  2               84
582         //      59              78                  80                  2               84
583         //
584         // Or...
585         //    Rounded up size:   4...76    Chunks:  1
586         //    Rounded up size:  80..152    Chunks:  2
587         //    Rounded up size: 156..228    Chunks:  3     ...etc...
588         if (isChunked) {
589             len += ((len + CHUNK_SIZE - 1) / CHUNK_SIZE) * CHUNK_SEPARATOR.length;
590         }
591 
592         if (len > Integer.MAX_VALUE) {
593             throw new IllegalArgumentException(
594                     "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE);
595         }
596         byte[] buf = new byte[(int) len];
597         b64.setInitialBuffer(buf, 0, buf.length);
598         b64.encode(binaryData, 0, binaryData.length);
599         b64.encode(binaryData, 0, -1); // Notify encoder of EOF.
600 
601         // Encoder might have resized, even though it was unnecessary.
602         if (b64.buf != buf) {
603             b64.readResults(buf, 0, buf.length);
604         }
605         return buf;
606     }
607 
608     /**
609      * Decodes Base64 data into octets
610      *
611      * @param base64Data Byte array containing Base64 data
612      * @return Array containing decoded data.
613      */
decodeBase64(byte[] base64Data)614     public static byte[] decodeBase64(byte[] base64Data) {
615         if (base64Data == null || base64Data.length == 0) {
616             return base64Data;
617         }
618         Base64 b64 = new Base64();
619 
620         long len = (base64Data.length * 3) / 4;
621         byte[] buf = new byte[(int) len];
622         b64.setInitialBuffer(buf, 0, buf.length);
623         b64.decode(base64Data, 0, base64Data.length);
624         b64.decode(base64Data, 0, -1); // Notify decoder of EOF.
625 
626         // We have no idea what the line-length was, so we
627         // cannot know how much of our array wasn't used.
628         byte[] result = new byte[b64.pos];
629         b64.readResults(result, 0, result.length);
630         return result;
631     }
632 
633     /**
634      * Discards any whitespace from a base-64 encoded block.
635      *
636      * @param data
637      *            The base-64 encoded data to discard the whitespace from.
638      * @return The data, less whitespace (see RFC 2045).
639      * @deprecated This method is no longer needed
640      */
discardWhitespace(byte[] data)641     static byte[] discardWhitespace(byte[] data) {
642         byte groomedData[] = new byte[data.length];
643         int bytesCopied = 0;
644 
645         for (int i = 0; i < data.length; i++) {
646             switch (data[i]) {
647                 case ' ' :
648                 case '\n' :
649                 case '\r' :
650                 case '\t' :
651                     break;
652                 default :
653                     groomedData[bytesCopied++] = data[i];
654             }
655         }
656 
657         byte packedData[] = new byte[bytesCopied];
658 
659         System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
660 
661         return packedData;
662     }
663 
664 
665     /**
666      * Check if a byte value is whitespace or not.
667      *
668      * @param byteToCheck the byte to check
669      * @return true if byte is whitespace, false otherwise
670      */
isWhiteSpace(byte byteToCheck)671     private static boolean isWhiteSpace(byte byteToCheck){
672         switch (byteToCheck) {
673         case ' ' :
674         case '\n' :
675         case '\r' :
676         case '\t' :
677             return true;
678         default :
679             return false;
680         }
681     }
682 
683     /**
684      * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any
685      * characters outside of the base64 alphabet are to be ignored in base64 encoded data."
686      *
687      * @param data
688      *            The base-64 encoded data to groom
689      * @return The data, less non-base64 characters (see RFC 2045).
690      */
discardNonBase64(byte[] data)691     static byte[] discardNonBase64(byte[] data) {
692         byte groomedData[] = new byte[data.length];
693         int bytesCopied = 0;
694 
695         for (int i = 0; i < data.length; i++) {
696             if (isBase64(data[i])) {
697                 groomedData[bytesCopied++] = data[i];
698             }
699         }
700 
701         byte packedData[] = new byte[bytesCopied];
702 
703         System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
704 
705         return packedData;
706     }
707 
708     // Implementation of the Encoder Interface
709 
710     /**
711      * Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
712      * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
713      *
714      * @param pObject
715      *            Object to encode
716      * @return An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied.
717      * @throws EncoderException
718      *             if the parameter supplied is not of type byte[]
719      */
encode(Object pObject)720     public Object encode(Object pObject) throws EncoderException {
721         if (!(pObject instanceof byte[])) {
722             throw new EncoderException("Parameter supplied to Base64 encode is not a byte[]");
723         }
724         return encode((byte[]) pObject);
725     }
726 
727     /**
728      * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
729      *
730      * @param pArray
731      *            a byte array containing binary data
732      * @return A byte array containing only Base64 character data
733      */
encode(byte[] pArray)734     public byte[] encode(byte[] pArray) {
735         return encodeBase64(pArray, false);
736     }
737 
738     // Implementation of integer encoding used for crypto
739     /**
740      * Decode a byte64-encoded integer according to crypto
741      * standards such as W3C's XML-Signature
742      *
743      * @param pArray a byte array containing base64 character data
744      * @return A BigInteger
745      */
decodeInteger(byte[] pArray)746     public static BigInteger decodeInteger(byte[] pArray) {
747         return new BigInteger(1, decodeBase64(pArray));
748     }
749 
750     /**
751      * Encode to a byte64-encoded integer according to crypto
752      * standards such as W3C's XML-Signature
753      *
754      * @param bigInt a BigInteger
755      * @return A byte array containing base64 character data
756      * @throws NullPointerException if null is passed in
757      */
encodeInteger(BigInteger bigInt)758     public static byte[] encodeInteger(BigInteger bigInt) {
759         if(bigInt == null)  {
760             throw new NullPointerException("encodeInteger called with null parameter");
761         }
762 
763         return encodeBase64(toIntegerBytes(bigInt), false);
764     }
765 
766     /**
767      * Returns a byte-array representation of a <code>BigInteger</code>
768      * without sign bit.
769      *
770      * @param bigInt <code>BigInteger</code> to be converted
771      * @return a byte array representation of the BigInteger parameter
772      */
toIntegerBytes(BigInteger bigInt)773      static byte[] toIntegerBytes(BigInteger bigInt) {
774         int bitlen = bigInt.bitLength();
775         // round bitlen
776         bitlen = ((bitlen + 7) >> 3) << 3;
777         byte[] bigBytes = bigInt.toByteArray();
778 
779         if(((bigInt.bitLength() % 8) != 0) &&
780             (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
781             return bigBytes;
782         }
783 
784         // set up params for copying everything but sign bit
785         int startSrc = 0;
786         int len = bigBytes.length;
787 
788         // if bigInt is exactly byte-aligned, just skip signbit in copy
789         if((bigInt.bitLength() % 8) == 0) {
790             startSrc = 1;
791             len--;
792         }
793 
794         int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
795         byte[] resizedBytes = new byte[bitlen / 8];
796 
797         System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
798 
799         return resizedBytes;
800     }
801 }
802