View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.codec.binary;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.junit.jupiter.api.Assertions.fail;
27  
28  import java.math.BigInteger;
29  import java.nio.charset.Charset;
30  import java.nio.charset.StandardCharsets;
31  import java.util.Arrays;
32  import java.util.Random;
33  import java.util.stream.Stream;
34  
35  import org.apache.commons.codec.CodecPolicy;
36  import org.apache.commons.codec.DecoderException;
37  import org.apache.commons.codec.EncoderException;
38  import org.apache.commons.lang3.ArrayUtils;
39  import org.junit.jupiter.api.Assumptions;
40  import org.junit.jupiter.api.Test;
41  import org.junit.jupiter.params.ParameterizedTest;
42  import org.junit.jupiter.params.provider.Arguments;
43  import org.junit.jupiter.params.provider.MethodSource;
44  import org.junit.jupiter.params.provider.ValueSource;
45  
46  /**
47   * Tests {@link Base64}.
48   *
49   * @see <a href="https://www.ietf.org/rfc/rfc2045">RFC 2045</a>
50   */
51  class Base64Test {
52  
53      private static final String FOX_BASE64 = "VGhlIH@$#$@%F1aWN@#@#@@rIGJyb3duIGZve\n\r\t%#%#%#%CBqd##$#$W1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==";
54  
55      private static final String FOX_TEXT = "The quick brown fox jumped over the lazy dogs.";
56  
57      private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
58  
59      /**
60       * Example test cases with valid characters but impossible combinations of
61       * trailing characters (i.e. cannot be created during encoding).
62       */
63      static final String[] BASE64_IMPOSSIBLE_CASES = {
64          "ZE==",
65          "ZmC=",
66          "Zm9vYE==",
67          "Zm9vYmC=",
68          "AB",
69      };
70  
71      /**
72       * Copy of the standard base-64 encoding table. Used to test decoding the final
73       * character of encoded bytes.
74       */
75      private static final byte[] STANDARD_ENCODE_TABLE = {
76              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
77              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
78              'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
79              'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
80              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
81      };
82  
83      /**
84       * Test base 64 decoding of the final trailing bits. Trailing encoded bytes
85       * cannot fit exactly into 6-bit characters so the last character has a limited
86       * alphabet where the final bits are zero. This asserts that illegal final
87       * characters throw an exception when decoding.
88       *
89       * @param nbits the number of trailing bits (must be a factor of 6 and {@code <24})
90       */
91      private static void assertBase64DecodingOfTrailingBits(final int nbits) {
92          final Base64 codec = new Base64(0, null, false, CodecPolicy.STRICT);
93          // Requires strict decoding
94          assertTrue(codec.isStrictDecoding());
95          assertEquals(CodecPolicy.STRICT, codec.getCodecPolicy());
96          // A lenient decoder should not re-encode to the same bytes
97          final Base64 defaultCodec = new Base64();
98          assertFalse(defaultCodec.isStrictDecoding());
99          assertEquals(CodecPolicy.LENIENT, defaultCodec.getCodecPolicy());
100         // Create the encoded bytes. The first characters must be valid so fill with 'zero'
101         // then pad to the block size.
102         final int length = nbits / 6;
103         final byte[] encoded = new byte[4];
104         Arrays.fill(encoded, 0, length, STANDARD_ENCODE_TABLE[0]);
105         Arrays.fill(encoded, length, encoded.length, (byte) '=');
106         // Compute how many bits would be discarded from 8-bit bytes
107         final int discard = nbits % 8;
108         final int emptyBitsMask = (1 << discard) - 1;
109         // Special case when an impossible number of trailing characters
110         final boolean invalid = length == 1;
111         // Enumerate all 64 possible final characters in the last position
112         final int last = length - 1;
113         for (int i = 0; i < 64; i++) {
114             encoded[last] = STANDARD_ENCODE_TABLE[i];
115             // If the lower bits are set we expect an exception. This is not a valid
116             // final character.
117             if (invalid || (i & emptyBitsMask) != 0) {
118                 assertThrows(IllegalArgumentException.class, () -> codec.decode(encoded), "Final base-64 digit should not be allowed");
119                 // The default lenient mode should decode this
120                 final byte[] decoded = defaultCodec.decode(encoded);
121                 // Re-encoding should not match the original array as it was invalid
122                 assertFalse(Arrays.equals(encoded, defaultCodec.encode(decoded)));
123             } else {
124                 // Otherwise this should decode
125                 final byte[] decoded = codec.decode(encoded);
126                 // Compute the bits that were encoded. This should match the final decoded byte.
127                 final int bitsEncoded = i >> discard;
128                 assertEquals(bitsEncoded, decoded[decoded.length - 1], "Invalid decoding of last character");
129                 // Re-encoding should match the original array (requires the same padding character)
130                 assertArrayEquals(encoded, codec.encode(decoded));
131             }
132         }
133     }
134 
135     static Stream<Object> testIsBase64() {
136         // @formatter:off
137         return Stream.of(
138             Arguments.of(new byte[] { 1, 2, 3 }, false),
139             Arguments.of(new byte[] { Byte.MIN_VALUE }, false),
140             Arguments.of(new byte[] { -125 }, false),
141             Arguments.of(new byte[] { -10 }, false),
142             Arguments.of(new byte[] { 0 }, false),
143             Arguments.of(new byte[] { 64, Byte.MAX_VALUE }, false),
144             Arguments.of(new byte[] { Byte.MAX_VALUE }, false),
145             Arguments.of(new byte[] { 'A' }, true),
146             Arguments.of(new byte[] { 'A', Byte.MIN_VALUE }, false),
147             Arguments.of(new byte[] { 'A', 'Z', 'a' }, true),
148             Arguments.of(new byte[] { '/', '=', '+' }, true),
149             Arguments.of(new byte[] { '$' }, false));
150         // @formatter:on
151     }
152 
153     static Stream<Object> testIsBase64Url() {
154         // @formatter:off
155         return Stream.of(
156             Arguments.of((byte) '=', true),
157             Arguments.of((byte) 32, false),
158             Arguments.of((byte) 0, false),
159             Arguments.of((byte) 1, false),
160             Arguments.of((byte) 2, false),
161             Arguments.of((byte) 999, false),
162             Arguments.of((byte) -1, false)
163             );
164         // @formatter:on
165     }
166 
167     private final Random random = new Random();
168 
169     /**
170      * @return the random.
171      */
172     public Random getRandom() {
173         return this.random;
174     }
175 
176     /**
177      * Test the Base64 implementation
178      */
179     @Test
180     void testBase64() {
181         final String content = "Hello World";
182         String encodedContent;
183         byte[] encodedBytes = Base64.encodeBase64(StringUtils.getBytesUtf8(content));
184         encodedContent = StringUtils.newStringUtf8(encodedBytes);
185         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
186         // null lineSeparator same as saying no-chunking
187         Base64 b64 = new Base64(BaseNCodec.MIME_CHUNK_SIZE, null);
188         encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
189         encodedContent = StringUtils.newStringUtf8(encodedBytes);
190         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
191         // null lineSeparator same as saying no-chunking
192         b64 = new Base64(0, null);
193         encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
194         encodedContent = StringUtils.newStringUtf8(encodedBytes);
195         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
196         // bogus characters to decode (to skip actually) {e-acute*6}
197         final byte[] decode = b64.decode("SGVsbG{\u00e9\u00e9\u00e9\u00e9\u00e9\u00e9}8gV29ybGQ=");
198         final String decodeString = StringUtils.newStringUtf8(decode);
199         assertEquals("Hello World", decodeString, "decode hello world");
200     }
201 
202     @Test
203     void testBase64AtBufferEnd() {
204         testBase64InBuffer(100, 0);
205     }
206 
207     @Test
208     void testBase64AtBufferMiddle() {
209         testBase64InBuffer(100, 100);
210     }
211 
212     @Test
213     void testBase64AtBufferStart() {
214         testBase64InBuffer(0, 100);
215     }
216 
217     @Test
218     void testBase64DecodingOfTrailing12Bits() {
219         assertBase64DecodingOfTrailingBits(12);
220     }
221 
222     @Test
223     void testBase64DecodingOfTrailing18Bits() {
224         assertBase64DecodingOfTrailingBits(18);
225     }
226 
227     @Test
228     void testBase64DecodingOfTrailing6Bits() {
229         assertBase64DecodingOfTrailingBits(6);
230     }
231 
232     @Test
233     void testBase64ImpossibleSamples() {
234         final Base64 codec = new Base64(0, null, false, CodecPolicy.STRICT);
235         for (final String s : BASE64_IMPOSSIBLE_CASES) {
236             assertThrows(IllegalArgumentException.class, () -> codec.decode(s));
237         }
238     }
239 
240     private void testBase64InBuffer(final int startPasSize, final int endPadSize) {
241         final String content = "Hello World";
242         final String encodedContent;
243         final byte[] bytesUtf8 = StringUtils.getBytesUtf8(content);
244         byte[] buffer = ArrayUtils.addAll(bytesUtf8, new byte[endPadSize]);
245         buffer = ArrayUtils.addAll(new byte[startPasSize], buffer);
246         final byte[] encodedBytes = new Base64().encode(buffer, startPasSize, bytesUtf8.length);
247         encodedContent = StringUtils.newStringUtf8(encodedBytes);
248         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
249     }
250 
251     @Test
252     void testBuilderCodecPolicy() {
253         assertEquals(CodecPolicy.LENIENT, Base64.builder().get().getCodecPolicy());
254         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(CodecPolicy.LENIENT).get().getCodecPolicy());
255         assertEquals(CodecPolicy.STRICT, Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().getCodecPolicy());
256         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).setDecodingPolicy(null).get().getCodecPolicy());
257         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(null).get().getCodecPolicy());
258     }
259 
260     @Test
261     void testBuilderLineAttributes() {
262         assertNull(Base64.builder().get().getLineSeparator());
263         assertNull(Base64.builder().setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
264         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
265         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(null).get().getLineSeparator());
266         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(10).setLineSeparator(null).get().getLineSeparator());
267         assertNull(Base64.builder().setLineLength(-1).setLineSeparator(null).get().getLineSeparator());
268         assertNull(Base64.builder().setLineLength(0).setLineSeparator(null).get().getLineSeparator());
269         assertArrayEquals(new byte[] { 1 }, Base64.builder().setLineLength(4).setLineSeparator((byte) 1).get().getLineSeparator());
270         assertEquals("Zm94\r\n", Base64.builder().setLineLength(4).get().encodeToString("fox".getBytes(CHARSET_UTF8)));
271     }
272 
273     @Test
274     void testBuilderPadingByte() {
275         assertNull(Base64.builder().get().getLineSeparator());
276         assertNull(Base64.builder().setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
277         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
278         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(null).get().getLineSeparator());
279         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(10).setLineSeparator(null).get().getLineSeparator());
280         assertNull(Base64.builder().setLineLength(-1).setLineSeparator(null).get().getLineSeparator());
281         assertNull(Base64.builder().setLineLength(0).setLineSeparator(null).get().getLineSeparator());
282         assertArrayEquals(new byte[] { 1 }, Base64.builder().setLineLength(4).setLineSeparator((byte) 1).get().getLineSeparator());
283         assertEquals("VGhlIGJyb3duIGZveA==", Base64.builder().get().encodeToString("The brown fox".getBytes(CHARSET_UTF8)));
284         assertEquals("VGhlIGJyb3duIGZveA__", Base64.builder().setPadding((byte) '_').get().encodeToString("The brown fox".getBytes(CHARSET_UTF8)));
285     }
286 
287     @Test
288     void testBuilderUrlSafe() {
289         assertFalse(Base64.builder().get().isUrlSafe());
290         assertFalse(Base64.builder().setUrlSafe(false).get().isUrlSafe());
291         assertFalse(Base64.builder().setUrlSafe(true).setUrlSafe(false).get().isUrlSafe());
292         assertTrue(Base64.builder().setUrlSafe(false).setUrlSafe(true).get().isUrlSafe());
293     }
294 
295     @Test
296     void testByteToStringVariations() throws DecoderException {
297         final Base64 base64 = new Base64(0);
298         final byte[] b1 = StringUtils.getBytesUtf8("Hello World");
299         final byte[] b2 = {};
300         final byte[] b3 = null;
301         final byte[] b4 = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090"); // for url-safe tests
302         assertEquals("SGVsbG8gV29ybGQ=", base64.encodeToString(b1), "byteToString Hello World");
303         assertEquals("SGVsbG8gV29ybGQ=", Base64.encodeBase64String(b1), "byteToString static Hello World");
304         assertEquals("", base64.encodeToString(b2), "byteToString \"\"");
305         assertEquals("", Base64.encodeBase64String(b2), "byteToString static \"\"");
306         assertNull(base64.encodeToString(b3), "byteToString null");
307         assertNull(Base64.encodeBase64String(b3), "byteToString static null");
308         assertEquals("K/fMJwH+Q5e0nr7tWsxwkA==", base64.encodeToString(b4), "byteToString UUID");
309         assertEquals("K/fMJwH+Q5e0nr7tWsxwkA==", Base64.encodeBase64String(b4), "byteToString static UUID");
310         assertEquals("K_fMJwH-Q5e0nr7tWsxwkA", Base64.encodeBase64URLSafeString(b4), "byteToString static-url-safe UUID");
311     }
312 
313     /**
314      * Tests Base64.encodeBase64().
315      */
316     @Test
317     void testChunkedEncodeMultipleOf76() {
318         final byte[] expectedEncode = Base64.encodeBase64(BaseNTestData.DECODED, true);
319         // convert to "\r\n" so we're equal to the old openssl encoding test stored in Base64TestData.ENCODED_76_CHARS_PER_LINE:
320         final String actualResult = Base64TestData.ENCODED_76_CHARS_PER_LINE.replace("\n", "\r\n");
321         final byte[] actualEncode = StringUtils.getBytesUtf8(actualResult);
322         assertArrayEquals(expectedEncode, actualEncode, "chunkedEncodeMultipleOf76");
323     }
324 
325     @Test
326     void testCodec112() { // size calculation assumes always chunked
327         final byte[] in = { 0 };
328         final byte[] out = Base64.encodeBase64(in);
329         Base64.encodeBase64(in, false, false, out.length);
330         // TODO Assert??
331     }
332 
333     /**
334      * Tests <a href="https://issues.apache.org/jira/browse/CODEC-263">CODEC-263</a>.
335      */
336     @Test
337     void testCodec263() {
338       Base64.decodeBase64("publishMessage");
339       assertTrue(Base64.isBase64("publishMessage"));
340     }
341 
342     /**
343      * Test for CODEC-265: Encode a 1GiB file.
344      *
345      * @see <a href="https://issues.apache.org/jira/projects/CODEC/issues/CODEC-265">CODEC-265</a>
346      */
347     @Test
348     void testCodec265() {
349         // 1GiB file to encode: 2^30 bytes
350         final int size1GiB = 1 << 30;
351         // Expecting a size of 4 output bytes per 3 input bytes plus the trailing bytes
352         // padded to a block size of 4.
353         final int blocks = (int) Math.ceil(size1GiB / 3.0);
354         final int expectedLength = 4 * blocks;
355         // This test is memory hungry. Check we can run it.
356         final long presumableFreeMemory = BaseNCodecTest.getPresumableFreeMemory();
357         // Estimate the maximum memory required:
358         // 1GiB + 1GiB + ~2GiB + ~1.33GiB + 32 KiB = ~5.33GiB
359         //
360         // 1GiB: Input buffer to encode
361         // 1GiB: Existing working buffer (due to doubling of default buffer size of 8192)
362         // ~2GiB: New working buffer to allocate (due to doubling)
363         // ~1.33GiB: Expected output size (since the working buffer is copied at the end)
364         // 32KiB: Some headroom
365         final long estimatedMemory = (long) size1GiB * 4 + expectedLength + 32 * 1024;
366         Assumptions.assumeTrue(presumableFreeMemory > estimatedMemory, "Not enough free memory for the test");
367         final byte[] bytes = new byte[size1GiB];
368         final byte[] encoded = Base64.encodeBase64(bytes);
369         assertEquals(expectedLength, encoded.length);
370     }
371 
372     /**
373      * CODEC-68: isBase64 throws ArrayIndexOutOfBoundsException on some
374      * non-BASE64 bytes
375      */
376     @Test
377     void testCodec68() {
378         final byte[] x = { 'n', 'A', '=', '=', (byte) 0x9c };
379         Base64.decodeBase64(x);
380     }
381 
382     @Test
383     void testCodeInteger1() {
384         final String encodedInt1 = "li7dzDacuo67Jg7mtqEm2TRuOMU=";
385         final BigInteger bigInt1 = new BigInteger("857393771208094202104259627990318636601332086981");
386         assertEquals(encodedInt1, new String(Base64.encodeInteger(bigInt1)));
387         assertEquals(bigInt1, Base64.decodeInteger(encodedInt1.getBytes(CHARSET_UTF8)));
388     }
389 
390     @Test
391     void testCodeInteger2() {
392         final String encodedInt2 = "9B5ypLY9pMOmtxCeTDHgwdNFeGs=";
393         final BigInteger bigInt2 = new BigInteger("1393672757286116725466646726891466679477132949611");
394         assertEquals(encodedInt2, new String(Base64.encodeInteger(bigInt2)));
395         assertEquals(bigInt2, Base64.decodeInteger(encodedInt2.getBytes(CHARSET_UTF8)));
396     }
397 
398     @Test
399     void testCodeInteger3() {
400         final String encodedInt3 = "FKIhdgaG5LGKiEtF1vHy4f3y700zaD6QwDS3IrNVGzNp2" +
401             "rY+1LFWTK6D44AyiC1n8uWz1itkYMZF0/aKDK0Yjg==";
402         final BigInteger bigInt3 = new BigInteger(
403             "10806548154093873461951748545" +
404             "1196989136416448805819079363524309897749044958112417136240557" +
405             "4495062430572478766856090958495998158114332651671116876320938126");
406         assertEquals(encodedInt3, new String(Base64.encodeInteger(bigInt3)));
407         assertEquals(bigInt3, Base64.decodeInteger(encodedInt3.getBytes(CHARSET_UTF8)));
408     }
409 
410     @Test
411     void testCodeInteger4() {
412         final String encodedInt4 = "ctA8YGxrtngg/zKVvqEOefnwmViFztcnPBYPlJsvh6yKI" +
413             "4iDm68fnp4Mi3RrJ6bZAygFrUIQLxLjV+OJtgJAEto0xAs+Mehuq1DkSFEpP3o" +
414             "DzCTOsrOiS1DwQe4oIb7zVk/9l7aPtJMHW0LVlMdwZNFNNJoqMcT2ZfCPrfvYv" +
415             "Q0=";
416         final BigInteger bigInt4 = new BigInteger("80624726256040348115552042320" +
417             "6968135001872753709424419772586693950232350200555646471175944" +
418             "519297087885987040810778908507262272892702303774422853675597" +
419             "748008534040890923814202286633163248086055216976551456088015" +
420             "338880713818192088877057717530169381044092839402438015097654" +
421             "53542091716518238707344493641683483917");
422         assertEquals(encodedInt4, new String(Base64.encodeInteger(bigInt4)));
423         assertEquals(bigInt4, Base64.decodeInteger(encodedInt4.getBytes(CHARSET_UTF8)));
424     }
425 
426     @Test
427     void testCodeIntegerEdgeCases() {
428         // TODO
429     }
430 
431     @Test
432     void testCodeIntegerNull() {
433         assertThrows(NullPointerException.class, () -> Base64.encodeInteger(null), "Exception not thrown when passing in null to encodeInteger(BigInteger)");
434     }
435 
436     @Test
437     void testConstructor_Int_ByteArray_Boolean() {
438         final Base64 base64 = new Base64(65, new byte[] { '\t' }, false);
439         final byte[] encoded = base64.encode(BaseNTestData.DECODED);
440         String expectedResult = Base64TestData.ENCODED_64_CHARS_PER_LINE;
441         expectedResult = expectedResult.replace('\n', '\t');
442         final String result = StringUtils.newStringUtf8(encoded);
443         assertEquals(expectedResult, result, "new Base64(65, \\t, false)");
444     }
445 
446     @Test
447     void testConstructor_Int_ByteArray_Boolean_UrlSafe() {
448         // url-safe variation
449         final Base64 base64 = new Base64(64, new byte[] { '\t' }, true);
450         final byte[] encoded = base64.encode(BaseNTestData.DECODED);
451         String expectedResult = Base64TestData.ENCODED_64_CHARS_PER_LINE;
452         expectedResult = expectedResult.replace("=", ""); // url-safe has no
453         expectedResult = expectedResult.replace('\n', '\t');
454         expectedResult = expectedResult.replace('+', '-');
455         expectedResult = expectedResult.replace('/', '_');
456         final String result = StringUtils.newStringUtf8(encoded);
457         assertEquals(result, expectedResult, "new Base64(64, \\t, true)");
458     }
459 
460     @Test
461     void testConstructors() {
462         Base64 base64;
463         base64 = new Base64();
464         base64 = new Base64(-1);
465         base64 = new Base64(-1, new byte[] {});
466         base64 = new Base64(64, new byte[] {});
467         base64 = new Base64(64, new byte[] {'$'}); // OK
468 
469         assertThrows(IllegalArgumentException.class, () -> new Base64(-1, new byte[] { 'A' }),
470                 "Should have rejected attempt to use 'A' as a line separator");
471         // TODO do we need to check sep if len = -1?
472 
473         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { 'A' }),
474                 "Should have rejected attempt to use 'A' as a line separator");
475 
476         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { '=' }),
477                 "Should have rejected attempt to use '=' as a line separator");
478 
479         base64 = new Base64(64, new byte[] { '$' }); // OK
480 
481         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { 'A', '$' }),
482                 "Should have rejected attempt to use 'A$' as a line separator");
483 
484         base64 = new Base64(64, new byte[] { ' ', '$', '\n', '\r', '\t' }); // OKassertNotNull(base64);
485     }
486 
487     @Test
488     void testCustomEncodingAlphabet() {
489         // created a duplicate of STANDARD_ENCODE_TABLE and replaced two chars with
490         // custom values not already present in table
491         // A => . B => -
492         // @formatter:off
493         final byte[] encodeTable = {
494                 '.', '-', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
495                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
496                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
497                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
498                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
499         };
500         // @formatter:on
501 
502         // two instances: one with default table and one with adjusted encoding table
503         final Base64 b64 = new Base64();
504         final Base64 b64customEncoding = Base64.builder().setEncodeTable(encodeTable).get();
505 
506         final String content = "! Hello World - this §$%";
507 
508         final byte[] encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
509         final String encodedContent = StringUtils.newStringUtf8(encodedBytes);
510 
511         final byte[] encodedBytesCustom = b64customEncoding.encode(StringUtils.getBytesUtf8(content));
512         final String encodedContentCustom = StringUtils.newStringUtf8(encodedBytesCustom);
513 
514         assertTrue(encodedContent.contains("A") && encodedContent.contains("B"),
515                 "testing precondition not met - ecodedContent should contain parts of modified table");
516 
517         assertEquals(encodedContent.replace('A', '.').replace('B', '-') // replace alphabet adjustments
518                 .replace("=", "") // remove padding (not default alphabet)
519                 , encodedContentCustom);
520 
521         // try decode encoded content
522         final byte[] decode = b64customEncoding.decode(encodedBytesCustom);
523         final String decodeString = StringUtils.newStringUtf8(decode);
524 
525         assertEquals(content, decodeString);
526     }
527 
528     @Test
529     void testCustomEncodingAlphabet_illegal() {
530         final byte[] encodeTable = {
531                 '.', '-', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'
532         };
533         assertThrows(IllegalArgumentException.class, () -> Base64.builder().setEncodeTable(encodeTable).get());
534     }
535 
536     @Test
537     void testDecodeBase64DiffChars() {
538         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF"));
539         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64("Zm9vYmF+"));
540         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64("Zm9vYmF-"));
541         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
542     }
543 
544     @Test
545     void testDecodeBase64StandardDiffChars() {
546         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64Standard("Zm9vYmF"));
547         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64Standard("Zm9vYmF+"));
548         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64Standard("Zm9vYmF-"));
549         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
550     }
551 
552     @Test
553     void testDecodeBase64UrlDiffChars() {
554         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64UrlSafe("Zm9vYmF"));
555         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64UrlSafe("Zm9vYmF+"));
556         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64UrlSafe("Zm9vYmF-"));
557         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
558     }
559 
560     private void testDecodeEncode(final String encodedText) {
561         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(encodedText));
562         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
563         assertEquals(encodedText, encodedText2);
564     }
565 
566     @ParameterizedTest
567     @ValueSource(strings = {
568             "",
569             "Zg==",
570             "Zm8=",
571             "Zm9v",
572             "Zm9vYg==",
573             "Zm9vYmE=",
574             "Zm9vYmFy",
575             "Zm9vYmF+",
576             "Zm9vYmF/"
577     })
578     void testDecodeEncodeStandardByteArray(final String encodedText) {
579         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64Standard(encodedText.getBytes(CHARSET_UTF8)));
580         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
581         assertEquals(encodedText, encodedText2);
582     }
583 
584     @ParameterizedTest
585     @ValueSource(strings = {
586             "",
587             "Zg==",
588             "Zm8=",
589             "Zm9v",
590             "Zm9vYg==",
591             "Zm9vYmE=",
592             "Zm9vYmFy",
593             "Zm9vYmF+",
594             "Zm9vYmF/"
595     })
596     void testDecodeEncodeStandardString(final String encodedText) {
597         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64Standard(encodedText));
598         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
599         assertEquals(encodedText, encodedText2);
600     }
601 
602     @ParameterizedTest
603     @ValueSource(strings = {
604             "",
605             "Zg",
606             "Zm8",
607             "Zm9v",
608             "Zm9vYg",
609             "Zm9vYmE",
610             "Zm9vYmFy",
611             "Zm9vYmF-",
612             "Zm9vYmF_"
613     })
614     void testDecodeEncodeUrlSafeByteArray(final String encodedText) {
615         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64UrlSafe(encodedText.getBytes(CHARSET_UTF8)));
616         final String encodedText2 = Base64.encodeBase64URLSafeString(StringUtils.getBytesUtf8(decodedText));
617         assertEquals(encodedText, encodedText2);
618     }
619 
620 
621     @ParameterizedTest
622     @ValueSource(strings = {
623             "",
624             "Zg",
625             "Zm8",
626             "Zm9v",
627             "Zm9vYg",
628             "Zm9vYmE",
629             "Zm9vYmFy",
630             "Zm9vYmF-",
631             "Zm9vYmF_"
632     })
633     void testDecodeEncodeUrl(final String encodedText) {
634         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64UrlSafe(encodedText));
635         final String encodedText2 = Base64.encodeBase64URLSafeString(StringUtils.getBytesUtf8(decodedText));
636         assertEquals(encodedText, encodedText2);
637     }
638 
639     /**
640      * Tests conditional true branch for "marker0" test.
641      */
642     @Test
643     void testDecodePadMarkerIndex2() {
644         assertEquals("A", new String(Base64.decodeBase64("QQ==".getBytes(CHARSET_UTF8))));
645     }
646 
647     /**
648      * Tests conditional branches for "marker1" test.
649      */
650     @Test
651     void testDecodePadMarkerIndex3() {
652         assertEquals("AA", new String(Base64.decodeBase64("QUE=".getBytes(CHARSET_UTF8))));
653         assertEquals("AAA", new String(Base64.decodeBase64("QUFB".getBytes(CHARSET_UTF8))));
654     }
655 
656     @Test
657     void testDecodePadOnly() {
658         assertEquals(0, Base64.decodeBase64("====".getBytes(CHARSET_UTF8)).length);
659         assertEquals("", new String(Base64.decodeBase64("====".getBytes(CHARSET_UTF8))));
660         // Test truncated padding
661         assertEquals(0, Base64.decodeBase64("===".getBytes(CHARSET_UTF8)).length);
662         assertEquals(0, Base64.decodeBase64("==".getBytes(CHARSET_UTF8)).length);
663         assertEquals(0, Base64.decodeBase64("=".getBytes(CHARSET_UTF8)).length);
664         assertEquals(0, Base64.decodeBase64("".getBytes(CHARSET_UTF8)).length);
665     }
666 
667     @Test
668     void testDecodePadOnlyChunked() {
669         assertEquals(0, Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8)).length);
670         assertEquals("", new String(Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8))));
671         // Test truncated padding
672         assertEquals(0, Base64.decodeBase64("===\n".getBytes(CHARSET_UTF8)).length);
673         assertEquals(0, Base64.decodeBase64("==\n".getBytes(CHARSET_UTF8)).length);
674         assertEquals(0, Base64.decodeBase64("=\n".getBytes(CHARSET_UTF8)).length);
675         assertEquals(0, Base64.decodeBase64("\n".getBytes(CHARSET_UTF8)).length);
676     }
677 
678     /**
679      * Test our decode with pad character in the middle. (Our current
680      * implementation: halt decode and return what we've got so far).
681      *
682      * The point of this test is not to say
683      * "this is the correct way to decode base64." The point is simply to keep
684      * us aware of the current logic since 1.4 so we don't accidentally break it
685      * without realizing.
686      *
687      * Note for historians. The 1.3 logic would decode to:
688      * "Hello World\u0000Hello World" -- null in the middle --- and 1.4
689      * unwittingly changed it to current logic.
690      */
691     @Test
692     void testDecodeWithInnerPad() {
693         final String content = "SGVsbG8gV29ybGQ=SGVsbG8gV29ybGQ=";
694         final byte[] result = Base64.decodeBase64(content);
695         final byte[] shouldBe = StringUtils.getBytesUtf8("Hello World");
696         assertArrayEquals(result, shouldBe, "decode should halt at pad (=)");
697     }
698 
699     @Test
700     void testDecodeWithWhitespace() throws Exception {
701 
702         final String orig = "I am a late night coder.";
703 
704         final byte[] encodedArray = Base64.encodeBase64(orig.getBytes(CHARSET_UTF8));
705         final StringBuilder intermediate = new StringBuilder(new String(encodedArray));
706 
707         intermediate.insert(2, ' ');
708         intermediate.insert(5, '\t');
709         intermediate.insert(10, '\r');
710         intermediate.insert(15, '\n');
711 
712         final byte[] encodedWithWS = intermediate.toString().getBytes(CHARSET_UTF8);
713         final byte[] decodedWithWS = Base64.decodeBase64(encodedWithWS);
714 
715         final String dest = new String(decodedWithWS);
716 
717         assertEquals(orig, dest, "Dest string doesn't equal the original");
718     }
719 
720     /**
721      * Test encode and decode of empty byte array.
722      */
723     @Test
724     void testEmptyBase64() {
725         byte[] empty = {};
726         byte[] result = Base64.encodeBase64(empty);
727         assertEquals(0, result.length, "empty base64 encode");
728         assertNull(Base64.encodeBase64(null), "empty base64 encode");
729         result = new Base64().encode(empty, 0, 1);
730         assertEquals(0, result.length, "empty base64 encode");
731         assertNull(new Base64().encode(null, 0, 1), "empty base64 encode");
732 
733         empty = new byte[0];
734         result = Base64.decodeBase64(empty);
735         assertEquals(0, result.length, "empty base64 decode");
736         assertNull(Base64.decodeBase64((byte[]) null), "empty base64 encode");
737     }
738 
739     private void testEncodeDecode(final String plainText) {
740         final String encodedText = Base64.encodeBase64String(StringUtils.getBytesUtf8(plainText));
741         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(encodedText));
742         assertEquals(plainText, decodedText);
743     }
744 
745     // encode/decode a large random array
746     @Test
747     void testEncodeDecodeRandom() {
748         for (int i = 1; i < 5; i++) {
749             final byte[] data = new byte[getRandom().nextInt(10000) + 1];
750             getRandom().nextBytes(data);
751             final byte[] enc = Base64.encodeBase64(data);
752             assertTrue(Base64.isBase64(enc));
753             final byte[] data2 = Base64.decodeBase64(enc);
754             assertArrayEquals(data, data2);
755         }
756     }
757 
758     // encode/decode random arrays from size 0 to size 11
759     @Test
760     void testEncodeDecodeSmall() {
761         for (int i = 0; i < 12; i++) {
762             final byte[] data = new byte[i];
763             getRandom().nextBytes(data);
764             final byte[] enc = Base64.encodeBase64(data);
765             assertTrue(Base64.isBase64(enc), "\"" + new String(enc) + "\" is Base64 data.");
766             final byte[] data2 = Base64.decodeBase64(enc);
767             assertArrayEquals(data, data2, toString(data) + " equals " + toString(data2));
768         }
769     }
770 
771     @Test
772     void testEncodeOverMaxSize() throws Exception {
773         testEncodeOverMaxSize(-1);
774         testEncodeOverMaxSize(0);
775         testEncodeOverMaxSize(1);
776         testEncodeOverMaxSize(2);
777     }
778 
779     private void testEncodeOverMaxSize(final int maxSize) {
780         assertThrows(IllegalArgumentException.class, () -> Base64.encodeBase64(BaseNTestData.DECODED, true, false, maxSize));
781     }
782 
783     /**
784      * Tests a lineSeparator much bigger than DEFAULT_BUFFER_SIZE.
785      *
786      * @see "<a href='https://mail-archives.apache.org/mod_mbox/commons-dev/201202.mbox/%3C4F3C85D7.5060706@snafu.de%3E'>dev@commons.apache.org</a>"
787      */
788     @Test
789     void testHugeLineSeparator() {
790         final int BaseNCodec_DEFAULT_BUFFER_SIZE = 8192;
791         final int Base64_BYTES_PER_ENCODED_BLOCK = 4;
792         final byte[] baLineSeparator = new byte[BaseNCodec_DEFAULT_BUFFER_SIZE * 4 - 3];
793         final Base64 b64 = new Base64(Base64_BYTES_PER_ENCODED_BLOCK, baLineSeparator);
794         final String strOriginal = "Hello World";
795         final String strDecoded = new String(b64.decode(b64.encode(StringUtils.getBytesUtf8(strOriginal))));
796         assertEquals(strOriginal, strDecoded, "testDEFAULT_BUFFER_SIZE");
797     }
798 
799     @Test
800     void testIgnoringNonBase64InDecode() throws Exception {
801         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(FOX_BASE64.getBytes(CHARSET_UTF8))));
802     }
803 
804     @ParameterizedTest
805     @MethodSource("testIsBase64")
806     void testIsArrayByteBase64(final byte[] arrayOctet, final boolean match) {
807         assertEquals(match, Base64.isArrayByteBase64(arrayOctet));
808     }
809 
810     @ParameterizedTest
811     @MethodSource
812     void testIsBase64(final byte[] arrayOctet, final boolean match) {
813         assertEquals(match, Base64.isBase64(arrayOctet));
814     }
815 
816     @ParameterizedTest
817     @MethodSource("testIsBase64Url")
818     void testIsBase64Standard(final byte octet, final boolean match) {
819         assertEquals(match, Base64.isBase64Standard(octet));
820     }
821 
822     @ParameterizedTest
823     @MethodSource
824     void testIsBase64Url(final byte octet, final boolean match) {
825         assertEquals(match, Base64.isBase64Url(octet));
826     }
827 
828     /**
829      * Test the isStringBase64 method.
830      */
831     @Test
832     void testIsStringBase64() {
833         final String nullString = null;
834         final String emptyString = "";
835         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
836         final String invalidString = validString + (char) 0; // append null character
837         final String standardString = "++AQIDBA/U==";
838         final String urlSafeString = "--AQIDBA_U==";
839         assertThrows(NullPointerException.class, () -> Base64.isBase64(nullString), "Base64.isBase64() should not be null-safe.");
840         assertTrue(Base64.isBase64(emptyString), "Base64.isBase64(empty-string) is true");
841         assertTrue(Base64.isBase64(validString), "Base64.isBase64(valid-string) is true");
842         assertFalse(Base64.isBase64(invalidString), "Base64.isBase64(invalid-string) is false");
843         assertTrue(Base64.isBase64(standardString), "Base64.isBase64(standard-string) is true");
844         assertTrue(Base64.isBase64(urlSafeString), "Base64.isBase64(urlSafe-string) is true");
845     }
846 
847     /**
848      * Test the isStringBase64Standard method.
849      */
850     @Test
851     void testIsStringBase64Standard() {
852         final String nullString = null;
853         final String emptyString = "";
854         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
855         final String invalidString = validString + (char) 0; // append null character
856         final String standardString = "++AQIDBA/U==";
857         final String urlSafeString = "--AQIDBA_U==";
858         assertThrows(NullPointerException.class, () -> Base64.isBase64Standard(nullString), "Base64.isBase64Standard() should not be null-safe.");
859         assertTrue(Base64.isBase64Standard(emptyString), "Base64.isBase64Standard(empty-string) is true");
860         assertTrue(Base64.isBase64Standard(validString), "Base64.isBase64Standard(valid-string) is true");
861         assertFalse(Base64.isBase64Standard(invalidString), "Base64.isBase64Standard(invalid-string) is false");
862         assertTrue(Base64.isBase64Standard(standardString), "Base64.isBase64Standard(standard-string) is true");
863         assertFalse(Base64.isBase64Standard(urlSafeString), "Base64.isBase64Standard(urlSafe-string) is false");
864     }
865 
866     /**
867      * Test the isStringBase64Url method.
868      */
869     @Test
870     void testIsStringBase64Url() {
871         final String nullString = null;
872         final String emptyString = "";
873         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
874         final String invalidString = validString + (char) 0; // append null character
875         final String standardString = "++AQIDBA/U==";
876         final String urlSafeString = "--AQIDBA_U==";
877         assertThrows(NullPointerException.class, () -> Base64.isBase64Url(nullString), "Base64.isBase64Url() should not be null-safe.");
878         assertTrue(Base64.isBase64Url(emptyString), "Base64.isBase64Url(empty-string) is true");
879         assertTrue(Base64.isBase64Url(validString), "Base64.isBase64Url(valid-string) is true");
880         assertFalse(Base64.isBase64Url(invalidString), "Base64.isBase64Url(invalid-string) is false");
881         assertFalse(Base64.isBase64Url(standardString), "Base64.isBase64Url(standard-string) is false");
882         assertTrue(Base64.isBase64Url(urlSafeString), "Base64.isBase64Url(urlSafe-string) is true");
883     }
884 
885     /**
886      * Tests isUrlSafe.
887      */
888     @Test
889     void testIsUrlSafe() {
890         final Base64 base64Standard = new Base64(false);
891         final Base64 base64URLSafe = new Base64(true);
892 
893         assertFalse(base64Standard.isUrlSafe(), "Base64.isUrlSafe=false");
894         assertTrue(base64URLSafe.isUrlSafe(), "Base64.isUrlSafe=true");
895 
896         final byte[] whiteSpace = { ' ', '\n', '\r', '\t' };
897         assertTrue(Base64.isBase64(whiteSpace), "Base64.isBase64(whiteSpace)=true");
898     }
899 
900     @Test
901     void testKnownDecodings() {
902         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(
903                 "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==".getBytes(CHARSET_UTF8))));
904         assertEquals("It was the best of times, it was the worst of times.", new String(Base64.decodeBase64(
905                 "SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==".getBytes(CHARSET_UTF8))));
906         assertEquals("http://jakarta.apache.org/commmons", new String(
907                 Base64.decodeBase64("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==".getBytes(CHARSET_UTF8))));
908         assertEquals("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", new String(Base64.decodeBase64(
909                 "QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==".getBytes(CHARSET_UTF8))));
910         assertEquals("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }",
911                 new String(Base64.decodeBase64("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=".getBytes(CHARSET_UTF8))));
912         assertEquals("xyzzy!", new String(Base64.decodeBase64("eHl6enkh".getBytes(CHARSET_UTF8))));
913     }
914 
915     @Test
916     void testKnownEncodings() {
917         assertEquals("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==", new String(
918                 Base64.encodeBase64(FOX_TEXT.getBytes(CHARSET_UTF8))));
919         assertEquals(
920                 "YmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJs\r\nYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFo\r\nIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBi\r\nbGFoIGJsYWg=\r\n",
921                 new String(Base64.encodeBase64Chunked(
922                         "blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah"
923                                 .getBytes(CHARSET_UTF8))));
924         assertEquals("SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==", new String(
925                 Base64.encodeBase64("It was the best of times, it was the worst of times.".getBytes(CHARSET_UTF8))));
926         assertEquals("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==",
927                 new String(Base64.encodeBase64("http://jakarta.apache.org/commmons".getBytes(CHARSET_UTF8))));
928         assertEquals("QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==", new String(
929                 Base64.encodeBase64("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".getBytes(CHARSET_UTF8))));
930         assertEquals("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=",
931                 new String(Base64.encodeBase64("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }".getBytes(CHARSET_UTF8))));
932         assertEquals("eHl6enkh", new String(Base64.encodeBase64("xyzzy!".getBytes(CHARSET_UTF8))));
933     }
934 
935     @Test
936     void testNonBase64Test() throws Exception {
937         final byte[] bArray = { '%' };
938         assertFalse(Base64.isBase64(bArray), "Invalid Base64 array was incorrectly validated as an array of Base64 encoded data");
939         try {
940             final Base64 b64 = new Base64();
941             final byte[] result = b64.decode(bArray);
942             assertEquals(0, result.length, "The result should be empty as the test encoded content did not contain any valid base 64 characters");
943         } catch (final Exception e) {
944             fail("Exception '" + e.getClass().getName() + "' was thrown when trying to decode invalid base64 encoded data - RFC 2045 requires that all " +
945                     "non base64 character be discarded, an exception should not have been thrown");
946         }
947     }
948 
949     @Test
950     void testObjectDecodeWithInvalidParameter() {
951         assertThrows(DecoderException.class, () -> new Base64().decode(Integer.valueOf(5)),
952                 "decode(Object) didn't throw an exception when passed an Integer object");
953     }
954 
955     @Test
956     void testObjectDecodeWithValidParameter() throws Exception {
957         final String original = "Hello World!";
958         final Object o = Base64.encodeBase64(original.getBytes(CHARSET_UTF8));
959         final Base64 b64 = new Base64();
960         final Object oDecoded = b64.decode(o);
961         final byte[] baDecoded = (byte[]) oDecoded;
962         final String dest = new String(baDecoded);
963         assertEquals(original, dest, "dest string does not equal original");
964     }
965 
966     @Test
967     void testObjectEncode() throws Exception {
968         final Base64 b64 = new Base64();
969         assertEquals("SGVsbG8gV29ybGQ=", new String(b64.encode("Hello World".getBytes(CHARSET_UTF8))));
970     }
971 
972     @Test
973     void testObjectEncodeWithInvalidParameter() {
974         assertThrows(EncoderException.class, () -> new Base64().encode("Yadayadayada"), "encode(Object) didn't throw an exception when passed a String object");
975     }
976 
977     @Test
978     void testObjectEncodeWithValidParameter() throws Exception {
979         final String original = "Hello World!";
980         final Object origObj = original.getBytes(CHARSET_UTF8);
981         final Base64 b64 = new Base64();
982         final Object oEncoded = b64.encode(origObj);
983         final byte[] bArray = Base64.decodeBase64((byte[]) oEncoded);
984         final String dest = new String(bArray);
985         assertEquals(original, dest, "dest string does not equal original");
986     }
987 
988     @Test
989     void testPairs() {
990         assertEquals("AAA=", new String(Base64.encodeBase64(new byte[] { 0, 0 })));
991         for (int i = -128; i <= 127; i++) {
992             final byte[] test = { (byte) i, (byte) i };
993             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
994         }
995     }
996 
997     /**
998      * Tests RFC 1421 section 4.3.2.4 chuck size definition.
999      */
1000     @Test
1001     void testRfc1421Section6Dot8ChunkSizeDefinition() {
1002         assertEquals(64, BaseNCodec.PEM_CHUNK_SIZE);
1003     }
1004 
1005     /**
1006      * Tests RFC 2045 section 2.1 CRLF definition.
1007      */
1008     @Test
1009     void testRfc2045Section2Dot1CrLfDefinition() {
1010         assertArrayEquals(new byte[]{13, 10}, BaseNCodec.CHUNK_SEPARATOR);
1011     }
1012 
1013     /**
1014      * Tests RFC 2045 section 6.8 chuck size definition.
1015      */
1016     @Test
1017     void testRfc2045Section6Dot8ChunkSizeDefinition() {
1018         assertEquals(76, BaseNCodec.MIME_CHUNK_SIZE);
1019     }
1020 
1021     /**
1022      * Tests RFC 4648 section 10 test vectors.
1023      * <ul>
1024      * <li>BASE64("") = ""</li>
1025      * <li>BASE64("f") = "Zg=="</li>
1026      * <li>BASE64("fo") = "Zm8="</li>
1027      * <li>BASE64("foo") = "Zm9v"</li>
1028      * <li>BASE64("foob") = "Zm9vYg=="</li>
1029      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1030      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1031      * </ul>
1032      *
1033      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1034      *      html/rfc4648</a>
1035      */
1036     @Test
1037     void testRfc4648Section10Decode() {
1038         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("")));
1039         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==")));
1040         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=")));
1041         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v")));
1042         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==")));
1043         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=")));
1044         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy")));
1045     }
1046 
1047     /**
1048      * Tests RFC 4648 section 10 test vectors.
1049      * <ul>
1050      * <li>BASE64("") = ""</li>
1051      * <li>BASE64("f") = "Zg=="</li>
1052      * <li>BASE64("fo") = "Zm8="</li>
1053      * <li>BASE64("foo") = "Zm9v"</li>
1054      * <li>BASE64("foob") = "Zm9vYg=="</li>
1055      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1056      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1057      * </ul>
1058      *
1059      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1060      *      html/rfc4648</a>
1061      */
1062     @ParameterizedTest
1063     // @formatter:off
1064     @ValueSource(strings = {
1065             "",
1066             "Zg==",
1067             "Zm8=",
1068             "Zm9v",
1069             "Zm9vYg==",
1070             "Zm9vYmE=",
1071             "Zm9vYmFy"
1072     })
1073     // @formatter:on
1074     void testRfc4648Section10DecodeEncode(final String input) {
1075         testDecodeEncode(input);
1076     }
1077 
1078     /**
1079      * Tests RFC 4648 section 10 test vectors.
1080      * <ul>
1081      * <li>BASE64("") = ""</li>
1082      * <li>BASE64("f") = "Zg=="</li>
1083      * <li>BASE64("fo") = "Zm8="</li>
1084      * <li>BASE64("foo") = "Zm9v"</li>
1085      * <li>BASE64("foob") = "Zm9vYg=="</li>
1086      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1087      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1088      * </ul>
1089      *
1090      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1091      *      html/rfc4648</a>
1092      */
1093     @Test
1094     void testRfc4648Section10DecodeWithCrLf() {
1095         final String CRLF = StringUtils.newStringUsAscii(BaseNCodec.CHUNK_SEPARATOR);
1096         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("" + CRLF)));
1097         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==" + CRLF)));
1098         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=" + CRLF)));
1099         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v" + CRLF)));
1100         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==" + CRLF)));
1101         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=" + CRLF)));
1102         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy" + CRLF)));
1103     }
1104 
1105     /**
1106      * Tests RFC 4648 section 10 test vectors.
1107      * <ul>
1108      * <li>BASE64("") = ""</li>
1109      * <li>BASE64("f") = "Zg=="</li>
1110      * <li>BASE64("fo") = "Zm8="</li>
1111      * <li>BASE64("foo") = "Zm9v"</li>
1112      * <li>BASE64("foob") = "Zm9vYg=="</li>
1113      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1114      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1115      * </ul>
1116      *
1117      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1118      *      html/rfc4648</a>
1119      */
1120     @Test
1121     void testRfc4648Section10Encode() {
1122         assertEquals("", Base64.encodeBase64String(StringUtils.getBytesUtf8("")));
1123         assertEquals("Zg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("f")));
1124         assertEquals("Zm8=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fo")));
1125         assertEquals("Zm9v", Base64.encodeBase64String(StringUtils.getBytesUtf8("foo")));
1126         assertEquals("Zm9vYg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("foob")));
1127         assertEquals("Zm9vYmE=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fooba")));
1128         assertEquals("Zm9vYmFy", Base64.encodeBase64String(StringUtils.getBytesUtf8("foobar")));
1129     }
1130 
1131     /**
1132      * Tests RFC 4648 section 10 test vectors.
1133      * <ul>
1134      * <li>BASE64("") = ""</li>
1135      * <li>BASE64("f") = "Zg=="</li>
1136      * <li>BASE64("fo") = "Zm8="</li>
1137      * <li>BASE64("foo") = "Zm9v"</li>
1138      * <li>BASE64("foob") = "Zm9vYg=="</li>
1139      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1140      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1141      * </ul>
1142      *
1143      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1144      *      html/rfc4648</a>
1145      */
1146     @ParameterizedTest
1147     // @formatter:off
1148     @ValueSource(strings = {
1149             "",
1150             "f",
1151             "fo",
1152             "foo",
1153             "foob",
1154             "fooba",
1155             "foobar",
1156     })
1157     // @formatter:on
1158     void testRfc4648Section10EncodeDecode(final String input) {
1159         testEncodeDecode(input);
1160     }
1161 
1162     @Test
1163     void testSingletons() {
1164         assertEquals("AA==", new String(Base64.encodeBase64(new byte[] { (byte) 0 })));
1165         assertEquals("AQ==", new String(Base64.encodeBase64(new byte[] { (byte) 1 })));
1166         assertEquals("Ag==", new String(Base64.encodeBase64(new byte[] { (byte) 2 })));
1167         assertEquals("Aw==", new String(Base64.encodeBase64(new byte[] { (byte) 3 })));
1168         assertEquals("BA==", new String(Base64.encodeBase64(new byte[] { (byte) 4 })));
1169         assertEquals("BQ==", new String(Base64.encodeBase64(new byte[] { (byte) 5 })));
1170         assertEquals("Bg==", new String(Base64.encodeBase64(new byte[] { (byte) 6 })));
1171         assertEquals("Bw==", new String(Base64.encodeBase64(new byte[] { (byte) 7 })));
1172         assertEquals("CA==", new String(Base64.encodeBase64(new byte[] { (byte) 8 })));
1173         assertEquals("CQ==", new String(Base64.encodeBase64(new byte[] { (byte) 9 })));
1174         assertEquals("Cg==", new String(Base64.encodeBase64(new byte[] { (byte) 10 })));
1175         assertEquals("Cw==", new String(Base64.encodeBase64(new byte[] { (byte) 11 })));
1176         assertEquals("DA==", new String(Base64.encodeBase64(new byte[] { (byte) 12 })));
1177         assertEquals("DQ==", new String(Base64.encodeBase64(new byte[] { (byte) 13 })));
1178         assertEquals("Dg==", new String(Base64.encodeBase64(new byte[] { (byte) 14 })));
1179         assertEquals("Dw==", new String(Base64.encodeBase64(new byte[] { (byte) 15 })));
1180         assertEquals("EA==", new String(Base64.encodeBase64(new byte[] { (byte) 16 })));
1181         assertEquals("EQ==", new String(Base64.encodeBase64(new byte[] { (byte) 17 })));
1182         assertEquals("Eg==", new String(Base64.encodeBase64(new byte[] { (byte) 18 })));
1183         assertEquals("Ew==", new String(Base64.encodeBase64(new byte[] { (byte) 19 })));
1184         assertEquals("FA==", new String(Base64.encodeBase64(new byte[] { (byte) 20 })));
1185         assertEquals("FQ==", new String(Base64.encodeBase64(new byte[] { (byte) 21 })));
1186         assertEquals("Fg==", new String(Base64.encodeBase64(new byte[] { (byte) 22 })));
1187         assertEquals("Fw==", new String(Base64.encodeBase64(new byte[] { (byte) 23 })));
1188         assertEquals("GA==", new String(Base64.encodeBase64(new byte[] { (byte) 24 })));
1189         assertEquals("GQ==", new String(Base64.encodeBase64(new byte[] { (byte) 25 })));
1190         assertEquals("Gg==", new String(Base64.encodeBase64(new byte[] { (byte) 26 })));
1191         assertEquals("Gw==", new String(Base64.encodeBase64(new byte[] { (byte) 27 })));
1192         assertEquals("HA==", new String(Base64.encodeBase64(new byte[] { (byte) 28 })));
1193         assertEquals("HQ==", new String(Base64.encodeBase64(new byte[] { (byte) 29 })));
1194         assertEquals("Hg==", new String(Base64.encodeBase64(new byte[] { (byte) 30 })));
1195         assertEquals("Hw==", new String(Base64.encodeBase64(new byte[] { (byte) 31 })));
1196         assertEquals("IA==", new String(Base64.encodeBase64(new byte[] { (byte) 32 })));
1197         assertEquals("IQ==", new String(Base64.encodeBase64(new byte[] { (byte) 33 })));
1198         assertEquals("Ig==", new String(Base64.encodeBase64(new byte[] { (byte) 34 })));
1199         assertEquals("Iw==", new String(Base64.encodeBase64(new byte[] { (byte) 35 })));
1200         assertEquals("JA==", new String(Base64.encodeBase64(new byte[] { (byte) 36 })));
1201         assertEquals("JQ==", new String(Base64.encodeBase64(new byte[] { (byte) 37 })));
1202         assertEquals("Jg==", new String(Base64.encodeBase64(new byte[] { (byte) 38 })));
1203         assertEquals("Jw==", new String(Base64.encodeBase64(new byte[] { (byte) 39 })));
1204         assertEquals("KA==", new String(Base64.encodeBase64(new byte[] { (byte) 40 })));
1205         assertEquals("KQ==", new String(Base64.encodeBase64(new byte[] { (byte) 41 })));
1206         assertEquals("Kg==", new String(Base64.encodeBase64(new byte[] { (byte) 42 })));
1207         assertEquals("Kw==", new String(Base64.encodeBase64(new byte[] { (byte) 43 })));
1208         assertEquals("LA==", new String(Base64.encodeBase64(new byte[] { (byte) 44 })));
1209         assertEquals("LQ==", new String(Base64.encodeBase64(new byte[] { (byte) 45 })));
1210         assertEquals("Lg==", new String(Base64.encodeBase64(new byte[] { (byte) 46 })));
1211         assertEquals("Lw==", new String(Base64.encodeBase64(new byte[] { (byte) 47 })));
1212         assertEquals("MA==", new String(Base64.encodeBase64(new byte[] { (byte) 48 })));
1213         assertEquals("MQ==", new String(Base64.encodeBase64(new byte[] { (byte) 49 })));
1214         assertEquals("Mg==", new String(Base64.encodeBase64(new byte[] { (byte) 50 })));
1215         assertEquals("Mw==", new String(Base64.encodeBase64(new byte[] { (byte) 51 })));
1216         assertEquals("NA==", new String(Base64.encodeBase64(new byte[] { (byte) 52 })));
1217         assertEquals("NQ==", new String(Base64.encodeBase64(new byte[] { (byte) 53 })));
1218         assertEquals("Ng==", new String(Base64.encodeBase64(new byte[] { (byte) 54 })));
1219         assertEquals("Nw==", new String(Base64.encodeBase64(new byte[] { (byte) 55 })));
1220         assertEquals("OA==", new String(Base64.encodeBase64(new byte[] { (byte) 56 })));
1221         assertEquals("OQ==", new String(Base64.encodeBase64(new byte[] { (byte) 57 })));
1222         assertEquals("Og==", new String(Base64.encodeBase64(new byte[] { (byte) 58 })));
1223         assertEquals("Ow==", new String(Base64.encodeBase64(new byte[] { (byte) 59 })));
1224         assertEquals("PA==", new String(Base64.encodeBase64(new byte[] { (byte) 60 })));
1225         assertEquals("PQ==", new String(Base64.encodeBase64(new byte[] { (byte) 61 })));
1226         assertEquals("Pg==", new String(Base64.encodeBase64(new byte[] { (byte) 62 })));
1227         assertEquals("Pw==", new String(Base64.encodeBase64(new byte[] { (byte) 63 })));
1228         assertEquals("QA==", new String(Base64.encodeBase64(new byte[] { (byte) 64 })));
1229         assertEquals("QQ==", new String(Base64.encodeBase64(new byte[] { (byte) 65 })));
1230         assertEquals("Qg==", new String(Base64.encodeBase64(new byte[] { (byte) 66 })));
1231         assertEquals("Qw==", new String(Base64.encodeBase64(new byte[] { (byte) 67 })));
1232         assertEquals("RA==", new String(Base64.encodeBase64(new byte[] { (byte) 68 })));
1233         assertEquals("RQ==", new String(Base64.encodeBase64(new byte[] { (byte) 69 })));
1234         assertEquals("Rg==", new String(Base64.encodeBase64(new byte[] { (byte) 70 })));
1235         assertEquals("Rw==", new String(Base64.encodeBase64(new byte[] { (byte) 71 })));
1236         assertEquals("SA==", new String(Base64.encodeBase64(new byte[] { (byte) 72 })));
1237         assertEquals("SQ==", new String(Base64.encodeBase64(new byte[] { (byte) 73 })));
1238         assertEquals("Sg==", new String(Base64.encodeBase64(new byte[] { (byte) 74 })));
1239         assertEquals("Sw==", new String(Base64.encodeBase64(new byte[] { (byte) 75 })));
1240         assertEquals("TA==", new String(Base64.encodeBase64(new byte[] { (byte) 76 })));
1241         assertEquals("TQ==", new String(Base64.encodeBase64(new byte[] { (byte) 77 })));
1242         assertEquals("Tg==", new String(Base64.encodeBase64(new byte[] { (byte) 78 })));
1243         assertEquals("Tw==", new String(Base64.encodeBase64(new byte[] { (byte) 79 })));
1244         assertEquals("UA==", new String(Base64.encodeBase64(new byte[] { (byte) 80 })));
1245         assertEquals("UQ==", new String(Base64.encodeBase64(new byte[] { (byte) 81 })));
1246         assertEquals("Ug==", new String(Base64.encodeBase64(new byte[] { (byte) 82 })));
1247         assertEquals("Uw==", new String(Base64.encodeBase64(new byte[] { (byte) 83 })));
1248         assertEquals("VA==", new String(Base64.encodeBase64(new byte[] { (byte) 84 })));
1249         assertEquals("VQ==", new String(Base64.encodeBase64(new byte[] { (byte) 85 })));
1250         assertEquals("Vg==", new String(Base64.encodeBase64(new byte[] { (byte) 86 })));
1251         assertEquals("Vw==", new String(Base64.encodeBase64(new byte[] { (byte) 87 })));
1252         assertEquals("WA==", new String(Base64.encodeBase64(new byte[] { (byte) 88 })));
1253         assertEquals("WQ==", new String(Base64.encodeBase64(new byte[] { (byte) 89 })));
1254         assertEquals("Wg==", new String(Base64.encodeBase64(new byte[] { (byte) 90 })));
1255         assertEquals("Ww==", new String(Base64.encodeBase64(new byte[] { (byte) 91 })));
1256         assertEquals("XA==", new String(Base64.encodeBase64(new byte[] { (byte) 92 })));
1257         assertEquals("XQ==", new String(Base64.encodeBase64(new byte[] { (byte) 93 })));
1258         assertEquals("Xg==", new String(Base64.encodeBase64(new byte[] { (byte) 94 })));
1259         assertEquals("Xw==", new String(Base64.encodeBase64(new byte[] { (byte) 95 })));
1260         assertEquals("YA==", new String(Base64.encodeBase64(new byte[] { (byte) 96 })));
1261         assertEquals("YQ==", new String(Base64.encodeBase64(new byte[] { (byte) 97 })));
1262         assertEquals("Yg==", new String(Base64.encodeBase64(new byte[] { (byte) 98 })));
1263         assertEquals("Yw==", new String(Base64.encodeBase64(new byte[] { (byte) 99 })));
1264         assertEquals("ZA==", new String(Base64.encodeBase64(new byte[] { (byte) 100 })));
1265         assertEquals("ZQ==", new String(Base64.encodeBase64(new byte[] { (byte) 101 })));
1266         assertEquals("Zg==", new String(Base64.encodeBase64(new byte[] { (byte) 102 })));
1267         assertEquals("Zw==", new String(Base64.encodeBase64(new byte[] { (byte) 103 })));
1268         assertEquals("aA==", new String(Base64.encodeBase64(new byte[] { (byte) 104 })));
1269         for (int i = -128; i <= 127; i++) {
1270             final byte[] test = { (byte) i };
1271             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
1272         }
1273     }
1274 
1275     @Test
1276     void testSingletonsChunked() {
1277         assertEquals("AA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0 })));
1278         assertEquals("AQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 1 })));
1279         assertEquals("Ag==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 2 })));
1280         assertEquals("Aw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 3 })));
1281         assertEquals("BA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 4 })));
1282         assertEquals("BQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 5 })));
1283         assertEquals("Bg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 6 })));
1284         assertEquals("Bw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 7 })));
1285         assertEquals("CA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 8 })));
1286         assertEquals("CQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 9 })));
1287         assertEquals("Cg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 10 })));
1288         assertEquals("Cw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 11 })));
1289         assertEquals("DA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 12 })));
1290         assertEquals("DQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 13 })));
1291         assertEquals("Dg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 14 })));
1292         assertEquals("Dw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 15 })));
1293         assertEquals("EA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 16 })));
1294         assertEquals("EQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 17 })));
1295         assertEquals("Eg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 18 })));
1296         assertEquals("Ew==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 19 })));
1297         assertEquals("FA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 20 })));
1298         assertEquals("FQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 21 })));
1299         assertEquals("Fg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 22 })));
1300         assertEquals("Fw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 23 })));
1301         assertEquals("GA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 24 })));
1302         assertEquals("GQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 25 })));
1303         assertEquals("Gg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 26 })));
1304         assertEquals("Gw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 27 })));
1305         assertEquals("HA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 28 })));
1306         assertEquals("HQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 29 })));
1307         assertEquals("Hg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 30 })));
1308         assertEquals("Hw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 31 })));
1309         assertEquals("IA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 32 })));
1310         assertEquals("IQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 33 })));
1311         assertEquals("Ig==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 34 })));
1312         assertEquals("Iw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 35 })));
1313         assertEquals("JA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 36 })));
1314         assertEquals("JQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 37 })));
1315         assertEquals("Jg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 38 })));
1316         assertEquals("Jw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 39 })));
1317         assertEquals("KA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 40 })));
1318         assertEquals("KQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 41 })));
1319         assertEquals("Kg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 42 })));
1320         assertEquals("Kw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 43 })));
1321         assertEquals("LA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 44 })));
1322         assertEquals("LQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 45 })));
1323         assertEquals("Lg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 46 })));
1324         assertEquals("Lw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 47 })));
1325         assertEquals("MA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 48 })));
1326         assertEquals("MQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 49 })));
1327         assertEquals("Mg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 50 })));
1328         assertEquals("Mw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 51 })));
1329         assertEquals("NA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 52 })));
1330         assertEquals("NQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 53 })));
1331         assertEquals("Ng==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 54 })));
1332         assertEquals("Nw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 55 })));
1333         assertEquals("OA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 56 })));
1334         assertEquals("OQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 57 })));
1335         assertEquals("Og==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 58 })));
1336         assertEquals("Ow==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 59 })));
1337         assertEquals("PA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 60 })));
1338         assertEquals("PQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 61 })));
1339         assertEquals("Pg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 62 })));
1340         assertEquals("Pw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 63 })));
1341         assertEquals("QA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 64 })));
1342         assertEquals("QQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 65 })));
1343         assertEquals("Qg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 66 })));
1344         assertEquals("Qw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 67 })));
1345         assertEquals("RA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 68 })));
1346         assertEquals("RQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 69 })));
1347         assertEquals("Rg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 70 })));
1348         assertEquals("Rw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 71 })));
1349         assertEquals("SA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 72 })));
1350         assertEquals("SQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 73 })));
1351         assertEquals("Sg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 74 })));
1352         assertEquals("Sw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 75 })));
1353         assertEquals("TA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 76 })));
1354         assertEquals("TQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 77 })));
1355         assertEquals("Tg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 78 })));
1356         assertEquals("Tw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 79 })));
1357         assertEquals("UA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 80 })));
1358         assertEquals("UQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 81 })));
1359         assertEquals("Ug==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 82 })));
1360         assertEquals("Uw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 83 })));
1361         assertEquals("VA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 84 })));
1362         assertEquals("VQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 85 })));
1363         assertEquals("Vg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 86 })));
1364         assertEquals("Vw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 87 })));
1365         assertEquals("WA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 88 })));
1366         assertEquals("WQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 89 })));
1367         assertEquals("Wg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 90 })));
1368         assertEquals("Ww==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 91 })));
1369         assertEquals("XA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 92 })));
1370         assertEquals("XQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 93 })));
1371         assertEquals("Xg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 94 })));
1372         assertEquals("Xw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 95 })));
1373         assertEquals("YA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 96 })));
1374         assertEquals("YQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 97 })));
1375         assertEquals("Yg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 98 })));
1376         assertEquals("Yw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 99 })));
1377         assertEquals("ZA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 100 })));
1378         assertEquals("ZQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 101 })));
1379         assertEquals("Zg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 102 })));
1380         assertEquals("Zw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 103 })));
1381         assertEquals("aA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 104 })));
1382     }
1383 
1384     @Test
1385     void testStringToByteVariations() throws DecoderException {
1386         final Base64 base64 = new Base64();
1387         final String s1 = "SGVsbG8gV29ybGQ=\r\n";
1388         final String s2 = "";
1389         final String s3 = null;
1390         final String s4a = "K/fMJwH+Q5e0nr7tWsxwkA==\r\n";
1391         final String s4b = "K_fMJwH-Q5e0nr7tWsxwkA";
1392         final byte[] b4 = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090"); // for url-safe tests
1393         assertEquals("Hello World", StringUtils.newStringUtf8(base64.decode(s1)), "StringToByte Hello World");
1394         assertEquals("Hello World", StringUtils.newStringUtf8((byte[]) base64.decode((Object) s1)), "StringToByte Hello World");
1395         assertEquals("Hello World", StringUtils.newStringUtf8(Base64.decodeBase64(s1)), "StringToByte static Hello World");
1396         assertEquals("", StringUtils.newStringUtf8(base64.decode(s2)), "StringToByte \"\"");
1397         assertEquals("", StringUtils.newStringUtf8(Base64.decodeBase64(s2)), "StringToByte static \"\"");
1398         assertNull(StringUtils.newStringUtf8(base64.decode(s3)), "StringToByte null");
1399         assertNull(StringUtils.newStringUtf8(Base64.decodeBase64(s3)), "StringToByte static null");
1400         assertArrayEquals(b4, base64.decode(s4b), "StringToByte UUID");
1401         assertArrayEquals(b4, Base64.decodeBase64(s4a), "StringToByte static UUID");
1402         assertArrayEquals(b4, Base64.decodeBase64(s4b), "StringToByte static-url-safe UUID");
1403     }
1404 
1405     @Test
1406     void testTriplets() {
1407         assertEquals("AAAA", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1408         assertEquals("AAAB", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1409         assertEquals("AAAC", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1410         assertEquals("AAAD", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1411         assertEquals("AAAE", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1412         assertEquals("AAAF", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1413         assertEquals("AAAG", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1414         assertEquals("AAAH", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1415         assertEquals("AAAI", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1416         assertEquals("AAAJ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1417         assertEquals("AAAK", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1418         assertEquals("AAAL", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1419         assertEquals("AAAM", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1420         assertEquals("AAAN", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1421         assertEquals("AAAO", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1422         assertEquals("AAAP", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1423         assertEquals("AAAQ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1424         assertEquals("AAAR", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1425         assertEquals("AAAS", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1426         assertEquals("AAAT", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1427         assertEquals("AAAU", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1428         assertEquals("AAAV", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1429         assertEquals("AAAW", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1430         assertEquals("AAAX", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1431         assertEquals("AAAY", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1432         assertEquals("AAAZ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1433         assertEquals("AAAa", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1434         assertEquals("AAAb", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1435         assertEquals("AAAc", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1436         assertEquals("AAAd", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1437         assertEquals("AAAe", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1438         assertEquals("AAAf", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1439         assertEquals("AAAg", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1440         assertEquals("AAAh", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1441         assertEquals("AAAi", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1442         assertEquals("AAAj", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1443         assertEquals("AAAk", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1444         assertEquals("AAAl", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1445         assertEquals("AAAm", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1446         assertEquals("AAAn", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1447         assertEquals("AAAo", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1448         assertEquals("AAAp", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1449         assertEquals("AAAq", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1450         assertEquals("AAAr", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1451         assertEquals("AAAs", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1452         assertEquals("AAAt", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1453         assertEquals("AAAu", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1454         assertEquals("AAAv", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1455         assertEquals("AAAw", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1456         assertEquals("AAAx", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1457         assertEquals("AAAy", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1458         assertEquals("AAAz", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1459         assertEquals("AAA0", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1460         assertEquals("AAA1", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1461         assertEquals("AAA2", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1462         assertEquals("AAA3", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1463         assertEquals("AAA4", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1464         assertEquals("AAA5", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1465         assertEquals("AAA6", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1466         assertEquals("AAA7", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1467         assertEquals("AAA8", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1468         assertEquals("AAA9", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1469         assertEquals("AAA+", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1470         assertEquals("AAA/", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1471     }
1472 
1473     @Test
1474     void testTripletsChunked() {
1475         assertEquals("AAAA\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1476         assertEquals("AAAB\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1477         assertEquals("AAAC\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1478         assertEquals("AAAD\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1479         assertEquals("AAAE\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1480         assertEquals("AAAF\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1481         assertEquals("AAAG\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1482         assertEquals("AAAH\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1483         assertEquals("AAAI\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1484         assertEquals("AAAJ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1485         assertEquals("AAAK\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1486         assertEquals("AAAL\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1487         assertEquals("AAAM\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1488         assertEquals("AAAN\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1489         assertEquals("AAAO\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1490         assertEquals("AAAP\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1491         assertEquals("AAAQ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1492         assertEquals("AAAR\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1493         assertEquals("AAAS\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1494         assertEquals("AAAT\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1495         assertEquals("AAAU\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1496         assertEquals("AAAV\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1497         assertEquals("AAAW\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1498         assertEquals("AAAX\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1499         assertEquals("AAAY\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1500         assertEquals("AAAZ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1501         assertEquals("AAAa\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1502         assertEquals("AAAb\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1503         assertEquals("AAAc\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1504         assertEquals("AAAd\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1505         assertEquals("AAAe\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1506         assertEquals("AAAf\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1507         assertEquals("AAAg\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1508         assertEquals("AAAh\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1509         assertEquals("AAAi\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1510         assertEquals("AAAj\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1511         assertEquals("AAAk\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1512         assertEquals("AAAl\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1513         assertEquals("AAAm\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1514         assertEquals("AAAn\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1515         assertEquals("AAAo\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1516         assertEquals("AAAp\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1517         assertEquals("AAAq\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1518         assertEquals("AAAr\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1519         assertEquals("AAAs\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1520         assertEquals("AAAt\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1521         assertEquals("AAAu\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1522         assertEquals("AAAv\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1523         assertEquals("AAAw\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1524         assertEquals("AAAx\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1525         assertEquals("AAAy\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1526         assertEquals("AAAz\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1527         assertEquals("AAA0\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1528         assertEquals("AAA1\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1529         assertEquals("AAA2\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1530         assertEquals("AAA3\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1531         assertEquals("AAA4\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1532         assertEquals("AAA5\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1533         assertEquals("AAA6\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1534         assertEquals("AAA7\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1535         assertEquals("AAA8\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1536         assertEquals("AAA9\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1537         assertEquals("AAA+\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1538         assertEquals("AAA/\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1539     }
1540 
1541     /**
1542      * Tests URL-safe Base64 against random data, sizes 0 to 150.
1543      */
1544     @Test
1545     void testUrlSafe() {
1546         // test random data of sizes 0 through 150
1547         final BaseNCodec codec = new Base64(true);
1548         for (int i = 0; i <= 150; i++) {
1549             final byte[][] randomData = BaseNTestData.randomData(codec, i);
1550             final byte[] encoded = randomData[1];
1551             final byte[] decoded = randomData[0];
1552             final byte[] result = Base64.decodeBase64(encoded);
1553             assertArrayEquals(decoded, result, "url-safe i=" + i);
1554             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '='), "url-safe i=" + i + " no '='");
1555             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '\\'), "url-safe i=" + i + " no '\\'");
1556             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '+'), "url-safe i=" + i + " no '+'");
1557         }
1558 
1559     }
1560 
1561     /**
1562      * Base64 encoding of UUID's is a common use-case, especially in URL-SAFE
1563      * mode. This test case ends up being the "URL-SAFE" JUnit's.
1564      *
1565      * @throws DecoderException
1566      *             if Hex.decode() fails - a serious problem since Hex comes
1567      *             from our own commons-codec!
1568      */
1569     @Test
1570     void testUUID() throws DecoderException {
1571         // The 4 UUID's below contains mixtures of + and / to help us test the
1572         // URL-SAFE encoding mode.
1573         final byte[][] ids = new byte[4][];
1574 
1575         // ids[0] was chosen so that it encodes with at least one +.
1576         ids[0] = Hex.decodeHex("94ed8d0319e4493399560fb67404d370");
1577 
1578         // ids[1] was chosen so that it encodes with both / and +.
1579         ids[1] = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090");
1580 
1581         // ids[2] was chosen so that it encodes with at least one /.
1582         ids[2] = Hex.decodeHex("64be154b6ffa40258d1a01288e7c31ca");
1583 
1584         // ids[3] was chosen so that it encodes with both / and +, with /
1585         // right at the beginning.
1586         ids[3] = Hex.decodeHex("ff7f8fc01cdb471a8c8b5a9306183fe8");
1587 
1588         final byte[][] standard = new byte[4][];
1589         standard[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg+2dATTcA==");
1590         standard[1] = StringUtils.getBytesUtf8("K/fMJwH+Q5e0nr7tWsxwkA==");
1591         standard[2] = StringUtils.getBytesUtf8("ZL4VS2/6QCWNGgEojnwxyg==");
1592         standard[3] = StringUtils.getBytesUtf8("/3+PwBzbRxqMi1qTBhg/6A==");
1593 
1594         final byte[][] urlSafe1 = new byte[4][];
1595         // regular padding (two '==' signs).
1596         urlSafe1[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA==");
1597         urlSafe1[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA==");
1598         urlSafe1[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg==");
1599         urlSafe1[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A==");
1600 
1601         final byte[][] urlSafe2 = new byte[4][];
1602         // single padding (only one '=' sign).
1603         urlSafe2[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA=");
1604         urlSafe2[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA=");
1605         urlSafe2[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg=");
1606         urlSafe2[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A=");
1607 
1608         final byte[][] urlSafe3 = new byte[4][];
1609         // no padding (no '=' signs).
1610         urlSafe3[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA");
1611         urlSafe3[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA");
1612         urlSafe3[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg");
1613         urlSafe3[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A");
1614 
1615         for (int i = 0; i < 4; i++) {
1616             final byte[] encodedStandard = Base64.encodeBase64(ids[i]);
1617             final byte[] encodedUrlSafe = Base64.encodeBase64URLSafe(ids[i]);
1618             final byte[] decodedStandard = Base64.decodeBase64(standard[i]);
1619             final byte[] decodedUrlSafe1 = Base64.decodeBase64(urlSafe1[i]);
1620             final byte[] decodedUrlSafe2 = Base64.decodeBase64(urlSafe2[i]);
1621             final byte[] decodedUrlSafe3 = Base64.decodeBase64(urlSafe3[i]);
1622 
1623             // Very important debugging output should anyone
1624             // ever need to delve closely into this stuff.
1625 //            {
1626 //                System.out.println("reference: [" + Hex.encodeHexString(ids[i]) + "]");
1627 //                System.out.println("standard:  [" + Hex.encodeHexString(decodedStandard) + "] From: ["
1628 //                        + StringUtils.newStringUtf8(standard[i]) + "]");
1629 //                System.out.println("safe1:     [" + Hex.encodeHexString(decodedUrlSafe1) + "] From: ["
1630 //                        + StringUtils.newStringUtf8(urlSafe1[i]) + "]");
1631 //                System.out.println("safe2:     [" + Hex.encodeHexString(decodedUrlSafe2) + "] From: ["
1632 //                        + StringUtils.newStringUtf8(urlSafe2[i]) + "]");
1633 //                System.out.println("safe3:     [" + Hex.encodeHexString(decodedUrlSafe3) + "] From: ["
1634 //                        + StringUtils.newStringUtf8(urlSafe3[i]) + "]");
1635 //            }
1636 
1637             assertArrayEquals(encodedStandard, standard[i], "standard encode uuid");
1638             assertArrayEquals(encodedUrlSafe, urlSafe3[i], "url-safe encode uuid");
1639             assertArrayEquals(decodedStandard, ids[i], "standard decode uuid");
1640             assertArrayEquals(decodedUrlSafe1, ids[i], "url-safe1 decode uuid");
1641             assertArrayEquals(decodedUrlSafe2, ids[i], "url-safe2 decode uuid");
1642             assertArrayEquals(decodedUrlSafe3, ids[i], "url-safe3 decode uuid");
1643         }
1644     }
1645 
1646     private String toString(final byte[] data) {
1647         return org.apache.commons.lang3.StringUtils.join(data, ',');
1648     }
1649 }