View Javadoc
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 org.apache.commons.net.util;
19  
20  import java.io.UnsupportedEncodingException;
21  import java.math.BigInteger;
22  
23  
24  
25  /**
26   * Provides Base64 encoding and decoding as defined by RFC 2045.
27   *
28   * <p>
29   * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
30   * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
31   * </p>
32   * <p>
33   * The class can be parameterized in the following manner with various constructors:
34   * <ul>
35   * <li>URL-safe mode: Default off.</li>
36   * <li>Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of
37   * 4 in the encoded data.
38   * <li>Line separator: Default is CRLF ("\r\n")</li>
39   * </ul>
40   * <p>
41   * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode
42   * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).
43   * </p>
44   *
45   * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
46   * @since 2.2
47   * @version $Id: Base64.java 1697293 2015-08-24 01:01:00Z sebb $
48   */
49  public class Base64 {
50      private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;
51  
52      private static final int DEFAULT_BUFFER_SIZE = 8192;
53  
54      /**
55       * Chunk size per RFC 2045 section 6.8.
56       *
57       * <p>
58       * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
59       * equal signs.
60       * </p>
61       *
62       * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
63       */
64      static final int CHUNK_SIZE = 76;
65  
66      /**
67       * Chunk separator per RFC 2045 section 2.1.
68       *
69       * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
70       */
71      private static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
72  
73      private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
74  
75      /**
76       * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"
77       * equivalents as specified in Table 1 of RFC 2045.
78       *
79       * Thanks to "commons" project in ws.apache.org for this code.
80       * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
81       */
82      private static final byte[] STANDARD_ENCODE_TABLE = {
83              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
84              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
85              'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
86              'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
87              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
88      };
89  
90      /**
91       * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and /
92       * changed to - and _ to make the encoded Base64 results more URL-SAFE.
93       * This table is only used when the Base64's mode is set to URL-SAFE.
94       */
95      private static final byte[] URL_SAFE_ENCODE_TABLE = {
96              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
97              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
98              'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
99              'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
100             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
101     };
102 
103     /**
104      * Byte used to pad output.
105      */
106     private static final byte PAD = '=';
107 
108     /**
109      * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in
110      * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64
111      * alphabet but fall within the bounds of the array are translated to -1.
112      *
113      * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both
114      * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit).
115      *
116      * Thanks to "commons" project in ws.apache.org for this code.
117      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
118      */
119     private static final byte[] DECODE_TABLE = {
120             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122             -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54,
123             55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
124             5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
125             24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
126             35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
127     };
128 
129     /** Mask used to extract 6 bits, used when encoding */
130     private static final int MASK_6BITS = 0x3f;
131 
132     /** Mask used to extract 8 bits, used in decoding base64 bytes */
133     private static final int MASK_8BITS = 0xff;
134 
135     // The static final fields above are used for the original static byte[] methods on Base64.
136     // The private member fields below are used with the new streaming approach, which requires
137     // some state be preserved between calls of encode() and decode().
138 
139     /**
140      * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able
141      * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch
142      * between the two modes.
143      */
144     private final byte[] encodeTable;
145 
146     /**
147      * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64
148      * encoded data.
149      */
150     private final int lineLength;
151 
152     /**
153      * Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
154      */
155     private final byte[] lineSeparator;
156 
157     /**
158      * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
159      * <code>decodeSize = 3 + lineSeparator.length;</code>
160      */
161     private final int decodeSize;
162 
163     /**
164      * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
165      * <code>encodeSize = 4 + lineSeparator.length;</code>
166      */
167     private final int encodeSize;
168 
169     /**
170      * Buffer for streaming.
171      */
172     private byte[] buffer;
173 
174     /**
175      * Position where next character should be written in the buffer.
176      */
177     private int pos;
178 
179     /**
180      * Position where next character should be read from the buffer.
181      */
182     private int readPos;
183 
184     /**
185      * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to
186      * make sure each encoded line never goes beyond lineLength (if lineLength > 0).
187      */
188     private int currentLinePos;
189 
190     /**
191      * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable
192      * helps track that.
193      */
194     private int modulus;
195 
196     /**
197      * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless,
198      * and must be thrown away.
199      */
200     private boolean eof;
201 
202     /**
203      * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the
204      * base64 encoding or decoding from this variable.
205      */
206     private int x;
207 
208     /**
209      * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
210      * <p>
211      * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
212      * </p>
213      *
214      * <p>
215      * When decoding all variants are supported.
216      * </p>
217      */
218     public Base64() {
219         this(false);
220     }
221 
222     /**
223      * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.
224      * <p>
225      * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
226      * </p>
227      *
228      * <p>
229      * When decoding all variants are supported.
230      * </p>
231      *
232      * @param urlSafe
233      *            if <code>true</code>, URL-safe encoding is used. In most cases this should be set to
234      *            <code>false</code>.
235      * @since 1.4
236      */
237     public Base64(boolean urlSafe) {
238         this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
239     }
240 
241     /**
242      * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
243      * <p>
244      * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is
245      * STANDARD_ENCODE_TABLE.
246      * </p>
247      * <p>
248      * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
249      * </p>
250      * <p>
251      * When decoding all variants are supported.
252      * </p>
253      *
254      * @param lineLength
255      *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
256      *            If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored when decoding.
257      * @since 1.4
258      */
259     public Base64(int lineLength) {
260         this(lineLength, CHUNK_SEPARATOR);
261     }
262 
263     /**
264      * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
265      * <p>
266      * When encoding the line length and line separator are given in the constructor, and the encoding table is
267      * STANDARD_ENCODE_TABLE.
268      * </p>
269      * <p>
270      * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
271      * </p>
272      * <p>
273      * When decoding all variants are supported.
274      * </p>
275      *
276      * @param lineLength
277      *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
278      *            If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored when decoding.
279      * @param lineSeparator
280      *            Each line of encoded data will end with this sequence of bytes.
281      * @throws IllegalArgumentException
282      *             Thrown when the provided lineSeparator included some base64 characters.
283      * @since 1.4
284      */
285     public Base64(int lineLength, byte[] lineSeparator) {
286         this(lineLength, lineSeparator, false);
287     }
288 
289     /**
290      * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
291      * <p>
292      * When encoding the line length and line separator are given in the constructor, and the encoding table is
293      * STANDARD_ENCODE_TABLE.
294      * </p>
295      * <p>
296      * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
297      * </p>
298      * <p>
299      * When decoding all variants are supported.
300      * </p>
301      *
302      * @param lineLength
303      *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
304      *            If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored when decoding.
305      * @param lineSeparator
306      *            Each line of encoded data will end with this sequence of bytes.
307      * @param urlSafe
308      *            Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
309      *            operations. Decoding seamlessly handles both modes.
310      * @throws IllegalArgumentException
311      *             The provided lineSeparator included some base64 characters. That's not going to work!
312      * @since 1.4
313      */
314     public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) {
315         if (lineSeparator == null) {
316             lineLength = 0;  // disable chunk-separating
317             lineSeparator = EMPTY_BYTE_ARRAY;  // this just gets ignored
318         }
319         this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0;
320         this.lineSeparator = new byte[lineSeparator.length];
321         System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
322         if (lineLength > 0) {
323             this.encodeSize = 4 + lineSeparator.length;
324         } else {
325             this.encodeSize = 4;
326         }
327         this.decodeSize = this.encodeSize - 1;
328         if (containsBase64Byte(lineSeparator)) {
329             String sep = newStringUtf8(lineSeparator);
330             throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
331         }
332         this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
333     }
334 
335     /**
336      * Returns our current encode mode. True if we're URL-SAFE, false otherwise.
337      *
338      * @return true if we're in URL-SAFE mode, false otherwise.
339      * @since 1.4
340      */
341     public boolean isUrlSafe() {
342         return this.encodeTable == URL_SAFE_ENCODE_TABLE;
343     }
344 
345     /**
346      * Returns true if this Base64 object has buffered data for reading.
347      *
348      * @return true if there is Base64 object still available for reading.
349      */
350     boolean hasData() {
351         return this.buffer != null;
352     }
353 
354     /**
355      * Returns the amount of buffered data available for reading.
356      *
357      * @return The amount of buffered data available for reading.
358      */
359     int avail() {
360         return buffer != null ? pos - readPos : 0;
361     }
362 
363     /** Doubles our buffer. */
364     private void resizeBuffer() {
365         if (buffer == null) {
366             buffer = new byte[DEFAULT_BUFFER_SIZE];
367             pos = 0;
368             readPos = 0;
369         } else {
370             byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR];
371             System.arraycopy(buffer, 0, b, 0, buffer.length);
372             buffer = b;
373         }
374     }
375 
376     /**
377      * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail
378      * bytes. Returns how many bytes were actually extracted.
379      *
380      * @param b
381      *            byte[] array to extract the buffered data into.
382      * @param bPos
383      *            position in byte[] array to start extraction at.
384      * @param bAvail
385      *            amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
386      * @return The number of bytes successfully extracted into the provided byte[] array.
387      */
388     int readResults(byte[] b, int bPos, int bAvail) {
389         if (buffer != null) {
390             int len = Math.min(avail(), bAvail);
391             if (buffer != b) {
392                 System.arraycopy(buffer, readPos, b, bPos, len);
393                 readPos += len;
394                 if (readPos >= pos) {
395                     buffer = null;
396                 }
397             } else {
398                 // Re-using the original consumer's output array is only
399                 // allowed for one round.
400                 buffer = null;
401             }
402             return len;
403         }
404         return eof ? -1 : 0;
405     }
406 
407     /**
408      * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output
409      * array for one round (if the consumer calls this method first) instead of starting our own buffer.
410      *
411      * @param out
412      *            byte[] array to buffer directly to.
413      * @param outPos
414      *            Position to start buffering into.
415      * @param outAvail
416      *            Amount of bytes available for direct buffering.
417      */
418     void setInitialBuffer(byte[] out, int outPos, int outAvail) {
419         // We can re-use consumer's original output array under
420         // special circumstances, saving on some System.arraycopy().
421         if (out != null && out.length == outAvail) {
422             buffer = out;
423             pos = outPos;
424             readPos = outPos;
425         }
426     }
427 
428     /**
429      * <p>
430      * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with
431      * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last
432      * remaining bytes (if not multiple of 3).
433      * </p>
434      * <p>
435      * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
436      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
437      * </p>
438      *
439      * @param in
440      *            byte[] array of binary data to base64 encode.
441      * @param inPos
442      *            Position to start reading data from.
443      * @param inAvail
444      *            Amount of bytes available from input for encoding.
445      */
446     void encode(byte[] in, int inPos, int inAvail) {
447         if (eof) {
448             return;
449         }
450         // inAvail < 0 is how we're informed of EOF in the underlying data we're
451         // encoding.
452         if (inAvail < 0) {
453             eof = true;
454             if (buffer == null || buffer.length - pos < encodeSize) {
455                 resizeBuffer();
456             }
457             switch (modulus) {
458                 case 1 :
459                     buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS];
460                     buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS];
461                     // URL-SAFE skips the padding to further reduce size.
462                     if (encodeTable == STANDARD_ENCODE_TABLE) {
463                         buffer[pos++] = PAD;
464                         buffer[pos++] = PAD;
465                     }
466                     break;
467 
468                 case 2 :
469                     buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS];
470                     buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS];
471                     buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS];
472                     // URL-SAFE skips the padding to further reduce size.
473                     if (encodeTable == STANDARD_ENCODE_TABLE) {
474                         buffer[pos++] = PAD;
475                     }
476                     break;
477                 default:
478                     break;  // other values ignored
479             }
480             if (lineLength > 0 && pos > 0) {
481                 System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
482                 pos += lineSeparator.length;
483             }
484         } else {
485             for (int i = 0; i < inAvail; i++) {
486                 if (buffer == null || buffer.length - pos < encodeSize) {
487                     resizeBuffer();
488                 }
489                 modulus = (++modulus) % 3;
490                 int b = in[inPos++];
491                 if (b < 0) {
492                     b += 256;
493                 }
494                 x = (x << 8) + b;
495                 if (0 == modulus) {
496                     buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS];
497                     buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS];
498                     buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS];
499                     buffer[pos++] = encodeTable[x & MASK_6BITS];
500                     currentLinePos += 4;
501                     if (lineLength > 0 && lineLength <= currentLinePos) {
502                         System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
503                         pos += lineSeparator.length;
504                         currentLinePos = 0;
505                     }
506                 }
507             }
508         }
509     }
510 
511     /**
512      * <p>
513      * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once
514      * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1"
515      * call is not necessary when decoding, but it doesn't hurt, either.
516      * </p>
517      * <p>
518      * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are
519      * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,
520      * garbage-out philosophy: it will not check the provided data for validity.
521      * </p>
522      * <p>
523      * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
524      * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
525      * </p>
526      *
527      * @param in
528      *            byte[] array of ascii data to base64 decode.
529      * @param inPos
530      *            Position to start reading data from.
531      * @param inAvail
532      *            Amount of bytes available from input for encoding.
533      */
534     void decode(byte[] in, int inPos, int inAvail) {
535         if (eof) {
536             return;
537         }
538         if (inAvail < 0) {
539             eof = true;
540         }
541         for (int i = 0; i < inAvail; i++) {
542             if (buffer == null || buffer.length - pos < decodeSize) {
543                 resizeBuffer();
544             }
545             byte b = in[inPos++];
546             if (b == PAD) {
547                 // We're done.
548                 eof = true;
549                 break;
550             } else {
551                 if (b >= 0 && b < DECODE_TABLE.length) {
552                     int result = DECODE_TABLE[b];
553                     if (result >= 0) {
554                         modulus = (++modulus) % 4;
555                         x = (x << 6) + result;
556                         if (modulus == 0) {
557                             buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
558                             buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
559                             buffer[pos++] = (byte) (x & MASK_8BITS);
560                         }
561                     }
562                 }
563             }
564         }
565 
566         // Two forms of EOF as far as base64 decoder is concerned: actual
567         // EOF (-1) and first time '=' character is encountered in stream.
568         // This approach makes the '=' padding characters completely optional.
569         if (eof && modulus != 0) {
570             x = x << 6;
571             switch (modulus) {
572                 case 2 :
573                     x = x << 6;
574                     buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
575                     break;
576                 case 3 :
577                     buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
578                     buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
579                     break;
580                 default:
581                     break;  // other values ignored
582             }
583         }
584     }
585 
586     /**
587      * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
588      *
589      * @param octet
590      *            The value to test
591      * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
592      * @since 1.4
593      */
594     public static boolean isBase64(byte octet) {
595         return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1);
596     }
597 
598     /**
599      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the
600      * method treats whitespace as valid.
601      *
602      * @param arrayOctet
603      *            byte array to test
604      * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is empty;
605      *         false, otherwise
606      */
607     public static boolean isArrayByteBase64(byte[] arrayOctet) {
608         for (int i = 0; i < arrayOctet.length; i++) {
609             if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
610                 return false;
611             }
612         }
613         return true;
614     }
615 
616     /**
617      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
618      *
619      * @param arrayOctet
620      *            byte array to test
621      * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
622      */
623     private static boolean containsBase64Byte(byte[] arrayOctet) {
624         for (byte element : arrayOctet)
625         {
626             if (isBase64(element)) {
627                 return true;
628             }
629         }
630         return false;
631     }
632 
633     /**
634      * Encodes binary data using the base64 algorithm but does not chunk the output.
635      *
636      * @param binaryData
637      *            binary data to encode
638      * @return byte[] containing Base64 characters in their UTF-8 representation.
639      */
640     public static byte[] encodeBase64(byte[] binaryData) {
641         return encodeBase64(binaryData, false);
642     }
643 
644     /**
645      * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF.
646      * <p>
647      * For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}.
648      *
649      * @param binaryData
650      *            binary data to encode
651      * @return String containing Base64 characters.
652      * @since 1.4
653      */
654     public static String encodeBase64String(byte[] binaryData) {
655         return newStringUtf8(encodeBase64(binaryData, true));
656     }
657 
658     /**
659      * Encodes binary data using the base64 algorithm, without using chunking.
660      * <p>
661      * For a chunking version, see {@link #encodeBase64String(byte[])}.
662      *
663      * @param binaryData
664      *            binary data to encode
665      * @return String containing Base64 characters.
666      * @since 3.2
667      */
668     public static String encodeBase64StringUnChunked(byte[] binaryData) {
669         return newStringUtf8(encodeBase64(binaryData, false));
670     }
671 
672     /**
673      * Encodes binary data using the base64 algorithm.
674      *
675      * @param binaryData
676      *            binary data to encode
677      * @param useChunking whether to split the output into chunks
678      * @return String containing Base64 characters.
679      * @since 3.2
680      */
681     public static String encodeBase64String(byte[] binaryData, boolean useChunking) {
682         return newStringUtf8(encodeBase64(binaryData, useChunking));
683     }
684 
685     /**
686      * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
687      * url-safe variation emits - and _ instead of + and / characters.
688      *
689      * @param binaryData
690      *            binary data to encode
691      * @return byte[] containing Base64 characters in their UTF-8 representation.
692      * @since 1.4
693      */
694     public static byte[] encodeBase64URLSafe(byte[] binaryData) {
695         return encodeBase64(binaryData, false, true);
696     }
697 
698     /**
699      * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
700      * url-safe variation emits - and _ instead of + and / characters.
701      *
702      * @param binaryData
703      *            binary data to encode
704      * @return String containing Base64 characters
705      * @since 1.4
706      */
707     public static String encodeBase64URLSafeString(byte[] binaryData) {
708         return newStringUtf8(encodeBase64(binaryData, false, true));
709     }
710 
711     /**
712      * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
713      *
714      * @param binaryData
715      *            binary data to encode
716      * @return Base64 characters chunked in 76 character blocks
717      */
718     public static byte[] encodeBase64Chunked(byte[] binaryData) {
719         return encodeBase64(binaryData, true);
720     }
721 
722     /**
723      * Decodes a String containing containing characters in the Base64 alphabet.
724      *
725      * @param pArray
726      *            A String containing Base64 character data
727      * @return a byte array containing binary data
728      * @since 1.4
729      */
730     public byte[] decode(String pArray) {
731         return decode(getBytesUtf8(pArray));
732     }
733 
734     private byte[] getBytesUtf8(String pArray) {
735         try {
736             return pArray.getBytes("UTF8");
737         } catch (UnsupportedEncodingException e) {
738             throw new RuntimeException(e);
739         }
740     }
741 
742     /**
743      * Decodes a byte[] containing containing characters in the Base64 alphabet.
744      *
745      * @param pArray
746      *            A byte array containing Base64 character data
747      * @return a byte array containing binary data
748      */
749     public byte[] decode(byte[] pArray) {
750         reset();
751         if (pArray == null || pArray.length == 0) {
752             return pArray;
753         }
754         long len = (pArray.length * 3) / 4;
755         byte[] buf = new byte[(int) len];
756         setInitialBuffer(buf, 0, buf.length);
757         decode(pArray, 0, pArray.length);
758         decode(pArray, 0, -1); // Notify decoder of EOF.
759 
760         // Would be nice to just return buf (like we sometimes do in the encode
761         // logic), but we have no idea what the line-length was (could even be
762         // variable).  So we cannot determine ahead of time exactly how big an
763         // array is necessary.  Hence the need to construct a 2nd byte array to
764         // hold the final result:
765 
766         byte[] result = new byte[pos];
767         readResults(result, 0, result.length);
768         return result;
769     }
770 
771     /**
772      * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
773      *
774      * @param binaryData
775      *            Array containing binary data to encode.
776      * @param isChunked
777      *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
778      * @return Base64-encoded data.
779      * @throws IllegalArgumentException
780      *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
781      */
782     public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
783         return encodeBase64(binaryData, isChunked, false);
784     }
785 
786     /**
787      * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
788      *
789      * @param binaryData
790      *            Array containing binary data to encode.
791      * @param isChunked
792      *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
793      * @param urlSafe
794      *            if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
795      * @return Base64-encoded data.
796      * @throws IllegalArgumentException
797      *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
798      * @since 1.4
799      */
800     public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) {
801         return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
802     }
803 
804     /**
805      * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
806      *
807      * @param binaryData
808      *            Array containing binary data to encode.
809      * @param isChunked
810      *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
811      * @param urlSafe
812      *            if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
813      * @param maxResultSize
814      *            The maximum result size to accept.
815      * @return Base64-encoded data.
816      * @throws IllegalArgumentException
817      *             Thrown when the input array needs an output array bigger than maxResultSize
818      * @since 1.4
819      */
820     public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) {
821         if (binaryData == null || binaryData.length == 0) {
822             return binaryData;
823         }
824 
825         long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, isChunked ? CHUNK_SEPARATOR : EMPTY_BYTE_ARRAY);
826         if (len > maxResultSize) {
827             throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
828                 len +
829                 ") than the specified maxium size of " +
830                 maxResultSize);
831         }
832 
833         Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
834         return b64.encode(binaryData);
835     }
836 
837     /**
838      * Decodes a Base64 String into octets
839      *
840      * @param base64String
841      *            String containing Base64 data
842      * @return Array containing decoded data.
843      * @since 1.4
844      */
845     public static byte[] decodeBase64(String base64String) {
846         return new Base64().decode(base64String);
847     }
848 
849     /**
850      * Decodes Base64 data into octets
851      *
852      * @param base64Data
853      *            Byte array containing Base64 data
854      * @return Array containing decoded data.
855      */
856     public static byte[] decodeBase64(byte[] base64Data) {
857         return new Base64().decode(base64Data);
858     }
859 
860 
861 
862     /**
863      * Checks if a byte value is whitespace or not.
864      *
865      * @param byteToCheck
866      *            the byte to check
867      * @return true if byte is whitespace, false otherwise
868      */
869     private static boolean isWhiteSpace(byte byteToCheck) {
870         switch (byteToCheck) {
871             case ' ' :
872             case '\n' :
873             case '\r' :
874             case '\t' :
875                 return true;
876             default :
877                 return false;
878         }
879     }
880 
881     /**
882      * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet.
883      *
884      * @param pArray
885      *            a byte array containing binary data
886      * @return A String containing only Base64 character data
887      * @since 1.4
888      */
889     public String encodeToString(byte[] pArray) {
890         return newStringUtf8(encode(pArray));
891     }
892 
893     private static String newStringUtf8(byte[] encode) {
894         String str = null;
895         try {
896             str = new String(encode, "UTF8");
897         } catch (UnsupportedEncodingException ue) {
898             throw new RuntimeException(ue);
899         }
900         return str;
901     }
902 
903     /**
904      * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
905      *
906      * @param pArray
907      *            a byte array containing binary data
908      * @return A byte array containing only Base64 character data
909      */
910     public byte[] encode(byte[] pArray) {
911         reset();
912         if (pArray == null || pArray.length == 0) {
913             return pArray;
914         }
915         long len = getEncodeLength(pArray, lineLength, lineSeparator);
916         byte[] buf = new byte[(int) len];
917         setInitialBuffer(buf, 0, buf.length);
918         encode(pArray, 0, pArray.length);
919         encode(pArray, 0, -1); // Notify encoder of EOF.
920         // Encoder might have resized, even though it was unnecessary.
921         if (buffer != buf) {
922             readResults(buf, 0, buf.length);
923         }
924         // In URL-SAFE mode we skip the padding characters, so sometimes our
925         // final length is a bit smaller.
926         if (isUrlSafe() && pos < buf.length) {
927             byte[] smallerBuf = new byte[pos];
928             System.arraycopy(buf, 0, smallerBuf, 0, pos);
929             buf = smallerBuf;
930         }
931         return buf;
932     }
933 
934     /**
935      * Pre-calculates the amount of space needed to base64-encode the supplied array.
936      *
937      * @param pArray byte[] array which will later be encoded
938      * @param chunkSize line-length of the output (<= 0 means no chunking) between each
939      *        chunkSeparator (e.g. CRLF).
940      * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF).
941      *
942      * @return amount of space needed to encoded the supplied array.  Returns
943      *         a long since a max-len array will require Integer.MAX_VALUE + 33%.
944      */
945     private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) {
946         // base64 always encodes to multiples of 4.
947         chunkSize = (chunkSize / 4) * 4;
948 
949         long len = (pArray.length * 4) / 3;
950         long mod = len % 4;
951         if (mod != 0) {
952             len += 4 - mod;
953         }
954         if (chunkSize > 0) {
955             boolean lenChunksPerfectly = len % chunkSize == 0;
956             len += (len / chunkSize) * chunkSeparator.length;
957             if (!lenChunksPerfectly) {
958                 len += chunkSeparator.length;
959             }
960         }
961         return len;
962     }
963 
964     // Implementation of integer encoding used for crypto
965     /**
966      * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature
967      *
968      * @param pArray
969      *            a byte array containing base64 character data
970      * @return A BigInteger
971      * @since 1.4
972      */
973     public static BigInteger decodeInteger(byte[] pArray) {
974         return new BigInteger(1, decodeBase64(pArray));
975     }
976 
977     /**
978      * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature
979      *
980      * @param bigInt
981      *            a BigInteger
982      * @return A byte array containing base64 character data
983      * @throws NullPointerException
984      *             if null is passed in
985      * @since 1.4
986      */
987     public static byte[] encodeInteger(BigInteger bigInt) {
988         if (bigInt == null) {
989             throw new NullPointerException("encodeInteger called with null parameter");
990         }
991         return encodeBase64(toIntegerBytes(bigInt), false);
992     }
993 
994     /**
995      * Returns a byte-array representation of a <code>BigInteger</code> without sign bit.
996      *
997      * @param bigInt
998      *            <code>BigInteger</code> to be converted
999      * @return a byte array representation of the BigInteger parameter
1000      */
1001     static byte[] toIntegerBytes(BigInteger bigInt) {
1002         int bitlen = bigInt.bitLength();
1003         // round bitlen
1004         bitlen = ((bitlen + 7) >> 3) << 3;
1005         byte[] bigBytes = bigInt.toByteArray();
1006 
1007         if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
1008             return bigBytes;
1009         }
1010         // set up params for copying everything but sign bit
1011         int startSrc = 0;
1012         int len = bigBytes.length;
1013 
1014         // if bigInt is exactly byte-aligned, just skip signbit in copy
1015         if ((bigInt.bitLength() % 8) == 0) {
1016             startSrc = 1;
1017             len--;
1018         }
1019         int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
1020         byte[] resizedBytes = new byte[bitlen / 8];
1021         System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
1022         return resizedBytes;
1023     }
1024 
1025     /**
1026      * Resets this Base64 object to its initial newly constructed state.
1027      */
1028     private void reset() {
1029         buffer = null;
1030         pos = 0;
1031         readPos = 0;
1032         currentLinePos = 0;
1033         modulus = 0;
1034         eof = false;
1035     }
1036 
1037     // Getters for use in testing
1038 
1039     int getLineLength() {
1040         return lineLength;
1041     }
1042 
1043     byte[] getLineSeparator() {
1044         return lineSeparator.clone();
1045     }
1046 }