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