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