1 /* 2 * Copyright (c) 2012, 2020, 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 34 import sun.nio.cs.ISO_8859_1; 35 36 import jdk.internal.vm.annotation.IntrinsicCandidate; 37 38 /** 39 * This class consists exclusively of static methods for obtaining 40 * encoders and decoders for the Base64 encoding scheme. The 41 * implementation of this class supports the following types of Base64 42 * as specified in 43 * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and 44 * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. 45 * 46 * <ul> 47 * <li><a id="basic"><b>Basic</b></a> 48 * <p> Uses "The Base64 Alphabet" as specified in Table 1 of 49 * RFC 4648 and RFC 2045 for encoding and decoding operation. 50 * The encoder does not add any line feed (line separator) 51 * character. The decoder rejects data that contains characters 52 * outside the base64 alphabet.</p></li> 53 * 54 * <li><a id="url"><b>URL and Filename safe</b></a> 55 * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified 56 * in Table 2 of RFC 4648 for encoding and decoding. The 57 * encoder does not add any line feed (line separator) character. 58 * The decoder rejects data that contains characters outside the 59 * base64 alphabet.</p></li> 60 * 61 * <li><a id="mime"><b>MIME</b></a> 62 * <p> Uses "The Base64 Alphabet" as specified in Table 1 of 63 * RFC 2045 for encoding and decoding operation. The encoded output 64 * must be represented in lines of no more than 76 characters each 65 * and uses a carriage return {@code '\r'} followed immediately by 66 * a linefeed {@code '\n'} as the line separator. No line separator 67 * is added to the end of the encoded output. All line separators 68 * or other characters not found in the base64 alphabet table are 69 * ignored in decoding operation.</p></li> 70 * </ul> 71 * 72 * <p> Unless otherwise noted, passing a {@code null} argument to a 73 * method of this class will cause a {@link java.lang.NullPointerException 74 * NullPointerException} to be thrown. 75 * 76 * @author Xueming Shen 77 * @since 1.8 78 */ 79 80 public class Base64 { 81 Base64()82 private Base64() {} 83 84 /** 85 * Returns a {@link Encoder} that encodes using the 86 * <a href="#basic">Basic</a> type base64 encoding scheme. 87 * 88 * @return A Base64 encoder. 89 */ getEncoder()90 public static Encoder getEncoder() { 91 return Encoder.RFC4648; 92 } 93 94 /** 95 * Returns a {@link Encoder} that encodes using the 96 * <a href="#url">URL and Filename safe</a> type base64 97 * encoding scheme. 98 * 99 * @return A Base64 encoder. 100 */ getUrlEncoder()101 public static Encoder getUrlEncoder() { 102 return Encoder.RFC4648_URLSAFE; 103 } 104 105 /** 106 * Returns a {@link Encoder} that encodes using the 107 * <a href="#mime">MIME</a> type base64 encoding scheme. 108 * 109 * @return A Base64 encoder. 110 */ getMimeEncoder()111 public static Encoder getMimeEncoder() { 112 return Encoder.RFC2045; 113 } 114 115 /** 116 * Returns a {@link Encoder} that encodes using the 117 * <a href="#mime">MIME</a> type base64 encoding scheme 118 * with specified line length and line separators. 119 * 120 * @param lineLength 121 * the length of each output line (rounded down to nearest multiple 122 * of 4). If the rounded down line length is not a positive value, 123 * the output will not be separated in lines 124 * @param lineSeparator 125 * the line separator for each output line 126 * 127 * @return A Base64 encoder. 128 * 129 * @throws IllegalArgumentException if {@code lineSeparator} includes any 130 * character of "The Base64 Alphabet" as specified in Table 1 of 131 * RFC 2045. 132 */ getMimeEncoder(int lineLength, byte[] lineSeparator)133 public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) { 134 Objects.requireNonNull(lineSeparator); 135 int[] base64 = Decoder.fromBase64; 136 for (byte b : lineSeparator) { 137 if (base64[b & 0xff] != -1) 138 throw new IllegalArgumentException( 139 "Illegal base64 line separator character 0x" + Integer.toString(b, 16)); 140 } 141 // round down to nearest multiple of 4 142 lineLength &= ~0b11; 143 if (lineLength <= 0) { 144 return Encoder.RFC4648; 145 } 146 return new Encoder(false, lineSeparator, lineLength, true); 147 } 148 149 /** 150 * Returns a {@link Decoder} that decodes using the 151 * <a href="#basic">Basic</a> type base64 encoding scheme. 152 * 153 * @return A Base64 decoder. 154 */ getDecoder()155 public static Decoder getDecoder() { 156 return Decoder.RFC4648; 157 } 158 159 /** 160 * Returns a {@link Decoder} that decodes using the 161 * <a href="#url">URL and Filename safe</a> type base64 162 * encoding scheme. 163 * 164 * @return A Base64 decoder. 165 */ getUrlDecoder()166 public static Decoder getUrlDecoder() { 167 return Decoder.RFC4648_URLSAFE; 168 } 169 170 /** 171 * Returns a {@link Decoder} that decodes using the 172 * <a href="#mime">MIME</a> type base64 decoding scheme. 173 * 174 * @return A Base64 decoder. 175 */ getMimeDecoder()176 public static Decoder getMimeDecoder() { 177 return Decoder.RFC2045; 178 } 179 180 /** 181 * This class implements an encoder for encoding byte data using 182 * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 183 * 184 * <p> Instances of {@link Encoder} class are safe for use by 185 * multiple concurrent threads. 186 * 187 * <p> Unless otherwise noted, passing a {@code null} argument to 188 * a method of this class will cause a 189 * {@link java.lang.NullPointerException NullPointerException} to 190 * be thrown. 191 * <p> If the encoded byte output of the needed size can not 192 * be allocated, the encode methods of this class will 193 * cause an {@link java.lang.OutOfMemoryError OutOfMemoryError} 194 * to be thrown. 195 * 196 * @see Decoder 197 * @since 1.8 198 */ 199 public static class Encoder { 200 201 private final byte[] newline; 202 private final int linemax; 203 private final boolean isURL; 204 private final boolean doPadding; 205 Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding)206 private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) { 207 this.isURL = isURL; 208 this.newline = newline; 209 this.linemax = linemax; 210 this.doPadding = doPadding; 211 } 212 213 /** 214 * This array is a lookup table that translates 6-bit positive integer 215 * index values into their "Base64 Alphabet" equivalents as specified 216 * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648). 217 */ 218 private static final char[] toBase64 = { 219 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 220 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 221 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 222 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 223 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 224 }; 225 226 /** 227 * It's the lookup table for "URL and Filename safe Base64" as specified 228 * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and 229 * '_'. This table is used when BASE64_URL is specified. 230 */ 231 private static final char[] toBase64URL = { 232 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 233 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 234 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 235 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 236 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' 237 }; 238 239 private static final int MIMELINEMAX = 76; 240 private static final byte[] CRLF = new byte[] {'\r', '\n'}; 241 242 static final Encoder RFC4648 = new Encoder(false, null, -1, true); 243 static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true); 244 static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true); 245 246 /** 247 * Calculates the length of the encoded output bytes. 248 * 249 * @param srclen length of the bytes to encode 250 * @param throwOOME if true, throws OutOfMemoryError if the length of 251 * the encoded bytes overflows; else returns the 252 * length 253 * @return length of the encoded bytes, or -1 if the length overflows 254 * 255 */ encodedOutLength(int srclen, boolean throwOOME)256 private final int encodedOutLength(int srclen, boolean throwOOME) { 257 int len = 0; 258 try { 259 if (doPadding) { 260 len = Math.multiplyExact(4, (Math.addExact(srclen, 2) / 3)); 261 } else { 262 int n = srclen % 3; 263 len = Math.addExact(Math.multiplyExact(4, (srclen / 3)), (n == 0 ? 0 : n + 1)); 264 } 265 if (linemax > 0) { // line separators 266 len = Math.addExact(len, (len - 1) / linemax * newline.length); 267 } 268 } catch (ArithmeticException ex) { 269 if (throwOOME) { 270 throw new OutOfMemoryError("Encoded size is too large"); 271 } else { 272 // let the caller know that encoded bytes length 273 // is too large 274 len = -1; 275 } 276 } 277 return len; 278 } 279 280 /** 281 * Encodes all bytes from the specified byte array into a newly-allocated 282 * byte array using the {@link Base64} encoding scheme. The returned byte 283 * array is of the length of the resulting bytes. 284 * 285 * @param src 286 * the byte array to encode 287 * @return A newly-allocated byte array containing the resulting 288 * encoded bytes. 289 */ encode(byte[] src)290 public byte[] encode(byte[] src) { 291 int len = encodedOutLength(src.length, true); // dst array size 292 byte[] dst = new byte[len]; 293 int ret = encode0(src, 0, src.length, dst); 294 if (ret != dst.length) 295 return Arrays.copyOf(dst, ret); 296 return dst; 297 } 298 299 /** 300 * Encodes all bytes from the specified byte array using the 301 * {@link Base64} encoding scheme, writing the resulting bytes to the 302 * given output byte array, starting at offset 0. 303 * 304 * <p> It is the responsibility of the invoker of this method to make 305 * sure the output byte array {@code dst} has enough space for encoding 306 * all bytes from the input byte array. No bytes will be written to the 307 * output byte array if the output byte array is not big enough. 308 * 309 * @param src 310 * the byte array to encode 311 * @param dst 312 * the output byte array 313 * @return The number of bytes written to the output byte array 314 * 315 * @throws IllegalArgumentException if {@code dst} does not have enough 316 * space for encoding all input bytes. 317 */ encode(byte[] src, byte[] dst)318 public int encode(byte[] src, byte[] dst) { 319 int len = encodedOutLength(src.length, false); // dst array size 320 if (dst.length < len || len == -1) 321 throw new IllegalArgumentException( 322 "Output byte array is too small for encoding all input bytes"); 323 return encode0(src, 0, src.length, dst); 324 } 325 326 /** 327 * Encodes the specified byte array into a String using the {@link Base64} 328 * encoding scheme. 329 * 330 * <p> This method first encodes all input bytes into a base64 encoded 331 * byte array and then constructs a new String by using the encoded byte 332 * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1 333 * ISO-8859-1} charset. 334 * 335 * <p> In other words, an invocation of this method has exactly the same 336 * effect as invoking 337 * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}. 338 * 339 * @param src 340 * the byte array to encode 341 * @return A String containing the resulting Base64 encoded characters 342 */ 343 @SuppressWarnings("deprecation") encodeToString(byte[] src)344 public String encodeToString(byte[] src) { 345 byte[] encoded = encode(src); 346 return new String(encoded, 0, 0, encoded.length); 347 } 348 349 /** 350 * Encodes all remaining bytes from the specified byte buffer into 351 * a newly-allocated ByteBuffer using the {@link Base64} encoding 352 * scheme. 353 * 354 * Upon return, the source buffer's position will be updated to 355 * its limit; its limit will not have been changed. The returned 356 * output buffer's position will be zero and its limit will be the 357 * number of resulting encoded bytes. 358 * 359 * @param buffer 360 * the source ByteBuffer to encode 361 * @return A newly-allocated byte buffer containing the encoded bytes. 362 */ encode(ByteBuffer buffer)363 public ByteBuffer encode(ByteBuffer buffer) { 364 int len = encodedOutLength(buffer.remaining(), true); 365 byte[] dst = new byte[len]; 366 int ret = 0; 367 if (buffer.hasArray()) { 368 ret = encode0(buffer.array(), 369 buffer.arrayOffset() + buffer.position(), 370 buffer.arrayOffset() + buffer.limit(), 371 dst); 372 buffer.position(buffer.limit()); 373 } else { 374 byte[] src = new byte[buffer.remaining()]; 375 buffer.get(src); 376 ret = encode0(src, 0, src.length, dst); 377 } 378 if (ret != dst.length) 379 dst = Arrays.copyOf(dst, ret); 380 return ByteBuffer.wrap(dst); 381 } 382 383 /** 384 * Wraps an output stream for encoding byte data using the {@link Base64} 385 * encoding scheme. 386 * 387 * <p> It is recommended to promptly close the returned output stream after 388 * use, during which it will flush all possible leftover bytes to the underlying 389 * output stream. Closing the returned output stream will close the underlying 390 * output stream. 391 * 392 * @param os 393 * the output stream. 394 * @return the output stream for encoding the byte data into the 395 * specified Base64 encoded format 396 */ wrap(OutputStream os)397 public OutputStream wrap(OutputStream os) { 398 Objects.requireNonNull(os); 399 return new EncOutputStream(os, isURL ? toBase64URL : toBase64, 400 newline, linemax, doPadding); 401 } 402 403 /** 404 * Returns an encoder instance that encodes equivalently to this one, 405 * but without adding any padding character at the end of the encoded 406 * byte data. 407 * 408 * <p> The encoding scheme of this encoder instance is unaffected by 409 * this invocation. The returned encoder instance should be used for 410 * non-padding encoding operation. 411 * 412 * @return an equivalent encoder that encodes without adding any 413 * padding character at the end 414 */ withoutPadding()415 public Encoder withoutPadding() { 416 if (!doPadding) 417 return this; 418 return new Encoder(isURL, newline, linemax, false); 419 } 420 421 @IntrinsicCandidate encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL)422 private void encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) { 423 char[] base64 = isURL ? toBase64URL : toBase64; 424 for (int sp0 = sp, dp0 = dp ; sp0 < sl; ) { 425 int bits = (src[sp0++] & 0xff) << 16 | 426 (src[sp0++] & 0xff) << 8 | 427 (src[sp0++] & 0xff); 428 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f]; 429 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f]; 430 dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f]; 431 dst[dp0++] = (byte)base64[bits & 0x3f]; 432 } 433 } 434 encode0(byte[] src, int off, int end, byte[] dst)435 private int encode0(byte[] src, int off, int end, byte[] dst) { 436 char[] base64 = isURL ? toBase64URL : toBase64; 437 int sp = off; 438 int slen = (end - off) / 3 * 3; 439 int sl = off + slen; 440 if (linemax > 0 && slen > linemax / 4 * 3) 441 slen = linemax / 4 * 3; 442 int dp = 0; 443 while (sp < sl) { 444 int sl0 = Math.min(sp + slen, sl); 445 encodeBlock(src, sp, sl0, dst, dp, isURL); 446 int dlen = (sl0 - sp) / 3 * 4; 447 dp += dlen; 448 sp = sl0; 449 if (dlen == linemax && sp < end) { 450 for (byte b : newline){ 451 dst[dp++] = b; 452 } 453 } 454 } 455 if (sp < end) { // 1 or 2 leftover bytes 456 int b0 = src[sp++] & 0xff; 457 dst[dp++] = (byte)base64[b0 >> 2]; 458 if (sp == end) { 459 dst[dp++] = (byte)base64[(b0 << 4) & 0x3f]; 460 if (doPadding) { 461 dst[dp++] = '='; 462 dst[dp++] = '='; 463 } 464 } else { 465 int b1 = src[sp++] & 0xff; 466 dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]; 467 dst[dp++] = (byte)base64[(b1 << 2) & 0x3f]; 468 if (doPadding) { 469 dst[dp++] = '='; 470 } 471 } 472 } 473 return dp; 474 } 475 } 476 477 /** 478 * This class implements a decoder for decoding byte data using the 479 * Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 480 * 481 * <p> The Base64 padding character {@code '='} is accepted and 482 * interpreted as the end of the encoded byte data, but is not 483 * required. So if the final unit of the encoded byte data only has 484 * two or three Base64 characters (without the corresponding padding 485 * character(s) padded), they are decoded as if followed by padding 486 * character(s). If there is a padding character present in the 487 * final unit, the correct number of padding character(s) must be 488 * present, otherwise {@code IllegalArgumentException} ( 489 * {@code IOException} when reading from a Base64 stream) is thrown 490 * during decoding. 491 * 492 * <p> Instances of {@link Decoder} class are safe for use by 493 * multiple concurrent threads. 494 * 495 * <p> Unless otherwise noted, passing a {@code null} argument to 496 * a method of this class will cause a 497 * {@link java.lang.NullPointerException NullPointerException} to 498 * be thrown. 499 * <p> If the decoded byte output of the needed size can not 500 * be allocated, the decode methods of this class will 501 * cause an {@link java.lang.OutOfMemoryError OutOfMemoryError} 502 * to be thrown. 503 * 504 * @see Encoder 505 * @since 1.8 506 */ 507 public static class Decoder { 508 509 private final boolean isURL; 510 private final boolean isMIME; 511 Decoder(boolean isURL, boolean isMIME)512 private Decoder(boolean isURL, boolean isMIME) { 513 this.isURL = isURL; 514 this.isMIME = isMIME; 515 } 516 517 /** 518 * Lookup table for decoding unicode characters drawn from the 519 * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into 520 * their 6-bit positive integer equivalents. Characters that 521 * are not in the Base64 alphabet but fall within the bounds of 522 * the array are encoded to -1. 523 * 524 */ 525 private static final int[] fromBase64 = new int[256]; 526 static { Arrays.fill(fromBase64, -1)527 Arrays.fill(fromBase64, -1); 528 for (int i = 0; i < Encoder.toBase64.length; i++) 529 fromBase64[Encoder.toBase64[i]] = i; 530 fromBase64['='] = -2; 531 } 532 533 /** 534 * Lookup table for decoding "URL and Filename safe Base64 Alphabet" 535 * as specified in Table2 of the RFC 4648. 536 */ 537 private static final int[] fromBase64URL = new int[256]; 538 539 static { Arrays.fill(fromBase64URL, -1)540 Arrays.fill(fromBase64URL, -1); 541 for (int i = 0; i < Encoder.toBase64URL.length; i++) 542 fromBase64URL[Encoder.toBase64URL[i]] = i; 543 fromBase64URL['='] = -2; 544 } 545 546 static final Decoder RFC4648 = new Decoder(false, false); 547 static final Decoder RFC4648_URLSAFE = new Decoder(true, false); 548 static final Decoder RFC2045 = new Decoder(false, true); 549 550 /** 551 * Decodes all bytes from the input byte array using the {@link Base64} 552 * encoding scheme, writing the results into a newly-allocated output 553 * byte array. The returned byte array is of the length of the resulting 554 * bytes. 555 * 556 * @param src 557 * the byte array to decode 558 * 559 * @return A newly-allocated byte array containing the decoded bytes. 560 * 561 * @throws IllegalArgumentException 562 * if {@code src} is not in valid Base64 scheme 563 */ decode(byte[] src)564 public byte[] decode(byte[] src) { 565 byte[] dst = new byte[decodedOutLength(src, 0, src.length)]; 566 int ret = decode0(src, 0, src.length, dst); 567 if (ret != dst.length) { 568 dst = Arrays.copyOf(dst, ret); 569 } 570 return dst; 571 } 572 573 /** 574 * Decodes a Base64 encoded String into a newly-allocated byte array 575 * using the {@link Base64} encoding scheme. 576 * 577 * <p> An invocation of this method has exactly the same effect as invoking 578 * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))} 579 * 580 * @param src 581 * the string to decode 582 * 583 * @return A newly-allocated byte array containing the decoded bytes. 584 * 585 * @throws IllegalArgumentException 586 * if {@code src} is not in valid Base64 scheme 587 */ decode(String src)588 public byte[] decode(String src) { 589 return decode(src.getBytes(ISO_8859_1.INSTANCE)); 590 } 591 592 /** 593 * Decodes all bytes from the input byte array using the {@link Base64} 594 * encoding scheme, writing the results into the given output byte array, 595 * starting at offset 0. 596 * 597 * <p> It is the responsibility of the invoker of this method to make 598 * sure the output byte array {@code dst} has enough space for decoding 599 * all bytes from the input byte array. No bytes will be written to 600 * the output byte array if the output byte array is not big enough. 601 * 602 * <p> If the input byte array is not in valid Base64 encoding scheme 603 * then some bytes may have been written to the output byte array before 604 * IllegalargumentException is thrown. 605 * 606 * @param src 607 * the byte array to decode 608 * @param dst 609 * the output byte array 610 * 611 * @return The number of bytes written to the output byte array 612 * 613 * @throws IllegalArgumentException 614 * if {@code src} is not in valid Base64 scheme, or {@code dst} 615 * does not have enough space for decoding all input bytes. 616 */ decode(byte[] src, byte[] dst)617 public int decode(byte[] src, byte[] dst) { 618 int len = decodedOutLength(src, 0, src.length); 619 if (dst.length < len || len == -1) 620 throw new IllegalArgumentException( 621 "Output byte array is too small for decoding all input bytes"); 622 return decode0(src, 0, src.length, dst); 623 } 624 625 /** 626 * Decodes all bytes from the input byte buffer using the {@link Base64} 627 * encoding scheme, writing the results into a newly-allocated ByteBuffer. 628 * 629 * <p> Upon return, the source buffer's position will be updated to 630 * its limit; its limit will not have been changed. The returned 631 * output buffer's position will be zero and its limit will be the 632 * number of resulting decoded bytes 633 * 634 * <p> {@code IllegalArgumentException} is thrown if the input buffer 635 * is not in valid Base64 encoding scheme. The position of the input 636 * buffer will not be advanced in this case. 637 * 638 * @param buffer 639 * the ByteBuffer to decode 640 * 641 * @return A newly-allocated byte buffer containing the decoded bytes 642 * 643 * @throws IllegalArgumentException 644 * if {@code buffer} is not in valid Base64 scheme 645 */ decode(ByteBuffer buffer)646 public ByteBuffer decode(ByteBuffer buffer) { 647 int pos0 = buffer.position(); 648 try { 649 byte[] src; 650 int sp, sl; 651 if (buffer.hasArray()) { 652 src = buffer.array(); 653 sp = buffer.arrayOffset() + buffer.position(); 654 sl = buffer.arrayOffset() + buffer.limit(); 655 buffer.position(buffer.limit()); 656 } else { 657 src = new byte[buffer.remaining()]; 658 buffer.get(src); 659 sp = 0; 660 sl = src.length; 661 } 662 byte[] dst = new byte[decodedOutLength(src, sp, sl)]; 663 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); 664 } catch (IllegalArgumentException iae) { 665 buffer.position(pos0); 666 throw iae; 667 } 668 } 669 670 /** 671 * Returns an input stream for decoding {@link Base64} encoded byte stream. 672 * 673 * <p> The {@code read} methods of the returned {@code InputStream} will 674 * throw {@code IOException} when reading bytes that cannot be decoded. 675 * 676 * <p> Closing the returned input stream will close the underlying 677 * input stream. 678 * 679 * @param is 680 * the input stream 681 * 682 * @return the input stream for decoding the specified Base64 encoded 683 * byte stream 684 */ wrap(InputStream is)685 public InputStream wrap(InputStream is) { 686 Objects.requireNonNull(is); 687 return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME); 688 } 689 690 /** 691 * Calculates the length of the decoded output bytes. 692 * 693 * @param src the byte array to decode 694 * @param sp the source position 695 * @param sl the source limit 696 * 697 * @return length of the decoded bytes 698 * 699 */ decodedOutLength(byte[] src, int sp, int sl)700 private int decodedOutLength(byte[] src, int sp, int sl) { 701 int[] base64 = isURL ? fromBase64URL : fromBase64; 702 int paddings = 0; 703 int len = sl - sp; 704 if (len == 0) 705 return 0; 706 if (len < 2) { 707 if (isMIME && base64[0] == -1) 708 return 0; 709 throw new IllegalArgumentException( 710 "Input byte[] should at least have 2 bytes for base64 bytes"); 711 } 712 if (isMIME) { 713 // scan all bytes to fill out all non-alphabet. a performance 714 // trade-off of pre-scan or Arrays.copyOf 715 int n = 0; 716 while (sp < sl) { 717 int b = src[sp++] & 0xff; 718 if (b == '=') { 719 len -= (sl - sp + 1); 720 break; 721 } 722 if ((b = base64[b]) == -1) 723 n++; 724 } 725 len -= n; 726 } else { 727 if (src[sl - 1] == '=') { 728 paddings++; 729 if (src[sl - 2] == '=') 730 paddings++; 731 } 732 } 733 if (paddings == 0 && (len & 0x3) != 0) 734 paddings = 4 - (len & 0x3); 735 736 // If len is near to Integer.MAX_VALUE, (len + 3) 737 // can possibly overflow, perform this operation as 738 // long and cast it back to integer when the value comes under 739 // integer limit. The final value will always be in integer 740 // limits 741 return 3 * (int) ((len + 3L) / 4) - paddings; 742 } 743 744 /** 745 * Decodes base64 characters, and returns the number of data bytes 746 * written into the destination array. 747 * 748 * It is the fast path for full 4-byte to 3-byte decoding w/o errors. 749 * 750 * decodeBlock() can be overridden by an arch-specific intrinsic. 751 * decodeBlock can choose to decode all, none, or a variable-sized 752 * prefix of the src bytes. This allows the intrinsic to decode in 753 * chunks of the src that are of a favorable size for the specific 754 * processor it's running on. 755 * 756 * If the intrinsic function does not process all of the bytes in 757 * src, it must process a multiple of four of them, making the 758 * returned destination length a multiple of three. 759 * 760 * If any illegal base64 bytes are encountered in src by the 761 * intrinsic, the intrinsic must return the actual number of valid 762 * data bytes already written to dst. Note that the '=' pad 763 * character is treated as an illegal Base64 character by 764 * decodeBlock, so it will not process a block of 4 bytes 765 * containing pad characters. 766 * 767 * Given the parameters, no length check is possible on dst, so dst 768 * is assumed to be large enough to store the decoded bytes. 769 * 770 * @param src 771 * the source byte array of Base64 encoded bytes 772 * @param sp 773 * the offset into src array to begin reading 774 * @param sl 775 * the offset (exclusive) past the last byte to be converted. 776 * @param dst 777 * the destination byte array of decoded data bytes 778 * @param dp 779 * the offset into dst array to begin writing 780 * @param isURL 781 * boolean, when true decode RFC4648 URL-safe base64 characters 782 * @return the number of destination data bytes produced 783 */ 784 @IntrinsicCandidate decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL)785 private int decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) { 786 int[] base64 = isURL ? fromBase64URL : fromBase64; 787 int sl0 = sp + ((sl - sp) & ~0b11); 788 int new_dp = dp; 789 while (sp < sl0) { 790 int b1 = base64[src[sp++] & 0xff]; 791 int b2 = base64[src[sp++] & 0xff]; 792 int b3 = base64[src[sp++] & 0xff]; 793 int b4 = base64[src[sp++] & 0xff]; 794 if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte 795 return new_dp - dp; 796 } 797 int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4; 798 dst[new_dp++] = (byte)(bits0 >> 16); 799 dst[new_dp++] = (byte)(bits0 >> 8); 800 dst[new_dp++] = (byte)(bits0); 801 } 802 return new_dp - dp; 803 } 804 decode0(byte[] src, int sp, int sl, byte[] dst)805 private int decode0(byte[] src, int sp, int sl, byte[] dst) { 806 int[] base64 = isURL ? fromBase64URL : fromBase64; 807 int dp = 0; 808 int bits = 0; 809 int shiftto = 18; // pos of first byte of 4-byte atom 810 811 while (sp < sl) { 812 if (shiftto == 18 && sp < sl - 4) { // fast path 813 int dl = decodeBlock(src, sp, sl, dst, dp, isURL); 814 /* 815 * Calculate how many characters were processed by how many 816 * bytes of data were returned. 817 */ 818 int chars_decoded = (dl / 3) * 4; 819 820 sp += chars_decoded; 821 dp += dl; 822 } 823 if (sp >= sl) { 824 // we're done 825 break; 826 } 827 int b = src[sp++] & 0xff; 828 if ((b = base64[b]) < 0) { 829 if (b == -2) { // padding byte '=' 830 // = shiftto==18 unnecessary padding 831 // x= shiftto==12 a dangling single x 832 // x to be handled together with non-padding case 833 // xx= shiftto==6&&sp==sl missing last = 834 // xx=y shiftto==6 last is not = 835 if (shiftto == 6 && (sp == sl || src[sp++] != '=') || 836 shiftto == 18) { 837 throw new IllegalArgumentException( 838 "Input byte array has wrong 4-byte ending unit"); 839 } 840 break; 841 } 842 if (isMIME) // skip if for rfc2045 843 continue; 844 else 845 throw new IllegalArgumentException( 846 "Illegal base64 character " + 847 Integer.toString(src[sp - 1], 16)); 848 } 849 bits |= (b << shiftto); 850 shiftto -= 6; 851 if (shiftto < 0) { 852 dst[dp++] = (byte)(bits >> 16); 853 dst[dp++] = (byte)(bits >> 8); 854 dst[dp++] = (byte)(bits); 855 shiftto = 18; 856 bits = 0; 857 } 858 } 859 // reached end of byte array or hit padding '=' characters. 860 if (shiftto == 6) { 861 dst[dp++] = (byte)(bits >> 16); 862 } else if (shiftto == 0) { 863 dst[dp++] = (byte)(bits >> 16); 864 dst[dp++] = (byte)(bits >> 8); 865 } else if (shiftto == 12) { 866 // dangling single "x", incorrectly encoded. 867 throw new IllegalArgumentException( 868 "Last unit does not have enough valid bits"); 869 } 870 // anything left is invalid, if is not MIME. 871 // if MIME, ignore all non-base64 character 872 while (sp < sl) { 873 if (isMIME && base64[src[sp++] & 0xff] < 0) 874 continue; 875 throw new IllegalArgumentException( 876 "Input byte array has incorrect ending byte at " + sp); 877 } 878 return dp; 879 } 880 } 881 882 /* 883 * An output stream for encoding bytes into the Base64. 884 */ 885 private static class EncOutputStream extends FilterOutputStream { 886 887 private int leftover = 0; 888 private int b0, b1, b2; 889 private boolean closed = false; 890 891 private final char[] base64; // byte->base64 mapping 892 private final byte[] newline; // line separator, if needed 893 private final int linemax; 894 private final boolean doPadding;// whether or not to pad 895 private int linepos = 0; 896 private byte[] buf; 897 EncOutputStream(OutputStream os, char[] base64, byte[] newline, int linemax, boolean doPadding)898 EncOutputStream(OutputStream os, char[] base64, 899 byte[] newline, int linemax, boolean doPadding) { 900 super(os); 901 this.base64 = base64; 902 this.newline = newline; 903 this.linemax = linemax; 904 this.doPadding = doPadding; 905 this.buf = new byte[linemax <= 0 ? 8124 : linemax]; 906 } 907 908 @Override write(int b)909 public void write(int b) throws IOException { 910 byte[] buf = new byte[1]; 911 buf[0] = (byte)(b & 0xff); 912 write(buf, 0, 1); 913 } 914 checkNewline()915 private void checkNewline() throws IOException { 916 if (linepos == linemax) { 917 out.write(newline); 918 linepos = 0; 919 } 920 } 921 writeb4(char b1, char b2, char b3, char b4)922 private void writeb4(char b1, char b2, char b3, char b4) throws IOException { 923 buf[0] = (byte)b1; 924 buf[1] = (byte)b2; 925 buf[2] = (byte)b3; 926 buf[3] = (byte)b4; 927 out.write(buf, 0, 4); 928 } 929 930 @Override write(byte[] b, int off, int len)931 public void write(byte[] b, int off, int len) throws IOException { 932 if (closed) 933 throw new IOException("Stream is closed"); 934 if (off < 0 || len < 0 || len > b.length - off) 935 throw new ArrayIndexOutOfBoundsException(); 936 if (len == 0) 937 return; 938 if (leftover != 0) { 939 if (leftover == 1) { 940 b1 = b[off++] & 0xff; 941 len--; 942 if (len == 0) { 943 leftover++; 944 return; 945 } 946 } 947 b2 = b[off++] & 0xff; 948 len--; 949 checkNewline(); 950 writeb4(base64[b0 >> 2], 951 base64[(b0 << 4) & 0x3f | (b1 >> 4)], 952 base64[(b1 << 2) & 0x3f | (b2 >> 6)], 953 base64[b2 & 0x3f]); 954 linepos += 4; 955 } 956 int nBits24 = len / 3; 957 leftover = len - (nBits24 * 3); 958 959 while (nBits24 > 0) { 960 checkNewline(); 961 int dl = linemax <= 0 ? buf.length : buf.length - linepos; 962 int sl = off + Math.min(nBits24, dl / 4) * 3; 963 int dp = 0; 964 for (int sp = off; sp < sl; ) { 965 int bits = (b[sp++] & 0xff) << 16 | 966 (b[sp++] & 0xff) << 8 | 967 (b[sp++] & 0xff); 968 buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f]; 969 buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f]; 970 buf[dp++] = (byte)base64[(bits >>> 6) & 0x3f]; 971 buf[dp++] = (byte)base64[bits & 0x3f]; 972 } 973 out.write(buf, 0, dp); 974 off = sl; 975 linepos += dp; 976 nBits24 -= dp / 4; 977 } 978 if (leftover == 1) { 979 b0 = b[off++] & 0xff; 980 } else if (leftover == 2) { 981 b0 = b[off++] & 0xff; 982 b1 = b[off++] & 0xff; 983 } 984 } 985 986 @Override close()987 public void close() throws IOException { 988 if (!closed) { 989 closed = true; 990 if (leftover == 1) { 991 checkNewline(); 992 out.write(base64[b0 >> 2]); 993 out.write(base64[(b0 << 4) & 0x3f]); 994 if (doPadding) { 995 out.write('='); 996 out.write('='); 997 } 998 } else if (leftover == 2) { 999 checkNewline(); 1000 out.write(base64[b0 >> 2]); 1001 out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); 1002 out.write(base64[(b1 << 2) & 0x3f]); 1003 if (doPadding) { 1004 out.write('='); 1005 } 1006 } 1007 leftover = 0; 1008 out.close(); 1009 } 1010 } 1011 } 1012 1013 /* 1014 * An input stream for decoding Base64 bytes 1015 */ 1016 private static class DecInputStream extends InputStream { 1017 1018 private final InputStream is; 1019 private final boolean isMIME; 1020 private final int[] base64; // base64 -> byte mapping 1021 private int bits = 0; // 24-bit buffer for decoding 1022 1023 /* writing bit pos inside bits; one of 24 (left, msb), 18, 12, 6, 0 */ 1024 private int wpos = 0; 1025 1026 /* reading bit pos inside bits: one of 24 (left, msb), 16, 8, 0 */ 1027 private int rpos = 0; 1028 1029 private boolean eof = false; 1030 private boolean closed = false; 1031 DecInputStream(InputStream is, int[] base64, boolean isMIME)1032 DecInputStream(InputStream is, int[] base64, boolean isMIME) { 1033 this.is = is; 1034 this.base64 = base64; 1035 this.isMIME = isMIME; 1036 } 1037 1038 private byte[] sbBuf = new byte[1]; 1039 1040 @Override read()1041 public int read() throws IOException { 1042 return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff; 1043 } 1044 leftovers(byte[] b, int off, int pos, int limit)1045 private int leftovers(byte[] b, int off, int pos, int limit) { 1046 eof = true; 1047 1048 /* 1049 * We use a loop here, as this method is executed only a few times. 1050 * Unrolling the loop would probably not contribute much here. 1051 */ 1052 while (rpos - 8 >= wpos && pos != limit) { 1053 rpos -= 8; 1054 b[pos++] = (byte) (bits >> rpos); 1055 } 1056 return pos - off != 0 || rpos - 8 >= wpos ? pos - off : -1; 1057 } 1058 eof(byte[] b, int off, int pos, int limit)1059 private int eof(byte[] b, int off, int pos, int limit) throws IOException { 1060 /* 1061 * pos != limit 1062 * 1063 * wpos == 18: x dangling single x, invalid unit 1064 * accept ending xx or xxx without padding characters 1065 */ 1066 if (wpos == 18) { 1067 throw new IOException("Base64 stream has one un-decoded dangling byte."); 1068 } 1069 rpos = 24; 1070 return leftovers(b, off, pos, limit); 1071 } 1072 padding(byte[] b, int off, int pos, int limit)1073 private int padding(byte[] b, int off, int pos, int limit) throws IOException { 1074 /* 1075 * pos != limit 1076 * 1077 * wpos == 24: = (unnecessary padding) 1078 * wpos == 18: x= (dangling single x, invalid unit) 1079 * wpos == 12 and missing last '=': xx= (invalid padding) 1080 * wpos == 12 and last is not '=': xx=x (invalid padding) 1081 */ 1082 if (wpos >= 18 || wpos == 12 && is.read() != '=') { 1083 throw new IOException("Illegal base64 ending sequence:" + wpos); 1084 } 1085 rpos = 24; 1086 return leftovers(b, off, pos, limit); 1087 } 1088 1089 @Override read(byte[] b, int off, int len)1090 public int read(byte[] b, int off, int len) throws IOException { 1091 if (closed) { 1092 throw new IOException("Stream is closed"); 1093 } 1094 Objects.checkFromIndexSize(off, len, b.length); 1095 if (len == 0) { 1096 return 0; 1097 } 1098 1099 /* 1100 * Rather than keeping 2 running vars (e.g., off and len), 1101 * we only keep one (pos), while definitely fixing the boundaries 1102 * of the range [off, limit). 1103 * More specifically, each use of pos as an index in b meets 1104 * pos - off >= 0 & limit - pos > 0 1105 * 1106 * Note that limit can overflow to Integer.MIN_VALUE. However, 1107 * as long as comparisons with pos are as coded, there's no harm. 1108 */ 1109 int pos = off; 1110 final int limit = off + len; 1111 if (eof) { 1112 return leftovers(b, off, pos, limit); 1113 } 1114 1115 /* 1116 * Leftovers from previous invocation; here, wpos = 0. 1117 * There can be at most 2 leftover bytes (rpos <= 16). 1118 * Further, b has at least one free place. 1119 * 1120 * The logic could be coded as a loop, (as in method leftovers()) 1121 * but the explicit "unrolling" makes it possible to generate 1122 * better byte extraction code. 1123 */ 1124 if (rpos == 16) { 1125 b[pos++] = (byte) (bits >> 8); 1126 rpos = 8; 1127 if (pos == limit) { 1128 return len; 1129 } 1130 } 1131 if (rpos == 8) { 1132 b[pos++] = (byte) bits; 1133 rpos = 0; 1134 if (pos == limit) { 1135 return len; 1136 } 1137 } 1138 1139 bits = 0; 1140 wpos = 24; 1141 for (;;) { 1142 /* pos != limit & rpos == 0 */ 1143 final int i = is.read(); 1144 if (i < 0) { 1145 return eof(b, off, pos, limit); 1146 } 1147 final int v = base64[i]; 1148 if (v < 0) { 1149 /* 1150 * i not in alphabet, thus 1151 * v == -2: i is '=', the padding 1152 * v == -1: i is something else, typically CR or LF 1153 */ 1154 if (v == -1) { 1155 if (isMIME) { 1156 continue; 1157 } 1158 throw new IOException("Illegal base64 character 0x" + 1159 Integer.toHexString(i)); 1160 } 1161 return padding(b, off, pos, limit); 1162 } 1163 wpos -= 6; 1164 bits |= v << wpos; 1165 if (wpos != 0) { 1166 continue; 1167 } 1168 if (limit - pos >= 3) { 1169 /* frequently taken fast path, no need to track rpos */ 1170 b[pos++] = (byte) (bits >> 16); 1171 b[pos++] = (byte) (bits >> 8); 1172 b[pos++] = (byte) bits; 1173 bits = 0; 1174 wpos = 24; 1175 if (pos == limit) { 1176 return len; 1177 } 1178 continue; 1179 } 1180 1181 /* b has either 1 or 2 free places */ 1182 b[pos++] = (byte) (bits >> 16); 1183 if (pos == limit) { 1184 rpos = 16; 1185 return len; 1186 } 1187 b[pos++] = (byte) (bits >> 8); 1188 /* pos == limit, no need for an if */ 1189 rpos = 8; 1190 return len; 1191 } 1192 } 1193 1194 @Override available()1195 public int available() throws IOException { 1196 if (closed) 1197 throw new IOException("Stream is closed"); 1198 return is.available(); // TBD: 1199 } 1200 1201 @Override close()1202 public void close() throws IOException { 1203 if (!closed) { 1204 closed = true; 1205 is.close(); 1206 } 1207 } 1208 } 1209 } 1210