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