• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.util;
27 
28 import java.io.FilterOutputStream;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.charset.StandardCharsets;
34 
35 /**
36  * This class consists exclusively of static methods for obtaining
37  * encoders and decoders for the Base64 encoding scheme. The
38  * implementation of this class supports the following types of Base64
39  * as specified in
40  * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
41  * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
42  *
43  * <ul>
44  * <li><a name="basic"><b>Basic</b></a>
45  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
46  *     RFC 4648 and RFC 2045 for encoding and decoding operation.
47  *     The encoder does not add any line feed (line separator)
48  *     character. The decoder rejects data that contains characters
49  *     outside the base64 alphabet.</p></li>
50  *
51  * <li><a name="url"><b>URL and Filename safe</b></a>
52  * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
53  *     in Table 2 of RFC 4648 for encoding and decoding. The
54  *     encoder does not add any line feed (line separator) character.
55  *     The decoder rejects data that contains characters outside the
56  *     base64 alphabet.</p></li>
57  *
58  * <li><a name="mime"><b>MIME</b></a>
59  * <p> Uses the "The Base64 Alphabet" as specified in Table 1 of
60  *     RFC 2045 for encoding and decoding operation. The encoded output
61  *     must be represented in lines of no more than 76 characters each
62  *     and uses a carriage return {@code '\r'} followed immediately by
63  *     a linefeed {@code '\n'} as the line separator. No line separator
64  *     is added to the end of the encoded output. All line separators
65  *     or other characters not found in the base64 alphabet table are
66  *     ignored in decoding operation.</p></li>
67  * </ul>
68  *
69  * <p> Unless otherwise noted, passing a {@code null} argument to a
70  * method of this class will cause a {@link java.lang.NullPointerException
71  * NullPointerException} to be thrown.
72  *
73  * @author  Xueming Shen
74  * @since   1.8
75  */
76 
77 public class Base64 {
78 
Base64()79     private Base64() {}
80 
81     /**
82      * Returns a {@link Encoder} that encodes using the
83      * <a href="#basic">Basic</a> type base64 encoding scheme.
84      *
85      * @return  A Base64 encoder.
86      */
getEncoder()87     public static Encoder getEncoder() {
88          return Encoder.RFC4648;
89     }
90 
91     /**
92      * Returns a {@link Encoder} that encodes using the
93      * <a href="#url">URL and Filename safe</a> type base64
94      * encoding scheme.
95      *
96      * @return  A Base64 encoder.
97      */
getUrlEncoder()98     public static Encoder getUrlEncoder() {
99          return Encoder.RFC4648_URLSAFE;
100     }
101 
102     /**
103      * Returns a {@link Encoder} that encodes using the
104      * <a href="#mime">MIME</a> type base64 encoding scheme.
105      *
106      * @return  A Base64 encoder.
107      */
getMimeEncoder()108     public static Encoder getMimeEncoder() {
109         return Encoder.RFC2045;
110     }
111 
112     /**
113      * Returns a {@link Encoder} that encodes using the
114      * <a href="#mime">MIME</a> type base64 encoding scheme
115      * with specified line length and line separators.
116      *
117      * @param   lineLength
118      *          the length of each output line (rounded down to nearest multiple
119      *          of 4). If {@code lineLength <= 0} the output will not be separated
120      *          in lines
121      * @param   lineSeparator
122      *          the line separator for each output line
123      *
124      * @return  A Base64 encoder.
125      *
126      * @throws  IllegalArgumentException if {@code lineSeparator} includes any
127      *          character of "The Base64 Alphabet" as specified in Table 1 of
128      *          RFC 2045.
129      */
getMimeEncoder(int lineLength, byte[] lineSeparator)130     public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) {
131          Objects.requireNonNull(lineSeparator);
132          int[] base64 = Decoder.fromBase64;
133          for (byte b : lineSeparator) {
134              if (base64[b & 0xff] != -1)
135                  throw new IllegalArgumentException(
136                      "Illegal base64 line separator character 0x" + Integer.toString(b, 16));
137          }
138          if (lineLength <= 0) {
139              return Encoder.RFC4648;
140          }
141          return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true);
142     }
143 
144     /**
145      * Returns a {@link Decoder} that decodes using the
146      * <a href="#basic">Basic</a> type base64 encoding scheme.
147      *
148      * @return  A Base64 decoder.
149      */
getDecoder()150     public static Decoder getDecoder() {
151          return Decoder.RFC4648;
152     }
153 
154     /**
155      * Returns a {@link Decoder} that decodes using the
156      * <a href="#url">URL and Filename safe</a> type base64
157      * encoding scheme.
158      *
159      * @return  A Base64 decoder.
160      */
getUrlDecoder()161     public static Decoder getUrlDecoder() {
162          return Decoder.RFC4648_URLSAFE;
163     }
164 
165     /**
166      * Returns a {@link Decoder} that decodes using the
167      * <a href="#mime">MIME</a> type base64 decoding scheme.
168      *
169      * @return  A Base64 decoder.
170      */
getMimeDecoder()171     public static Decoder getMimeDecoder() {
172          return Decoder.RFC2045;
173     }
174 
175     /**
176      * This class implements an encoder for encoding byte data using
177      * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
178      *
179      * <p> Instances of {@link Encoder} class are safe for use by
180      * multiple concurrent threads.
181      *
182      * <p> Unless otherwise noted, passing a {@code null} argument to
183      * a method of this class will cause a
184      * {@link java.lang.NullPointerException NullPointerException} to
185      * be thrown.
186      *
187      * @see     Decoder
188      * @since   1.8
189      */
190     public static class Encoder {
191 
192         private final byte[] newline;
193         private final int linemax;
194         private final boolean isURL;
195         private final boolean doPadding;
196 
Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding)197         private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
198             this.isURL = isURL;
199             this.newline = newline;
200             this.linemax = linemax;
201             this.doPadding = doPadding;
202         }
203 
204         /**
205          * This array is a lookup table that translates 6-bit positive integer
206          * index values into their "Base64 Alphabet" equivalents as specified
207          * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
208          */
209         private static final char[] toBase64 = {
210             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
211             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
212             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
213             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
214             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
215         };
216 
217         /**
218          * It's the lookup table for "URL and Filename safe Base64" as specified
219          * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
220          * '_'. This table is used when BASE64_URL is specified.
221          */
222         private static final char[] toBase64URL = {
223             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
224             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
225             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
226             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
227             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
228         };
229 
230         private static final int MIMELINEMAX = 76;
231         private static final byte[] CRLF = new byte[] {'\r', '\n'};
232 
233         static final Encoder RFC4648 = new Encoder(false, null, -1, true);
234         static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
235         static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
236 
outLength(int srclen)237         private final int outLength(int srclen) {
238             int len = 0;
239             if (doPadding) {
240                 len = 4 * ((srclen + 2) / 3);
241             } else {
242                 int n = srclen % 3;
243                 len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
244             }
245             if (linemax > 0)                                  // line separators
246                 len += (len - 1) / linemax * newline.length;
247             return len;
248         }
249 
250         /**
251          * Encodes all bytes from the specified byte array into a newly-allocated
252          * byte array using the {@link Base64} encoding scheme. The returned byte
253          * array is of the length of the resulting bytes.
254          *
255          * @param   src
256          *          the byte array to encode
257          * @return  A newly-allocated byte array containing the resulting
258          *          encoded bytes.
259          */
encode(byte[] src)260         public byte[] encode(byte[] src) {
261             int len = outLength(src.length);          // dst array size
262             byte[] dst = new byte[len];
263             int ret = encode0(src, 0, src.length, dst);
264             if (ret != dst.length)
265                  return Arrays.copyOf(dst, ret);
266             return dst;
267         }
268 
269         /**
270          * Encodes all bytes from the specified byte array using the
271          * {@link Base64} encoding scheme, writing the resulting bytes to the
272          * given output byte array, starting at offset 0.
273          *
274          * <p> It is the responsibility of the invoker of this method to make
275          * sure the output byte array {@code dst} has enough space for encoding
276          * all bytes from the input byte array. No bytes will be written to the
277          * output byte array if the output byte array is not big enough.
278          *
279          * @param   src
280          *          the byte array to encode
281          * @param   dst
282          *          the output byte array
283          * @return  The number of bytes written to the output byte array
284          *
285          * @throws  IllegalArgumentException if {@code dst} does not have enough
286          *          space for encoding all input bytes.
287          */
encode(byte[] src, byte[] dst)288         public int encode(byte[] src, byte[] dst) {
289             int len = outLength(src.length);         // dst array size
290             if (dst.length < len)
291                 throw new IllegalArgumentException(
292                     "Output byte array is too small for encoding all input bytes");
293             return encode0(src, 0, src.length, dst);
294         }
295 
296         /**
297          * Encodes the specified byte array into a String using the {@link Base64}
298          * encoding scheme.
299          *
300          * <p> This method first encodes all input bytes into a base64 encoded
301          * byte array and then constructs a new String by using the encoded byte
302          * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
303          * ISO-8859-1} charset.
304          *
305          * <p> In other words, an invocation of this method has exactly the same
306          * effect as invoking
307          * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
308          *
309          * @param   src
310          *          the byte array to encode
311          * @return  A String containing the resulting Base64 encoded characters
312          */
313         @SuppressWarnings("deprecation")
encodeToString(byte[] src)314         public String encodeToString(byte[] src) {
315             byte[] encoded = encode(src);
316             return new String(encoded, 0, 0, encoded.length);
317         }
318 
319         /**
320          * Encodes all remaining bytes from the specified byte buffer into
321          * a newly-allocated ByteBuffer using the {@link Base64} encoding
322          * scheme.
323          *
324          * Upon return, the source buffer's position will be updated to
325          * its limit; its limit will not have been changed. The returned
326          * output buffer's position will be zero and its limit will be the
327          * number of resulting encoded bytes.
328          *
329          * @param   buffer
330          *          the source ByteBuffer to encode
331          * @return  A newly-allocated byte buffer containing the encoded bytes.
332          */
encode(ByteBuffer buffer)333         public ByteBuffer encode(ByteBuffer buffer) {
334             int len = outLength(buffer.remaining());
335             byte[] dst = new byte[len];
336             int ret = 0;
337             if (buffer.hasArray()) {
338                 ret = encode0(buffer.array(),
339                               buffer.arrayOffset() + buffer.position(),
340                               buffer.arrayOffset() + buffer.limit(),
341                               dst);
342                 buffer.position(buffer.limit());
343             } else {
344                 byte[] src = new byte[buffer.remaining()];
345                 buffer.get(src);
346                 ret = encode0(src, 0, src.length, dst);
347             }
348             if (ret != dst.length)
349                  dst = Arrays.copyOf(dst, ret);
350             return ByteBuffer.wrap(dst);
351         }
352 
353         /**
354          * Wraps an output stream for encoding byte data using the {@link Base64}
355          * encoding scheme.
356          *
357          * <p> It is recommended to promptly close the returned output stream after
358          * use, during which it will flush all possible leftover bytes to the underlying
359          * output stream. Closing the returned output stream will close the underlying
360          * output stream.
361          *
362          * @param   os
363          *          the output stream.
364          * @return  the output stream for encoding the byte data into the
365          *          specified Base64 encoded format
366          */
wrap(OutputStream os)367         public OutputStream wrap(OutputStream os) {
368             Objects.requireNonNull(os);
369             return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
370                                        newline, linemax, doPadding);
371         }
372 
373         /**
374          * Returns an encoder instance that encodes equivalently to this one,
375          * but without adding any padding character at the end of the encoded
376          * byte data.
377          *
378          * <p> The encoding scheme of this encoder instance is unaffected by
379          * this invocation. The returned encoder instance should be used for
380          * non-padding encoding operation.
381          *
382          * @return an equivalent encoder that encodes without adding any
383          *         padding character at the end
384          */
withoutPadding()385         public Encoder withoutPadding() {
386             if (!doPadding)
387                 return this;
388             return new Encoder(isURL, newline, linemax, false);
389         }
390 
encode0(byte[] src, int off, int end, byte[] dst)391         private int encode0(byte[] src, int off, int end, byte[] dst) {
392             char[] base64 = isURL ? toBase64URL : toBase64;
393             int sp = off;
394             int slen = (end - off) / 3 * 3;
395             int sl = off + slen;
396             if (linemax > 0 && slen  > linemax / 4 * 3)
397                 slen = linemax / 4 * 3;
398             int dp = 0;
399             while (sp < sl) {
400                 int sl0 = Math.min(sp + slen, sl);
401                 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
402                     int bits = (src[sp0++] & 0xff) << 16 |
403                                (src[sp0++] & 0xff) <<  8 |
404                                (src[sp0++] & 0xff);
405                     dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
406                     dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
407                     dst[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
408                     dst[dp0++] = (byte)base64[bits & 0x3f];
409                 }
410                 int dlen = (sl0 - sp) / 3 * 4;
411                 dp += dlen;
412                 sp = sl0;
413                 if (dlen == linemax && sp < end) {
414                     for (byte b : newline){
415                         dst[dp++] = b;
416                     }
417                 }
418             }
419             if (sp < end) {               // 1 or 2 leftover bytes
420                 int b0 = src[sp++] & 0xff;
421                 dst[dp++] = (byte)base64[b0 >> 2];
422                 if (sp == end) {
423                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
424                     if (doPadding) {
425                         dst[dp++] = '=';
426                         dst[dp++] = '=';
427                     }
428                 } else {
429                     int b1 = src[sp++] & 0xff;
430                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
431                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
432                     if (doPadding) {
433                         dst[dp++] = '=';
434                     }
435                 }
436             }
437             return dp;
438         }
439     }
440 
441     /**
442      * This class implements a decoder for decoding byte data using the
443      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
444      *
445      * <p> The Base64 padding character {@code '='} is accepted and
446      * interpreted as the end of the encoded byte data, but is not
447      * required. So if the final unit of the encoded byte data only has
448      * two or three Base64 characters (without the corresponding padding
449      * character(s) padded), they are decoded as if followed by padding
450      * character(s). If there is a padding character present in the
451      * final unit, the correct number of padding character(s) must be
452      * present, otherwise {@code IllegalArgumentException} (
453      * {@code IOException} when reading from a Base64 stream) is thrown
454      * during decoding.
455      *
456      * <p> Instances of {@link Decoder} class are safe for use by
457      * multiple concurrent threads.
458      *
459      * <p> Unless otherwise noted, passing a {@code null} argument to
460      * a method of this class will cause a
461      * {@link java.lang.NullPointerException NullPointerException} to
462      * be thrown.
463      *
464      * @see     Encoder
465      * @since   1.8
466      */
467     public static class Decoder {
468 
469         private final boolean isURL;
470         private final boolean isMIME;
471 
Decoder(boolean isURL, boolean isMIME)472         private Decoder(boolean isURL, boolean isMIME) {
473             this.isURL = isURL;
474             this.isMIME = isMIME;
475         }
476 
477         /**
478          * Lookup table for decoding unicode characters drawn from the
479          * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
480          * their 6-bit positive integer equivalents.  Characters that
481          * are not in the Base64 alphabet but fall within the bounds of
482          * the array are encoded to -1.
483          *
484          */
485         private static final int[] fromBase64 = new int[256];
486         static {
Arrays.fill(fromBase64, -1)487             Arrays.fill(fromBase64, -1);
488             for (int i = 0; i < Encoder.toBase64.length; i++)
489                 fromBase64[Encoder.toBase64[i]] = i;
490             fromBase64['='] = -2;
491         }
492 
493         /**
494          * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
495          * as specified in Table2 of the RFC 4648.
496          */
497         private static final int[] fromBase64URL = new int[256];
498 
499         static {
Arrays.fill(fromBase64URL, -1)500             Arrays.fill(fromBase64URL, -1);
501             for (int i = 0; i < Encoder.toBase64URL.length; i++)
502                 fromBase64URL[Encoder.toBase64URL[i]] = i;
503             fromBase64URL['='] = -2;
504         }
505 
506         static final Decoder RFC4648         = new Decoder(false, false);
507         static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
508         static final Decoder RFC2045         = new Decoder(false, true);
509 
510         /**
511          * Decodes all bytes from the input byte array using the {@link Base64}
512          * encoding scheme, writing the results into a newly-allocated output
513          * byte array. The returned byte array is of the length of the resulting
514          * bytes.
515          *
516          * @param   src
517          *          the byte array to decode
518          *
519          * @return  A newly-allocated byte array containing the decoded bytes.
520          *
521          * @throws  IllegalArgumentException
522          *          if {@code src} is not in valid Base64 scheme
523          */
decode(byte[] src)524         public byte[] decode(byte[] src) {
525             byte[] dst = new byte[outLength(src, 0, src.length)];
526             int ret = decode0(src, 0, src.length, dst);
527             if (ret != dst.length) {
528                 dst = Arrays.copyOf(dst, ret);
529             }
530             return dst;
531         }
532 
533         /**
534          * Decodes a Base64 encoded String into a newly-allocated byte array
535          * using the {@link Base64} encoding scheme.
536          *
537          * <p> An invocation of this method has exactly the same effect as invoking
538          * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
539          *
540          * @param   src
541          *          the string to decode
542          *
543          * @return  A newly-allocated byte array containing the decoded bytes.
544          *
545          * @throws  IllegalArgumentException
546          *          if {@code src} is not in valid Base64 scheme
547          */
decode(String src)548         public byte[] decode(String src) {
549             return decode(src.getBytes(StandardCharsets.ISO_8859_1));
550         }
551 
552         /**
553          * Decodes all bytes from the input byte array using the {@link Base64}
554          * encoding scheme, writing the results into the given output byte array,
555          * starting at offset 0.
556          *
557          * <p> It is the responsibility of the invoker of this method to make
558          * sure the output byte array {@code dst} has enough space for decoding
559          * all bytes from the input byte array. No bytes will be be written to
560          * the output byte array if the output byte array is not big enough.
561          *
562          * <p> If the input byte array is not in valid Base64 encoding scheme
563          * then some bytes may have been written to the output byte array before
564          * IllegalargumentException is thrown.
565          *
566          * @param   src
567          *          the byte array to decode
568          * @param   dst
569          *          the output byte array
570          *
571          * @return  The number of bytes written to the output byte array
572          *
573          * @throws  IllegalArgumentException
574          *          if {@code src} is not in valid Base64 scheme, or {@code dst}
575          *          does not have enough space for decoding all input bytes.
576          */
decode(byte[] src, byte[] dst)577         public int decode(byte[] src, byte[] dst) {
578             int len = outLength(src, 0, src.length);
579             if (dst.length < len)
580                 throw new IllegalArgumentException(
581                     "Output byte array is too small for decoding all input bytes");
582             return decode0(src, 0, src.length, dst);
583         }
584 
585         /**
586          * Decodes all bytes from the input byte buffer using the {@link Base64}
587          * encoding scheme, writing the results into a newly-allocated ByteBuffer.
588          *
589          * <p> Upon return, the source buffer's position will be updated to
590          * its limit; its limit will not have been changed. The returned
591          * output buffer's position will be zero and its limit will be the
592          * number of resulting decoded bytes
593          *
594          * <p> {@code IllegalArgumentException} is thrown if the input buffer
595          * is not in valid Base64 encoding scheme. The position of the input
596          * buffer will not be advanced in this case.
597          *
598          * @param   buffer
599          *          the ByteBuffer to decode
600          *
601          * @return  A newly-allocated byte buffer containing the decoded bytes
602          *
603          * @throws  IllegalArgumentException
604          *          if {@code src} is not in valid Base64 scheme.
605          */
decode(ByteBuffer buffer)606         public ByteBuffer decode(ByteBuffer buffer) {
607             int pos0 = buffer.position();
608             try {
609                 byte[] src;
610                 int sp, sl;
611                 if (buffer.hasArray()) {
612                     src = buffer.array();
613                     sp = buffer.arrayOffset() + buffer.position();
614                     sl = buffer.arrayOffset() + buffer.limit();
615                     buffer.position(buffer.limit());
616                 } else {
617                     src = new byte[buffer.remaining()];
618                     buffer.get(src);
619                     sp = 0;
620                     sl = src.length;
621                 }
622                 byte[] dst = new byte[outLength(src, sp, sl)];
623                 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
624             } catch (IllegalArgumentException iae) {
625                 buffer.position(pos0);
626                 throw iae;
627             }
628         }
629 
630         /**
631          * Returns an input stream for decoding {@link Base64} encoded byte stream.
632          *
633          * <p> The {@code read}  methods of the returned {@code InputStream} will
634          * throw {@code IOException} when reading bytes that cannot be decoded.
635          *
636          * <p> Closing the returned input stream will close the underlying
637          * input stream.
638          *
639          * @param   is
640          *          the input stream
641          *
642          * @return  the input stream for decoding the specified Base64 encoded
643          *          byte stream
644          */
wrap(InputStream is)645         public InputStream wrap(InputStream is) {
646             Objects.requireNonNull(is);
647             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
648         }
649 
outLength(byte[] src, int sp, int sl)650         private int outLength(byte[] src, int sp, int sl) {
651             int[] base64 = isURL ? fromBase64URL : fromBase64;
652             int paddings = 0;
653             int len = sl - sp;
654             if (len == 0)
655                 return 0;
656             if (len < 2) {
657                 if (isMIME && base64[0] == -1)
658                     return 0;
659                 throw new IllegalArgumentException(
660                     "Input byte[] should at least have 2 bytes for base64 bytes");
661             }
662             if (isMIME) {
663                 // scan all bytes to fill out all non-alphabet. a performance
664                 // trade-off of pre-scan or Arrays.copyOf
665                 int n = 0;
666                 while (sp < sl) {
667                     int b = src[sp++] & 0xff;
668                     if (b == '=') {
669                         len -= (sl - sp + 1);
670                         break;
671                     }
672                     if ((b = base64[b]) == -1)
673                         n++;
674                 }
675                 len -= n;
676             } else {
677                 if (src[sl - 1] == '=') {
678                     paddings++;
679                     if (src[sl - 2] == '=')
680                         paddings++;
681                 }
682             }
683             if (paddings == 0 && (len & 0x3) !=  0)
684                 paddings = 4 - (len & 0x3);
685             return 3 * ((len + 3) / 4) - paddings;
686         }
687 
decode0(byte[] src, int sp, int sl, byte[] dst)688         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
689             int[] base64 = isURL ? fromBase64URL : fromBase64;
690             int dp = 0;
691             int bits = 0;
692             int shiftto = 18;       // pos of first byte of 4-byte atom
693             while (sp < sl) {
694                 int b = src[sp++] & 0xff;
695                 if ((b = base64[b]) < 0) {
696                     if (b == -2) {         // padding byte '='
697                         // =     shiftto==18 unnecessary padding
698                         // x=    shiftto==12 a dangling single x
699                         // x     to be handled together with non-padding case
700                         // xx=   shiftto==6&&sp==sl missing last =
701                         // xx=y  shiftto==6 last is not =
702                         if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
703                             shiftto == 18) {
704                             throw new IllegalArgumentException(
705                                 "Input byte array has wrong 4-byte ending unit");
706                         }
707                         break;
708                     }
709                     if (isMIME)    // skip if for rfc2045
710                         continue;
711                     else
712                         throw new IllegalArgumentException(
713                             "Illegal base64 character " +
714                             Integer.toString(src[sp - 1], 16));
715                 }
716                 bits |= (b << shiftto);
717                 shiftto -= 6;
718                 if (shiftto < 0) {
719                     dst[dp++] = (byte)(bits >> 16);
720                     dst[dp++] = (byte)(bits >>  8);
721                     dst[dp++] = (byte)(bits);
722                     shiftto = 18;
723                     bits = 0;
724                 }
725             }
726             // reached end of byte array or hit padding '=' characters.
727             if (shiftto == 6) {
728                 dst[dp++] = (byte)(bits >> 16);
729             } else if (shiftto == 0) {
730                 dst[dp++] = (byte)(bits >> 16);
731                 dst[dp++] = (byte)(bits >>  8);
732             } else if (shiftto == 12) {
733                 // dangling single "x", incorrectly encoded.
734                 throw new IllegalArgumentException(
735                     "Last unit does not have enough valid bits");
736             }
737             // anything left is invalid, if is not MIME.
738             // if MIME, ignore all non-base64 character
739             while (sp < sl) {
740                 if (isMIME && base64[src[sp++]] < 0)
741                     continue;
742                 throw new IllegalArgumentException(
743                     "Input byte array has incorrect ending byte at " + sp);
744             }
745             return dp;
746         }
747     }
748 
749     /*
750      * An output stream for encoding bytes into the Base64.
751      */
752     private static class EncOutputStream extends FilterOutputStream {
753 
754         private int leftover = 0;
755         private int b0, b1, b2;
756         private boolean closed = false;
757 
758         private final char[] base64;    // byte->base64 mapping
759         private final byte[] newline;   // line separator, if needed
760         private final int linemax;
761         private final boolean doPadding;// whether or not to pad
762         private int linepos = 0;
763 
EncOutputStream(OutputStream os, char[] base64, byte[] newline, int linemax, boolean doPadding)764         EncOutputStream(OutputStream os, char[] base64,
765                         byte[] newline, int linemax, boolean doPadding) {
766             super(os);
767             this.base64 = base64;
768             this.newline = newline;
769             this.linemax = linemax;
770             this.doPadding = doPadding;
771         }
772 
773         @Override
write(int b)774         public void write(int b) throws IOException {
775             byte[] buf = new byte[1];
776             buf[0] = (byte)(b & 0xff);
777             write(buf, 0, 1);
778         }
779 
checkNewline()780         private void checkNewline() throws IOException {
781             if (linepos == linemax) {
782                 out.write(newline);
783                 linepos = 0;
784             }
785         }
786 
787         @Override
write(byte[] b, int off, int len)788         public void write(byte[] b, int off, int len) throws IOException {
789             if (closed)
790                 throw new IOException("Stream is closed");
791             // Android-changed: Upstream fix to avoid overflow.
792             // This upstream fix is from beyond OpenJDK8u121-b13. http://b/62368386
793             // if (off < 0 || len < 0 || off + len > b.length)
794             if (off < 0 || len < 0 || len > b.length - off)
795                 throw new ArrayIndexOutOfBoundsException();
796             if (len == 0)
797                 return;
798             if (leftover != 0) {
799                 if (leftover == 1) {
800                     b1 = b[off++] & 0xff;
801                     len--;
802                     if (len == 0) {
803                         leftover++;
804                         return;
805                     }
806                 }
807                 b2 = b[off++] & 0xff;
808                 len--;
809                 checkNewline();
810                 out.write(base64[b0 >> 2]);
811                 out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
812                 out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);
813                 out.write(base64[b2 & 0x3f]);
814                 linepos += 4;
815             }
816             int nBits24 = len / 3;
817             leftover = len - (nBits24 * 3);
818             while (nBits24-- > 0) {
819                 checkNewline();
820                 int bits = (b[off++] & 0xff) << 16 |
821                            (b[off++] & 0xff) <<  8 |
822                            (b[off++] & 0xff);
823                 out.write(base64[(bits >>> 18) & 0x3f]);
824                 out.write(base64[(bits >>> 12) & 0x3f]);
825                 out.write(base64[(bits >>> 6)  & 0x3f]);
826                 out.write(base64[bits & 0x3f]);
827                 linepos += 4;
828            }
829             if (leftover == 1) {
830                 b0 = b[off++] & 0xff;
831             } else if (leftover == 2) {
832                 b0 = b[off++] & 0xff;
833                 b1 = b[off++] & 0xff;
834             }
835         }
836 
837         @Override
close()838         public void close() throws IOException {
839             if (!closed) {
840                 closed = true;
841                 if (leftover == 1) {
842                     checkNewline();
843                     out.write(base64[b0 >> 2]);
844                     out.write(base64[(b0 << 4) & 0x3f]);
845                     if (doPadding) {
846                         out.write('=');
847                         out.write('=');
848                     }
849                 } else if (leftover == 2) {
850                     checkNewline();
851                     out.write(base64[b0 >> 2]);
852                     out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
853                     out.write(base64[(b1 << 2) & 0x3f]);
854                     if (doPadding) {
855                        out.write('=');
856                     }
857                 }
858                 leftover = 0;
859                 out.close();
860             }
861         }
862     }
863 
864     /*
865      * An input stream for decoding Base64 bytes
866      */
867     private static class DecInputStream extends InputStream {
868 
869         private final InputStream is;
870         private final boolean isMIME;
871         private final int[] base64;      // base64 -> byte mapping
872         private int bits = 0;            // 24-bit buffer for decoding
873         private int nextin = 18;         // next available "off" in "bits" for input;
874                                          // -> 18, 12, 6, 0
875         private int nextout = -8;        // next available "off" in "bits" for output;
876                                          // -> 8, 0, -8 (no byte for output)
877         private boolean eof = false;
878         private boolean closed = false;
879 
DecInputStream(InputStream is, int[] base64, boolean isMIME)880         DecInputStream(InputStream is, int[] base64, boolean isMIME) {
881             this.is = is;
882             this.base64 = base64;
883             this.isMIME = isMIME;
884         }
885 
886         private byte[] sbBuf = new byte[1];
887 
888         @Override
read()889         public int read() throws IOException {
890             return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
891         }
892 
893         @Override
read(byte[] b, int off, int len)894         public int read(byte[] b, int off, int len) throws IOException {
895             if (closed)
896                 throw new IOException("Stream is closed");
897             if (eof && nextout < 0)    // eof and no leftover
898                 return -1;
899             if (off < 0 || len < 0 || len > b.length - off)
900                 throw new IndexOutOfBoundsException();
901             int oldOff = off;
902             if (nextout >= 0) {       // leftover output byte(s) in bits buf
903                 do {
904                     if (len == 0)
905                         return off - oldOff;
906                     b[off++] = (byte)(bits >> nextout);
907                     len--;
908                     nextout -= 8;
909                 } while (nextout >= 0);
910                 bits = 0;
911             }
912             while (len > 0) {
913                 int v = is.read();
914                 if (v == -1) {
915                     eof = true;
916                     if (nextin != 18) {
917                         if (nextin == 12)
918                             throw new IOException("Base64 stream has one un-decoded dangling byte.");
919                         // treat ending xx/xxx without padding character legal.
920                         // same logic as v == '=' below
921                         b[off++] = (byte)(bits >> (16));
922                         len--;
923                         if (nextin == 0) {           // only one padding byte
924                             if (len == 0) {          // no enough output space
925                                 bits >>= 8;          // shift to lowest byte
926                                 nextout = 0;
927                             } else {
928                                 b[off++] = (byte) (bits >>  8);
929                             }
930                         }
931                     }
932                     if (off == oldOff)
933                         return -1;
934                     else
935                         return off - oldOff;
936                 }
937                 if (v == '=') {                  // padding byte(s)
938                     // =     shiftto==18 unnecessary padding
939                     // x=    shiftto==12 dangling x, invalid unit
940                     // xx=   shiftto==6 && missing last '='
941                     // xx=y  or last is not '='
942                     if (nextin == 18 || nextin == 12 ||
943                         nextin == 6 && is.read() != '=') {
944                         throw new IOException("Illegal base64 ending sequence:" + nextin);
945                     }
946                     b[off++] = (byte)(bits >> (16));
947                     len--;
948                     if (nextin == 0) {           // only one padding byte
949                         if (len == 0) {          // no enough output space
950                             bits >>= 8;          // shift to lowest byte
951                             nextout = 0;
952                         } else {
953                             b[off++] = (byte) (bits >>  8);
954                         }
955                     }
956                     eof = true;
957                     break;
958                 }
959                 if ((v = base64[v]) == -1) {
960                     if (isMIME)                 // skip if for rfc2045
961                         continue;
962                     else
963                         throw new IOException("Illegal base64 character " +
964                             Integer.toString(v, 16));
965                 }
966                 bits |= (v << nextin);
967                 if (nextin == 0) {
968                     nextin = 18;    // clear for next
969                     nextout = 16;
970                     while (nextout >= 0) {
971                         b[off++] = (byte)(bits >> nextout);
972                         len--;
973                         nextout -= 8;
974                         if (len == 0 && nextout >= 0) {  // don't clean "bits"
975                             return off - oldOff;
976                         }
977                     }
978                     bits = 0;
979                 } else {
980                     nextin -= 6;
981                 }
982             }
983             return off - oldOff;
984         }
985 
986         @Override
available()987         public int available() throws IOException {
988             if (closed)
989                 throw new IOException("Stream is closed");
990             return is.available();   // TBD:
991         }
992 
993         @Override
close()994         public void close() throws IOException {
995             if (!closed) {
996                 closed = true;
997                 is.close();
998             }
999         }
1000     }
1001 }
1002