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 testCustomEncodingAlphabetAllowsNonAsciiBytes() {
542         final byte[] encodeTable = STANDARD_ENCODE_TABLE.clone();
543         encodeTable[0] = (byte) 0x80;
544         final Base64 base64 = Base64.builder().setEncodeTable(encodeTable).get();
545         final byte[] data = { 0 };
546         final byte[] encoded = base64.encode(data);
547         assertArrayEquals(new byte[] { (byte) 0x80, (byte) 0x80 }, encoded);
548         assertTrue(base64.isInAlphabet(encoded, false));
549         assertArrayEquals(data, base64.decode(encoded));
550     }
551 
552     @Test
553     void testCustomEncodingAlphabetAllowsPaddingByteWhenPaddingChanges() {
554         final byte[] encodeTable = STANDARD_ENCODE_TABLE.clone();
555         encodeTable[0] = '=';
556         final Base64 base64 = Base64.builder().setEncodeTable(encodeTable).setPadding((byte) '.').get();
557         final byte[] data = { 0 };
558         assertArrayEquals(data, base64.decode(base64.encode(data)));
559     }
560 
561     @Test
562     void testCustomEncodingAlphabetRejectsConfiguredPaddingByte() {
563         final byte[] encodeTable = STANDARD_ENCODE_TABLE.clone();
564         encodeTable[0] = '=';
565         assertThrows(IllegalArgumentException.class, () -> Base64.builder().setEncodeTable(encodeTable).get());
566         assertThrows(IllegalArgumentException.class, () -> Base64.builder().setPadding((byte) 'A').get());
567     }
568 
569     @Test
570     void testCustomEncodingAlphabetRejectsDuplicateEntries() {
571         final byte[] encodeTable = STANDARD_ENCODE_TABLE.clone();
572         encodeTable[1] = encodeTable[0];
573         assertThrows(IllegalArgumentException.class, () -> Base64.builder().setEncodeTable(encodeTable));
574     }
575 
576     @Test
577     void testDecodeBase64DiffChars() {
578         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF"));
579         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64("Zm9vYmF+"));
580         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64("Zm9vYmF-"));
581         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
582     }
583 
584     @Test
585     void testDecodeBase64StandardDiffChars() {
586         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64Standard("Zm9vYmF"));
587         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64Standard("Zm9vYmF+"));
588         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64Standard("Zm9vYmF-"));
589         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
590     }
591 
592     @Test
593     void testDecodeBase64UrlDiffChars() {
594         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64UrlSafe("Zm9vYmF"));
595         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64UrlSafe("Zm9vYmF+"));
596         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97, 126 }, Base64.decodeBase64UrlSafe("Zm9vYmF-"));
597         assertArrayEquals(new byte[] { 102, 111, 111, 98, 97 }, Base64.decodeBase64("Zm9vYmF~"));
598     }
599 
600     private void testDecodeEncode(final String encodedText) {
601         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(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 testDecodeEncodeStandardByteArray(final String encodedText) {
619         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64Standard(encodedText.getBytes(CHARSET_UTF8)));
620         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
621         assertEquals(encodedText, encodedText2);
622     }
623 
624     @ParameterizedTest
625     @ValueSource(strings = {
626             "",
627             "Zg==",
628             "Zm8=",
629             "Zm9v",
630             "Zm9vYg==",
631             "Zm9vYmE=",
632             "Zm9vYmFy",
633             "Zm9vYmF+",
634             "Zm9vYmF/"
635     })
636     void testDecodeEncodeStandardString(final String encodedText) {
637         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64Standard(encodedText));
638         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
639         assertEquals(encodedText, encodedText2);
640     }
641 
642     @ParameterizedTest
643     @ValueSource(strings = {
644             "",
645             "Zg",
646             "Zm8",
647             "Zm9v",
648             "Zm9vYg",
649             "Zm9vYmE",
650             "Zm9vYmFy",
651             "Zm9vYmF-",
652             "Zm9vYmF_"
653     })
654     void testDecodeEncodeUrl(final String encodedText) {
655         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64UrlSafe(encodedText));
656         final String encodedText2 = Base64.encodeBase64URLSafeString(StringUtils.getBytesUtf8(decodedText));
657         assertEquals(encodedText, encodedText2);
658     }
659 
660 
661     @ParameterizedTest
662     @ValueSource(strings = {
663             "",
664             "Zg",
665             "Zm8",
666             "Zm9v",
667             "Zm9vYg",
668             "Zm9vYmE",
669             "Zm9vYmFy",
670             "Zm9vYmF-",
671             "Zm9vYmF_"
672     })
673     void testDecodeEncodeUrlSafeByteArray(final String encodedText) {
674         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64UrlSafe(encodedText.getBytes(CHARSET_UTF8)));
675         final String encodedText2 = Base64.encodeBase64URLSafeString(StringUtils.getBytesUtf8(decodedText));
676         assertEquals(encodedText, encodedText2);
677     }
678 
679     /**
680      * Tests conditional true branch for "marker0" test.
681      */
682     @Test
683     void testDecodePadMarkerIndex2() {
684         assertEquals("A", new String(Base64.decodeBase64("QQ==".getBytes(CHARSET_UTF8))));
685     }
686 
687     /**
688      * Tests conditional branches for "marker1" test.
689      */
690     @Test
691     void testDecodePadMarkerIndex3() {
692         assertEquals("AA", new String(Base64.decodeBase64("QUE=".getBytes(CHARSET_UTF8))));
693         assertEquals("AAA", new String(Base64.decodeBase64("QUFB".getBytes(CHARSET_UTF8))));
694     }
695 
696     @Test
697     void testDecodePadOnly() {
698         assertEquals(0, Base64.decodeBase64("====".getBytes(CHARSET_UTF8)).length);
699         assertEquals("", new String(Base64.decodeBase64("====".getBytes(CHARSET_UTF8))));
700         // Test truncated padding
701         assertEquals(0, Base64.decodeBase64("===".getBytes(CHARSET_UTF8)).length);
702         assertEquals(0, Base64.decodeBase64("==".getBytes(CHARSET_UTF8)).length);
703         assertEquals(0, Base64.decodeBase64("=".getBytes(CHARSET_UTF8)).length);
704         assertEquals(0, Base64.decodeBase64("".getBytes(CHARSET_UTF8)).length);
705     }
706 
707     @Test
708     void testDecodePadOnlyChunked() {
709         assertEquals(0, Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8)).length);
710         assertEquals("", new String(Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8))));
711         // Test truncated padding
712         assertEquals(0, Base64.decodeBase64("===\n".getBytes(CHARSET_UTF8)).length);
713         assertEquals(0, Base64.decodeBase64("==\n".getBytes(CHARSET_UTF8)).length);
714         assertEquals(0, Base64.decodeBase64("=\n".getBytes(CHARSET_UTF8)).length);
715         assertEquals(0, Base64.decodeBase64("\n".getBytes(CHARSET_UTF8)).length);
716     }
717 
718     /**
719      * Test our decode with pad character in the middle. (Our current
720      * implementation: halt decode and return what we've got so far).
721      *
722      * The point of this test is not to say
723      * "this is the correct way to decode base64." The point is simply to keep
724      * us aware of the current logic since 1.4 so we don't accidentally break it
725      * without realizing.
726      *
727      * Note for historians. The 1.3 logic would decode to:
728      * "Hello World\u0000Hello World" -- null in the middle --- and 1.4
729      * unwittingly changed it to current logic.
730      */
731     @Test
732     void testDecodeWithInnerPad() {
733         final String content = "SGVsbG8gV29ybGQ=SGVsbG8gV29ybGQ=";
734         final byte[] result = Base64.decodeBase64(content);
735         final byte[] shouldBe = StringUtils.getBytesUtf8("Hello World");
736         assertArrayEquals(result, shouldBe, "decode should halt at pad (=)");
737     }
738 
739     @Test
740     void testDecodeWithWhitespace() throws Exception {
741 
742         final String orig = "I am a late night coder.";
743 
744         final byte[] encodedArray = Base64.encodeBase64(orig.getBytes(CHARSET_UTF8));
745         final StringBuilder intermediate = new StringBuilder(new String(encodedArray));
746 
747         intermediate.insert(2, ' ');
748         intermediate.insert(5, '\t');
749         intermediate.insert(10, '\r');
750         intermediate.insert(15, '\n');
751 
752         final byte[] encodedWithWS = intermediate.toString().getBytes(CHARSET_UTF8);
753         final byte[] decodedWithWS = Base64.decodeBase64(encodedWithWS);
754 
755         final String dest = new String(decodedWithWS);
756 
757         assertEquals(orig, dest, "Dest string doesn't equal the original");
758     }
759 
760     /**
761      * Test encode and decode of empty byte array.
762      */
763     @Test
764     void testEmptyBase64() {
765         byte[] empty = {};
766         byte[] result = Base64.encodeBase64(empty);
767         assertEquals(0, result.length, "empty base64 encode");
768         assertNull(Base64.encodeBase64(null), "empty base64 encode");
769         result = new Base64().encode(empty, 0, 1);
770         assertEquals(0, result.length, "empty base64 encode");
771         assertNull(new Base64().encode(null, 0, 1), "empty base64 encode");
772 
773         empty = new byte[0];
774         result = Base64.decodeBase64(empty);
775         assertEquals(0, result.length, "empty base64 decode");
776         assertNull(Base64.decodeBase64((byte[]) null), "empty base64 encode");
777     }
778 
779     private void testEncodeDecode(final String plainText) {
780         final String encodedText = Base64.encodeBase64String(StringUtils.getBytesUtf8(plainText));
781         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(encodedText));
782         assertEquals(plainText, decodedText);
783     }
784 
785     // encode/decode a large random array
786     @Test
787     void testEncodeDecodeRandom() {
788         for (int i = 1; i < 5; i++) {
789             final byte[] data = new byte[getRandom().nextInt(10000) + 1];
790             getRandom().nextBytes(data);
791             final byte[] enc = Base64.encodeBase64(data);
792             assertTrue(Base64.isBase64(enc));
793             final byte[] data2 = Base64.decodeBase64(enc);
794             assertArrayEquals(data, data2);
795         }
796     }
797 
798     // encode/decode random arrays from size 0 to size 11
799     @Test
800     void testEncodeDecodeSmall() {
801         for (int i = 0; i < 12; i++) {
802             final byte[] data = new byte[i];
803             getRandom().nextBytes(data);
804             final byte[] enc = Base64.encodeBase64(data);
805             assertTrue(Base64.isBase64(enc), "\"" + new String(enc) + "\" is Base64 data.");
806             final byte[] data2 = Base64.decodeBase64(enc);
807             assertArrayEquals(data, data2, toString(data) + " equals " + toString(data2));
808         }
809     }
810 
811     @Test
812     void testEncodeOverMaxSize() throws Exception {
813         testEncodeOverMaxSize(-1);
814         testEncodeOverMaxSize(0);
815         testEncodeOverMaxSize(1);
816         testEncodeOverMaxSize(2);
817     }
818 
819     private void testEncodeOverMaxSize(final int maxSize) {
820         assertThrows(IllegalArgumentException.class, () -> Base64.encodeBase64(BaseNTestData.DECODED, true, false, maxSize));
821     }
822 
823     /**
824      * Tests a lineSeparator much bigger than DEFAULT_BUFFER_SIZE.
825      *
826      * @see "<a href='https://mail-archives.apache.org/mod_mbox/commons-dev/201202.mbox/%3C4F3C85D7.5060706@snafu.de%3E'>dev@commons.apache.org</a>"
827      */
828     @Test
829     void testHugeLineSeparator() {
830         final int BaseNCodec_DEFAULT_BUFFER_SIZE = 8192;
831         final int Base64_BYTES_PER_ENCODED_BLOCK = 4;
832         final byte[] baLineSeparator = new byte[BaseNCodec_DEFAULT_BUFFER_SIZE * 4 - 3];
833         final Base64 b64 = new Base64(Base64_BYTES_PER_ENCODED_BLOCK, baLineSeparator);
834         final String strOriginal = "Hello World";
835         final String strDecoded = new String(b64.decode(b64.encode(StringUtils.getBytesUtf8(strOriginal))));
836         assertEquals(strOriginal, strDecoded, "testDEFAULT_BUFFER_SIZE");
837     }
838 
839     @Test
840     void testIgnoringNonBase64InDecode() throws Exception {
841         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(FOX_BASE64.getBytes(CHARSET_UTF8))));
842     }
843 
844     @ParameterizedTest
845     @MethodSource("testIsBase64")
846     void testIsArrayByteBase64(final byte[] arrayOctet, final boolean match) {
847         assertEquals(match, Base64.isArrayByteBase64(arrayOctet));
848     }
849 
850     @ParameterizedTest
851     @MethodSource
852     void testIsBase64(final byte[] arrayOctet, final boolean match) {
853         assertEquals(match, Base64.isBase64(arrayOctet));
854     }
855 
856     @ParameterizedTest
857     @MethodSource("testIsBase64Url")
858     void testIsBase64Standard(final byte octet, final boolean match) {
859         assertEquals(match, Base64.isBase64Standard(octet));
860     }
861 
862     @ParameterizedTest
863     @MethodSource
864     void testIsBase64Url(final byte octet, final boolean match) {
865         assertEquals(match, Base64.isBase64Url(octet));
866     }
867 
868     /**
869      * Test the isStringBase64 method.
870      */
871     @Test
872     void testIsStringBase64() {
873         final String nullString = null;
874         final String emptyString = "";
875         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
876         final String invalidString = validString + (char) 0; // append null character
877         final String standardString = "++AQIDBA/U==";
878         final String urlSafeString = "--AQIDBA_U==";
879         assertThrows(NullPointerException.class, () -> Base64.isBase64(nullString), "Base64.isBase64() should not be null-safe.");
880         assertTrue(Base64.isBase64(emptyString), "Base64.isBase64(empty-string) is true");
881         assertTrue(Base64.isBase64(validString), "Base64.isBase64(valid-string) is true");
882         assertFalse(Base64.isBase64(invalidString), "Base64.isBase64(invalid-string) is false");
883         assertTrue(Base64.isBase64(standardString), "Base64.isBase64(standard-string) is true");
884         assertTrue(Base64.isBase64(urlSafeString), "Base64.isBase64(urlSafe-string) is true");
885     }
886 
887     /**
888      * Test the isStringBase64Standard method.
889      */
890     @Test
891     void testIsStringBase64Standard() {
892         final String nullString = null;
893         final String emptyString = "";
894         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
895         final String invalidString = validString + (char) 0; // append null character
896         final String standardString = "++AQIDBA/U==";
897         final String urlSafeString = "--AQIDBA_U==";
898         assertThrows(NullPointerException.class, () -> Base64.isBase64Standard(nullString), "Base64.isBase64Standard() should not be null-safe.");
899         assertTrue(Base64.isBase64Standard(emptyString), "Base64.isBase64Standard(empty-string) is true");
900         assertTrue(Base64.isBase64Standard(validString), "Base64.isBase64Standard(valid-string) is true");
901         assertFalse(Base64.isBase64Standard(invalidString), "Base64.isBase64Standard(invalid-string) is false");
902         assertTrue(Base64.isBase64Standard(standardString), "Base64.isBase64Standard(standard-string) is true");
903         assertFalse(Base64.isBase64Standard(urlSafeString), "Base64.isBase64Standard(urlSafe-string) is false");
904     }
905 
906     /**
907      * Test the isStringBase64Url method.
908      */
909     @Test
910     void testIsStringBase64Url() {
911         final String nullString = null;
912         final String emptyString = "";
913         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
914         final String invalidString = validString + (char) 0; // append null character
915         final String standardString = "++AQIDBA/U==";
916         final String urlSafeString = "--AQIDBA_U==";
917         assertThrows(NullPointerException.class, () -> Base64.isBase64Url(nullString), "Base64.isBase64Url() should not be null-safe.");
918         assertTrue(Base64.isBase64Url(emptyString), "Base64.isBase64Url(empty-string) is true");
919         assertTrue(Base64.isBase64Url(validString), "Base64.isBase64Url(valid-string) is true");
920         assertFalse(Base64.isBase64Url(invalidString), "Base64.isBase64Url(invalid-string) is false");
921         assertFalse(Base64.isBase64Url(standardString), "Base64.isBase64Url(standard-string) is false");
922         assertTrue(Base64.isBase64Url(urlSafeString), "Base64.isBase64Url(urlSafe-string) is true");
923     }
924 
925     /**
926      * Tests isUrlSafe.
927      */
928     @Test
929     void testIsUrlSafe() {
930         final Base64 base64Standard = new Base64(false);
931         final Base64 base64URLSafe = new Base64(true);
932 
933         assertFalse(base64Standard.isUrlSafe(), "Base64.isUrlSafe=false");
934         assertTrue(base64URLSafe.isUrlSafe(), "Base64.isUrlSafe=true");
935 
936         final byte[] whiteSpace = { ' ', '\n', '\r', '\t' };
937         assertTrue(Base64.isBase64(whiteSpace), "Base64.isBase64(whiteSpace)=true");
938     }
939 
940     @Test
941     void testKnownDecodings() {
942         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(
943                 "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==".getBytes(CHARSET_UTF8))));
944         assertEquals("It was the best of times, it was the worst of times.", new String(Base64.decodeBase64(
945                 "SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==".getBytes(CHARSET_UTF8))));
946         assertEquals("http://jakarta.apache.org/commmons", new String(
947                 Base64.decodeBase64("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==".getBytes(CHARSET_UTF8))));
948         assertEquals("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", new String(Base64.decodeBase64(
949                 "QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==".getBytes(CHARSET_UTF8))));
950         assertEquals("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }",
951                 new String(Base64.decodeBase64("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=".getBytes(CHARSET_UTF8))));
952         assertEquals("xyzzy!", new String(Base64.decodeBase64("eHl6enkh".getBytes(CHARSET_UTF8))));
953     }
954 
955     @Test
956     void testKnownEncodings() {
957         assertEquals("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==", new String(
958                 Base64.encodeBase64(FOX_TEXT.getBytes(CHARSET_UTF8))));
959         assertEquals(
960                 "YmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJs\r\nYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFo\r\nIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBi\r\nbGFoIGJsYWg=\r\n",
961                 new String(Base64.encodeBase64Chunked(
962                         "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"
963                                 .getBytes(CHARSET_UTF8))));
964         assertEquals("SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==", new String(
965                 Base64.encodeBase64("It was the best of times, it was the worst of times.".getBytes(CHARSET_UTF8))));
966         assertEquals("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==",
967                 new String(Base64.encodeBase64("http://jakarta.apache.org/commmons".getBytes(CHARSET_UTF8))));
968         assertEquals("QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==", new String(
969                 Base64.encodeBase64("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".getBytes(CHARSET_UTF8))));
970         assertEquals("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=",
971                 new String(Base64.encodeBase64("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }".getBytes(CHARSET_UTF8))));
972         assertEquals("eHl6enkh", new String(Base64.encodeBase64("xyzzy!".getBytes(CHARSET_UTF8))));
973     }
974 
975     @Test
976     void testNonBase64Test() throws Exception {
977         final byte[] bArray = { '%' };
978         assertFalse(Base64.isBase64(bArray), "Invalid Base64 array was incorrectly validated as an array of Base64 encoded data");
979         try {
980             final Base64 b64 = new Base64();
981             final byte[] result = b64.decode(bArray);
982             assertEquals(0, result.length, "The result should be empty as the test encoded content did not contain any valid base 64 characters");
983         } catch (final Exception e) {
984             fail("Exception '" + e.getClass().getName() + "' was thrown when trying to decode invalid base64 encoded data - RFC 2045 requires that all " +
985                     "non base64 character be discarded, an exception should not have been thrown");
986         }
987     }
988 
989     @Test
990     void testObjectDecodeWithInvalidParameter() {
991         assertThrows(DecoderException.class, () -> new Base64().decode(Integer.valueOf(5)),
992                 "decode(Object) didn't throw an exception when passed an Integer object");
993     }
994 
995     @Test
996     void testObjectDecodeWithValidParameter() throws Exception {
997         final String original = "Hello World!";
998         final Object o = Base64.encodeBase64(original.getBytes(CHARSET_UTF8));
999         final Base64 b64 = new Base64();
1000         final Object oDecoded = b64.decode(o);
1001         final byte[] baDecoded = (byte[]) oDecoded;
1002         final String dest = new String(baDecoded);
1003         assertEquals(original, dest, "dest string does not equal original");
1004     }
1005 
1006     @Test
1007     void testObjectEncode() throws Exception {
1008         final Base64 b64 = new Base64();
1009         assertEquals("SGVsbG8gV29ybGQ=", new String(b64.encode("Hello World".getBytes(CHARSET_UTF8))));
1010     }
1011 
1012     @Test
1013     void testObjectEncodeWithInvalidParameter() {
1014         assertThrows(EncoderException.class, () -> new Base64().encode("Yadayadayada"), "encode(Object) didn't throw an exception when passed a String object");
1015     }
1016 
1017     @Test
1018     void testObjectEncodeWithValidParameter() throws Exception {
1019         final String original = "Hello World!";
1020         final Object origObj = original.getBytes(CHARSET_UTF8);
1021         final Base64 b64 = new Base64();
1022         final Object oEncoded = b64.encode(origObj);
1023         final byte[] bArray = Base64.decodeBase64((byte[]) oEncoded);
1024         final String dest = new String(bArray);
1025         assertEquals(original, dest, "dest string does not equal original");
1026     }
1027 
1028     @Test
1029     void testPairs() {
1030         assertEquals("AAA=", new String(Base64.encodeBase64(new byte[] { 0, 0 })));
1031         for (int i = -128; i <= 127; i++) {
1032             final byte[] test = { (byte) i, (byte) i };
1033             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
1034         }
1035     }
1036 
1037     /**
1038      * Tests RFC 1421 section 4.3.2.4 chuck size definition.
1039      */
1040     @Test
1041     void testRfc1421Section6Dot8ChunkSizeDefinition() {
1042         assertEquals(64, BaseNCodec.PEM_CHUNK_SIZE);
1043     }
1044 
1045     /**
1046      * Tests RFC 2045 section 2.1 CRLF definition.
1047      */
1048     @Test
1049     void testRfc2045Section2Dot1CrLfDefinition() {
1050         assertArrayEquals(new byte[]{13, 10}, BaseNCodec.CHUNK_SEPARATOR);
1051     }
1052 
1053     /**
1054      * Tests RFC 2045 section 6.8 chuck size definition.
1055      */
1056     @Test
1057     void testRfc2045Section6Dot8ChunkSizeDefinition() {
1058         assertEquals(76, BaseNCodec.MIME_CHUNK_SIZE);
1059     }
1060 
1061     /**
1062      * Tests RFC 4648 section 10 test vectors.
1063      * <ul>
1064      * <li>BASE64("") = ""</li>
1065      * <li>BASE64("f") = "Zg=="</li>
1066      * <li>BASE64("fo") = "Zm8="</li>
1067      * <li>BASE64("foo") = "Zm9v"</li>
1068      * <li>BASE64("foob") = "Zm9vYg=="</li>
1069      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1070      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1071      * </ul>
1072      *
1073      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1074      *      html/rfc4648</a>
1075      */
1076     @Test
1077     void testRfc4648Section10Decode() {
1078         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("")));
1079         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==")));
1080         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=")));
1081         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v")));
1082         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==")));
1083         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=")));
1084         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy")));
1085     }
1086 
1087     /**
1088      * Tests RFC 4648 section 10 test vectors.
1089      * <ul>
1090      * <li>BASE64("") = ""</li>
1091      * <li>BASE64("f") = "Zg=="</li>
1092      * <li>BASE64("fo") = "Zm8="</li>
1093      * <li>BASE64("foo") = "Zm9v"</li>
1094      * <li>BASE64("foob") = "Zm9vYg=="</li>
1095      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1096      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1097      * </ul>
1098      *
1099      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1100      *      html/rfc4648</a>
1101      */
1102     @ParameterizedTest
1103     // @formatter:off
1104     @ValueSource(strings = {
1105             "",
1106             "Zg==",
1107             "Zm8=",
1108             "Zm9v",
1109             "Zm9vYg==",
1110             "Zm9vYmE=",
1111             "Zm9vYmFy"
1112     })
1113     // @formatter:on
1114     void testRfc4648Section10DecodeEncode(final String input) {
1115         testDecodeEncode(input);
1116     }
1117 
1118     /**
1119      * Tests RFC 4648 section 10 test vectors.
1120      * <ul>
1121      * <li>BASE64("") = ""</li>
1122      * <li>BASE64("f") = "Zg=="</li>
1123      * <li>BASE64("fo") = "Zm8="</li>
1124      * <li>BASE64("foo") = "Zm9v"</li>
1125      * <li>BASE64("foob") = "Zm9vYg=="</li>
1126      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1127      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1128      * </ul>
1129      *
1130      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1131      *      html/rfc4648</a>
1132      */
1133     @Test
1134     void testRfc4648Section10DecodeWithCrLf() {
1135         final String CRLF = StringUtils.newStringUsAscii(BaseNCodec.CHUNK_SEPARATOR);
1136         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("" + CRLF)));
1137         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==" + CRLF)));
1138         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=" + CRLF)));
1139         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v" + CRLF)));
1140         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==" + CRLF)));
1141         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=" + CRLF)));
1142         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy" + CRLF)));
1143     }
1144 
1145     /**
1146      * Tests RFC 4648 section 10 test vectors.
1147      * <ul>
1148      * <li>BASE64("") = ""</li>
1149      * <li>BASE64("f") = "Zg=="</li>
1150      * <li>BASE64("fo") = "Zm8="</li>
1151      * <li>BASE64("foo") = "Zm9v"</li>
1152      * <li>BASE64("foob") = "Zm9vYg=="</li>
1153      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1154      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1155      * </ul>
1156      *
1157      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1158      *      html/rfc4648</a>
1159      */
1160     @Test
1161     void testRfc4648Section10Encode() {
1162         assertEquals("", Base64.encodeBase64String(StringUtils.getBytesUtf8("")));
1163         assertEquals("Zg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("f")));
1164         assertEquals("Zm8=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fo")));
1165         assertEquals("Zm9v", Base64.encodeBase64String(StringUtils.getBytesUtf8("foo")));
1166         assertEquals("Zm9vYg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("foob")));
1167         assertEquals("Zm9vYmE=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fooba")));
1168         assertEquals("Zm9vYmFy", Base64.encodeBase64String(StringUtils.getBytesUtf8("foobar")));
1169     }
1170 
1171     /**
1172      * Tests RFC 4648 section 10 test vectors.
1173      * <ul>
1174      * <li>BASE64("") = ""</li>
1175      * <li>BASE64("f") = "Zg=="</li>
1176      * <li>BASE64("fo") = "Zm8="</li>
1177      * <li>BASE64("foo") = "Zm9v"</li>
1178      * <li>BASE64("foob") = "Zm9vYg=="</li>
1179      * <li>BASE64("fooba") = "Zm9vYmE="</li>
1180      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
1181      * </ul>
1182      *
1183      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1184      *      html/rfc4648</a>
1185      */
1186     @ParameterizedTest
1187     // @formatter:off
1188     @ValueSource(strings = {
1189             "",
1190             "f",
1191             "fo",
1192             "foo",
1193             "foob",
1194             "fooba",
1195             "foobar",
1196     })
1197     // @formatter:on
1198     void testRfc4648Section10EncodeDecode(final String input) {
1199         testEncodeDecode(input);
1200     }
1201 
1202     @Test
1203     void testSingletons() {
1204         assertEquals("AA==", new String(Base64.encodeBase64(new byte[] { (byte) 0 })));
1205         assertEquals("AQ==", new String(Base64.encodeBase64(new byte[] { (byte) 1 })));
1206         assertEquals("Ag==", new String(Base64.encodeBase64(new byte[] { (byte) 2 })));
1207         assertEquals("Aw==", new String(Base64.encodeBase64(new byte[] { (byte) 3 })));
1208         assertEquals("BA==", new String(Base64.encodeBase64(new byte[] { (byte) 4 })));
1209         assertEquals("BQ==", new String(Base64.encodeBase64(new byte[] { (byte) 5 })));
1210         assertEquals("Bg==", new String(Base64.encodeBase64(new byte[] { (byte) 6 })));
1211         assertEquals("Bw==", new String(Base64.encodeBase64(new byte[] { (byte) 7 })));
1212         assertEquals("CA==", new String(Base64.encodeBase64(new byte[] { (byte) 8 })));
1213         assertEquals("CQ==", new String(Base64.encodeBase64(new byte[] { (byte) 9 })));
1214         assertEquals("Cg==", new String(Base64.encodeBase64(new byte[] { (byte) 10 })));
1215         assertEquals("Cw==", new String(Base64.encodeBase64(new byte[] { (byte) 11 })));
1216         assertEquals("DA==", new String(Base64.encodeBase64(new byte[] { (byte) 12 })));
1217         assertEquals("DQ==", new String(Base64.encodeBase64(new byte[] { (byte) 13 })));
1218         assertEquals("Dg==", new String(Base64.encodeBase64(new byte[] { (byte) 14 })));
1219         assertEquals("Dw==", new String(Base64.encodeBase64(new byte[] { (byte) 15 })));
1220         assertEquals("EA==", new String(Base64.encodeBase64(new byte[] { (byte) 16 })));
1221         assertEquals("EQ==", new String(Base64.encodeBase64(new byte[] { (byte) 17 })));
1222         assertEquals("Eg==", new String(Base64.encodeBase64(new byte[] { (byte) 18 })));
1223         assertEquals("Ew==", new String(Base64.encodeBase64(new byte[] { (byte) 19 })));
1224         assertEquals("FA==", new String(Base64.encodeBase64(new byte[] { (byte) 20 })));
1225         assertEquals("FQ==", new String(Base64.encodeBase64(new byte[] { (byte) 21 })));
1226         assertEquals("Fg==", new String(Base64.encodeBase64(new byte[] { (byte) 22 })));
1227         assertEquals("Fw==", new String(Base64.encodeBase64(new byte[] { (byte) 23 })));
1228         assertEquals("GA==", new String(Base64.encodeBase64(new byte[] { (byte) 24 })));
1229         assertEquals("GQ==", new String(Base64.encodeBase64(new byte[] { (byte) 25 })));
1230         assertEquals("Gg==", new String(Base64.encodeBase64(new byte[] { (byte) 26 })));
1231         assertEquals("Gw==", new String(Base64.encodeBase64(new byte[] { (byte) 27 })));
1232         assertEquals("HA==", new String(Base64.encodeBase64(new byte[] { (byte) 28 })));
1233         assertEquals("HQ==", new String(Base64.encodeBase64(new byte[] { (byte) 29 })));
1234         assertEquals("Hg==", new String(Base64.encodeBase64(new byte[] { (byte) 30 })));
1235         assertEquals("Hw==", new String(Base64.encodeBase64(new byte[] { (byte) 31 })));
1236         assertEquals("IA==", new String(Base64.encodeBase64(new byte[] { (byte) 32 })));
1237         assertEquals("IQ==", new String(Base64.encodeBase64(new byte[] { (byte) 33 })));
1238         assertEquals("Ig==", new String(Base64.encodeBase64(new byte[] { (byte) 34 })));
1239         assertEquals("Iw==", new String(Base64.encodeBase64(new byte[] { (byte) 35 })));
1240         assertEquals("JA==", new String(Base64.encodeBase64(new byte[] { (byte) 36 })));
1241         assertEquals("JQ==", new String(Base64.encodeBase64(new byte[] { (byte) 37 })));
1242         assertEquals("Jg==", new String(Base64.encodeBase64(new byte[] { (byte) 38 })));
1243         assertEquals("Jw==", new String(Base64.encodeBase64(new byte[] { (byte) 39 })));
1244         assertEquals("KA==", new String(Base64.encodeBase64(new byte[] { (byte) 40 })));
1245         assertEquals("KQ==", new String(Base64.encodeBase64(new byte[] { (byte) 41 })));
1246         assertEquals("Kg==", new String(Base64.encodeBase64(new byte[] { (byte) 42 })));
1247         assertEquals("Kw==", new String(Base64.encodeBase64(new byte[] { (byte) 43 })));
1248         assertEquals("LA==", new String(Base64.encodeBase64(new byte[] { (byte) 44 })));
1249         assertEquals("LQ==", new String(Base64.encodeBase64(new byte[] { (byte) 45 })));
1250         assertEquals("Lg==", new String(Base64.encodeBase64(new byte[] { (byte) 46 })));
1251         assertEquals("Lw==", new String(Base64.encodeBase64(new byte[] { (byte) 47 })));
1252         assertEquals("MA==", new String(Base64.encodeBase64(new byte[] { (byte) 48 })));
1253         assertEquals("MQ==", new String(Base64.encodeBase64(new byte[] { (byte) 49 })));
1254         assertEquals("Mg==", new String(Base64.encodeBase64(new byte[] { (byte) 50 })));
1255         assertEquals("Mw==", new String(Base64.encodeBase64(new byte[] { (byte) 51 })));
1256         assertEquals("NA==", new String(Base64.encodeBase64(new byte[] { (byte) 52 })));
1257         assertEquals("NQ==", new String(Base64.encodeBase64(new byte[] { (byte) 53 })));
1258         assertEquals("Ng==", new String(Base64.encodeBase64(new byte[] { (byte) 54 })));
1259         assertEquals("Nw==", new String(Base64.encodeBase64(new byte[] { (byte) 55 })));
1260         assertEquals("OA==", new String(Base64.encodeBase64(new byte[] { (byte) 56 })));
1261         assertEquals("OQ==", new String(Base64.encodeBase64(new byte[] { (byte) 57 })));
1262         assertEquals("Og==", new String(Base64.encodeBase64(new byte[] { (byte) 58 })));
1263         assertEquals("Ow==", new String(Base64.encodeBase64(new byte[] { (byte) 59 })));
1264         assertEquals("PA==", new String(Base64.encodeBase64(new byte[] { (byte) 60 })));
1265         assertEquals("PQ==", new String(Base64.encodeBase64(new byte[] { (byte) 61 })));
1266         assertEquals("Pg==", new String(Base64.encodeBase64(new byte[] { (byte) 62 })));
1267         assertEquals("Pw==", new String(Base64.encodeBase64(new byte[] { (byte) 63 })));
1268         assertEquals("QA==", new String(Base64.encodeBase64(new byte[] { (byte) 64 })));
1269         assertEquals("QQ==", new String(Base64.encodeBase64(new byte[] { (byte) 65 })));
1270         assertEquals("Qg==", new String(Base64.encodeBase64(new byte[] { (byte) 66 })));
1271         assertEquals("Qw==", new String(Base64.encodeBase64(new byte[] { (byte) 67 })));
1272         assertEquals("RA==", new String(Base64.encodeBase64(new byte[] { (byte) 68 })));
1273         assertEquals("RQ==", new String(Base64.encodeBase64(new byte[] { (byte) 69 })));
1274         assertEquals("Rg==", new String(Base64.encodeBase64(new byte[] { (byte) 70 })));
1275         assertEquals("Rw==", new String(Base64.encodeBase64(new byte[] { (byte) 71 })));
1276         assertEquals("SA==", new String(Base64.encodeBase64(new byte[] { (byte) 72 })));
1277         assertEquals("SQ==", new String(Base64.encodeBase64(new byte[] { (byte) 73 })));
1278         assertEquals("Sg==", new String(Base64.encodeBase64(new byte[] { (byte) 74 })));
1279         assertEquals("Sw==", new String(Base64.encodeBase64(new byte[] { (byte) 75 })));
1280         assertEquals("TA==", new String(Base64.encodeBase64(new byte[] { (byte) 76 })));
1281         assertEquals("TQ==", new String(Base64.encodeBase64(new byte[] { (byte) 77 })));
1282         assertEquals("Tg==", new String(Base64.encodeBase64(new byte[] { (byte) 78 })));
1283         assertEquals("Tw==", new String(Base64.encodeBase64(new byte[] { (byte) 79 })));
1284         assertEquals("UA==", new String(Base64.encodeBase64(new byte[] { (byte) 80 })));
1285         assertEquals("UQ==", new String(Base64.encodeBase64(new byte[] { (byte) 81 })));
1286         assertEquals("Ug==", new String(Base64.encodeBase64(new byte[] { (byte) 82 })));
1287         assertEquals("Uw==", new String(Base64.encodeBase64(new byte[] { (byte) 83 })));
1288         assertEquals("VA==", new String(Base64.encodeBase64(new byte[] { (byte) 84 })));
1289         assertEquals("VQ==", new String(Base64.encodeBase64(new byte[] { (byte) 85 })));
1290         assertEquals("Vg==", new String(Base64.encodeBase64(new byte[] { (byte) 86 })));
1291         assertEquals("Vw==", new String(Base64.encodeBase64(new byte[] { (byte) 87 })));
1292         assertEquals("WA==", new String(Base64.encodeBase64(new byte[] { (byte) 88 })));
1293         assertEquals("WQ==", new String(Base64.encodeBase64(new byte[] { (byte) 89 })));
1294         assertEquals("Wg==", new String(Base64.encodeBase64(new byte[] { (byte) 90 })));
1295         assertEquals("Ww==", new String(Base64.encodeBase64(new byte[] { (byte) 91 })));
1296         assertEquals("XA==", new String(Base64.encodeBase64(new byte[] { (byte) 92 })));
1297         assertEquals("XQ==", new String(Base64.encodeBase64(new byte[] { (byte) 93 })));
1298         assertEquals("Xg==", new String(Base64.encodeBase64(new byte[] { (byte) 94 })));
1299         assertEquals("Xw==", new String(Base64.encodeBase64(new byte[] { (byte) 95 })));
1300         assertEquals("YA==", new String(Base64.encodeBase64(new byte[] { (byte) 96 })));
1301         assertEquals("YQ==", new String(Base64.encodeBase64(new byte[] { (byte) 97 })));
1302         assertEquals("Yg==", new String(Base64.encodeBase64(new byte[] { (byte) 98 })));
1303         assertEquals("Yw==", new String(Base64.encodeBase64(new byte[] { (byte) 99 })));
1304         assertEquals("ZA==", new String(Base64.encodeBase64(new byte[] { (byte) 100 })));
1305         assertEquals("ZQ==", new String(Base64.encodeBase64(new byte[] { (byte) 101 })));
1306         assertEquals("Zg==", new String(Base64.encodeBase64(new byte[] { (byte) 102 })));
1307         assertEquals("Zw==", new String(Base64.encodeBase64(new byte[] { (byte) 103 })));
1308         assertEquals("aA==", new String(Base64.encodeBase64(new byte[] { (byte) 104 })));
1309         for (int i = -128; i <= 127; i++) {
1310             final byte[] test = { (byte) i };
1311             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
1312         }
1313     }
1314 
1315     @Test
1316     void testSingletonsChunked() {
1317         assertEquals("AA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0 })));
1318         assertEquals("AQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 1 })));
1319         assertEquals("Ag==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 2 })));
1320         assertEquals("Aw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 3 })));
1321         assertEquals("BA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 4 })));
1322         assertEquals("BQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 5 })));
1323         assertEquals("Bg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 6 })));
1324         assertEquals("Bw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 7 })));
1325         assertEquals("CA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 8 })));
1326         assertEquals("CQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 9 })));
1327         assertEquals("Cg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 10 })));
1328         assertEquals("Cw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 11 })));
1329         assertEquals("DA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 12 })));
1330         assertEquals("DQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 13 })));
1331         assertEquals("Dg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 14 })));
1332         assertEquals("Dw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 15 })));
1333         assertEquals("EA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 16 })));
1334         assertEquals("EQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 17 })));
1335         assertEquals("Eg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 18 })));
1336         assertEquals("Ew==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 19 })));
1337         assertEquals("FA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 20 })));
1338         assertEquals("FQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 21 })));
1339         assertEquals("Fg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 22 })));
1340         assertEquals("Fw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 23 })));
1341         assertEquals("GA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 24 })));
1342         assertEquals("GQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 25 })));
1343         assertEquals("Gg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 26 })));
1344         assertEquals("Gw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 27 })));
1345         assertEquals("HA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 28 })));
1346         assertEquals("HQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 29 })));
1347         assertEquals("Hg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 30 })));
1348         assertEquals("Hw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 31 })));
1349         assertEquals("IA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 32 })));
1350         assertEquals("IQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 33 })));
1351         assertEquals("Ig==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 34 })));
1352         assertEquals("Iw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 35 })));
1353         assertEquals("JA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 36 })));
1354         assertEquals("JQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 37 })));
1355         assertEquals("Jg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 38 })));
1356         assertEquals("Jw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 39 })));
1357         assertEquals("KA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 40 })));
1358         assertEquals("KQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 41 })));
1359         assertEquals("Kg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 42 })));
1360         assertEquals("Kw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 43 })));
1361         assertEquals("LA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 44 })));
1362         assertEquals("LQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 45 })));
1363         assertEquals("Lg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 46 })));
1364         assertEquals("Lw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 47 })));
1365         assertEquals("MA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 48 })));
1366         assertEquals("MQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 49 })));
1367         assertEquals("Mg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 50 })));
1368         assertEquals("Mw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 51 })));
1369         assertEquals("NA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 52 })));
1370         assertEquals("NQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 53 })));
1371         assertEquals("Ng==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 54 })));
1372         assertEquals("Nw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 55 })));
1373         assertEquals("OA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 56 })));
1374         assertEquals("OQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 57 })));
1375         assertEquals("Og==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 58 })));
1376         assertEquals("Ow==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 59 })));
1377         assertEquals("PA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 60 })));
1378         assertEquals("PQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 61 })));
1379         assertEquals("Pg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 62 })));
1380         assertEquals("Pw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 63 })));
1381         assertEquals("QA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 64 })));
1382         assertEquals("QQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 65 })));
1383         assertEquals("Qg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 66 })));
1384         assertEquals("Qw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 67 })));
1385         assertEquals("RA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 68 })));
1386         assertEquals("RQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 69 })));
1387         assertEquals("Rg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 70 })));
1388         assertEquals("Rw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 71 })));
1389         assertEquals("SA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 72 })));
1390         assertEquals("SQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 73 })));
1391         assertEquals("Sg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 74 })));
1392         assertEquals("Sw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 75 })));
1393         assertEquals("TA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 76 })));
1394         assertEquals("TQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 77 })));
1395         assertEquals("Tg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 78 })));
1396         assertEquals("Tw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 79 })));
1397         assertEquals("UA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 80 })));
1398         assertEquals("UQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 81 })));
1399         assertEquals("Ug==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 82 })));
1400         assertEquals("Uw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 83 })));
1401         assertEquals("VA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 84 })));
1402         assertEquals("VQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 85 })));
1403         assertEquals("Vg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 86 })));
1404         assertEquals("Vw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 87 })));
1405         assertEquals("WA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 88 })));
1406         assertEquals("WQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 89 })));
1407         assertEquals("Wg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 90 })));
1408         assertEquals("Ww==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 91 })));
1409         assertEquals("XA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 92 })));
1410         assertEquals("XQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 93 })));
1411         assertEquals("Xg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 94 })));
1412         assertEquals("Xw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 95 })));
1413         assertEquals("YA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 96 })));
1414         assertEquals("YQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 97 })));
1415         assertEquals("Yg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 98 })));
1416         assertEquals("Yw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 99 })));
1417         assertEquals("ZA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 100 })));
1418         assertEquals("ZQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 101 })));
1419         assertEquals("Zg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 102 })));
1420         assertEquals("Zw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 103 })));
1421         assertEquals("aA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 104 })));
1422     }
1423 
1424     @Test
1425     void testStringToByteVariations() throws DecoderException {
1426         final Base64 base64 = new Base64();
1427         final String s1 = "SGVsbG8gV29ybGQ=\r\n";
1428         final String s2 = "";
1429         final String s3 = null;
1430         final String s4a = "K/fMJwH+Q5e0nr7tWsxwkA==\r\n";
1431         final String s4b = "K_fMJwH-Q5e0nr7tWsxwkA";
1432         final byte[] b4 = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090"); // for url-safe tests
1433         assertEquals("Hello World", StringUtils.newStringUtf8(base64.decode(s1)), "StringToByte Hello World");
1434         assertEquals("Hello World", StringUtils.newStringUtf8((byte[]) base64.decode((Object) s1)), "StringToByte Hello World");
1435         assertEquals("Hello World", StringUtils.newStringUtf8(Base64.decodeBase64(s1)), "StringToByte static Hello World");
1436         assertEquals("", StringUtils.newStringUtf8(base64.decode(s2)), "StringToByte \"\"");
1437         assertEquals("", StringUtils.newStringUtf8(Base64.decodeBase64(s2)), "StringToByte static \"\"");
1438         assertNull(StringUtils.newStringUtf8(base64.decode(s3)), "StringToByte null");
1439         assertNull(StringUtils.newStringUtf8(Base64.decodeBase64(s3)), "StringToByte static null");
1440         assertArrayEquals(b4, base64.decode(s4b), "StringToByte UUID");
1441         assertArrayEquals(b4, Base64.decodeBase64(s4a), "StringToByte static UUID");
1442         assertArrayEquals(b4, Base64.decodeBase64(s4b), "StringToByte static-url-safe UUID");
1443     }
1444 
1445     @Test
1446     void testTriplets() {
1447         assertEquals("AAAA", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1448         assertEquals("AAAB", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1449         assertEquals("AAAC", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1450         assertEquals("AAAD", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1451         assertEquals("AAAE", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1452         assertEquals("AAAF", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1453         assertEquals("AAAG", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1454         assertEquals("AAAH", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1455         assertEquals("AAAI", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1456         assertEquals("AAAJ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1457         assertEquals("AAAK", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1458         assertEquals("AAAL", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1459         assertEquals("AAAM", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1460         assertEquals("AAAN", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1461         assertEquals("AAAO", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1462         assertEquals("AAAP", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1463         assertEquals("AAAQ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1464         assertEquals("AAAR", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1465         assertEquals("AAAS", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1466         assertEquals("AAAT", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1467         assertEquals("AAAU", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1468         assertEquals("AAAV", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1469         assertEquals("AAAW", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1470         assertEquals("AAAX", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1471         assertEquals("AAAY", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1472         assertEquals("AAAZ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1473         assertEquals("AAAa", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1474         assertEquals("AAAb", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1475         assertEquals("AAAc", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1476         assertEquals("AAAd", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1477         assertEquals("AAAe", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1478         assertEquals("AAAf", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1479         assertEquals("AAAg", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1480         assertEquals("AAAh", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1481         assertEquals("AAAi", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1482         assertEquals("AAAj", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1483         assertEquals("AAAk", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1484         assertEquals("AAAl", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1485         assertEquals("AAAm", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1486         assertEquals("AAAn", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1487         assertEquals("AAAo", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1488         assertEquals("AAAp", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1489         assertEquals("AAAq", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1490         assertEquals("AAAr", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1491         assertEquals("AAAs", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1492         assertEquals("AAAt", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1493         assertEquals("AAAu", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1494         assertEquals("AAAv", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1495         assertEquals("AAAw", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1496         assertEquals("AAAx", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1497         assertEquals("AAAy", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1498         assertEquals("AAAz", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1499         assertEquals("AAA0", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1500         assertEquals("AAA1", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1501         assertEquals("AAA2", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1502         assertEquals("AAA3", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1503         assertEquals("AAA4", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1504         assertEquals("AAA5", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1505         assertEquals("AAA6", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1506         assertEquals("AAA7", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1507         assertEquals("AAA8", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1508         assertEquals("AAA9", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1509         assertEquals("AAA+", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1510         assertEquals("AAA/", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1511     }
1512 
1513     @Test
1514     void testTripletsChunked() {
1515         assertEquals("AAAA\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1516         assertEquals("AAAB\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1517         assertEquals("AAAC\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1518         assertEquals("AAAD\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1519         assertEquals("AAAE\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1520         assertEquals("AAAF\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1521         assertEquals("AAAG\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1522         assertEquals("AAAH\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1523         assertEquals("AAAI\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1524         assertEquals("AAAJ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1525         assertEquals("AAAK\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1526         assertEquals("AAAL\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1527         assertEquals("AAAM\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1528         assertEquals("AAAN\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1529         assertEquals("AAAO\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1530         assertEquals("AAAP\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1531         assertEquals("AAAQ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1532         assertEquals("AAAR\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1533         assertEquals("AAAS\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1534         assertEquals("AAAT\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1535         assertEquals("AAAU\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1536         assertEquals("AAAV\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1537         assertEquals("AAAW\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1538         assertEquals("AAAX\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1539         assertEquals("AAAY\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1540         assertEquals("AAAZ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1541         assertEquals("AAAa\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1542         assertEquals("AAAb\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1543         assertEquals("AAAc\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1544         assertEquals("AAAd\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1545         assertEquals("AAAe\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1546         assertEquals("AAAf\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1547         assertEquals("AAAg\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1548         assertEquals("AAAh\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1549         assertEquals("AAAi\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1550         assertEquals("AAAj\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1551         assertEquals("AAAk\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1552         assertEquals("AAAl\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1553         assertEquals("AAAm\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1554         assertEquals("AAAn\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1555         assertEquals("AAAo\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1556         assertEquals("AAAp\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1557         assertEquals("AAAq\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1558         assertEquals("AAAr\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1559         assertEquals("AAAs\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1560         assertEquals("AAAt\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1561         assertEquals("AAAu\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1562         assertEquals("AAAv\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1563         assertEquals("AAAw\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1564         assertEquals("AAAx\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1565         assertEquals("AAAy\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1566         assertEquals("AAAz\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1567         assertEquals("AAA0\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1568         assertEquals("AAA1\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1569         assertEquals("AAA2\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1570         assertEquals("AAA3\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1571         assertEquals("AAA4\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1572         assertEquals("AAA5\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1573         assertEquals("AAA6\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1574         assertEquals("AAA7\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1575         assertEquals("AAA8\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1576         assertEquals("AAA9\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1577         assertEquals("AAA+\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1578         assertEquals("AAA/\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1579     }
1580 
1581     /**
1582      * Tests URL-safe Base64 against random data, sizes 0 to 150.
1583      */
1584     @Test
1585     void testUrlSafe() {
1586         // test random data of sizes 0 through 150
1587         final BaseNCodec codec = new Base64(true);
1588         for (int i = 0; i <= 150; i++) {
1589             final byte[][] randomData = BaseNTestData.randomData(codec, i);
1590             final byte[] encoded = randomData[1];
1591             final byte[] decoded = randomData[0];
1592             final byte[] result = Base64.decodeBase64(encoded);
1593             assertArrayEquals(decoded, result, "url-safe i=" + i);
1594             assertFalse(ArrayUtils.contains(encoded, (byte) '='), "url-safe i=" + i + " no '='");
1595             assertFalse(ArrayUtils.contains(encoded, (byte) '\\'), "url-safe i=" + i + " no '\\'");
1596             assertFalse(ArrayUtils.contains(encoded, (byte) '+'), "url-safe i=" + i + " no '+'");
1597         }
1598 
1599     }
1600 
1601     /**
1602      * Base64 encoding of UUID's is a common use-case, especially in URL-SAFE
1603      * mode. This test case ends up being the "URL-SAFE" JUnit's.
1604      *
1605      * @throws DecoderException
1606      *             if Hex.decode() fails - a serious problem since Hex comes
1607      *             from our own commons-codec!
1608      */
1609     @Test
1610     void testUUID() throws DecoderException {
1611         // The 4 UUID's below contains mixtures of + and / to help us test the
1612         // URL-SAFE encoding mode.
1613         final byte[][] ids = new byte[4][];
1614 
1615         // ids[0] was chosen so that it encodes with at least one +.
1616         ids[0] = Hex.decodeHex("94ed8d0319e4493399560fb67404d370");
1617 
1618         // ids[1] was chosen so that it encodes with both / and +.
1619         ids[1] = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090");
1620 
1621         // ids[2] was chosen so that it encodes with at least one /.
1622         ids[2] = Hex.decodeHex("64be154b6ffa40258d1a01288e7c31ca");
1623 
1624         // ids[3] was chosen so that it encodes with both / and +, with /
1625         // right at the beginning.
1626         ids[3] = Hex.decodeHex("ff7f8fc01cdb471a8c8b5a9306183fe8");
1627 
1628         final byte[][] standard = new byte[4][];
1629         standard[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg+2dATTcA==");
1630         standard[1] = StringUtils.getBytesUtf8("K/fMJwH+Q5e0nr7tWsxwkA==");
1631         standard[2] = StringUtils.getBytesUtf8("ZL4VS2/6QCWNGgEojnwxyg==");
1632         standard[3] = StringUtils.getBytesUtf8("/3+PwBzbRxqMi1qTBhg/6A==");
1633 
1634         final byte[][] urlSafe1 = new byte[4][];
1635         // regular padding (two '==' signs).
1636         urlSafe1[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA==");
1637         urlSafe1[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA==");
1638         urlSafe1[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg==");
1639         urlSafe1[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A==");
1640 
1641         final byte[][] urlSafe2 = new byte[4][];
1642         // single padding (only one '=' sign).
1643         urlSafe2[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA=");
1644         urlSafe2[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA=");
1645         urlSafe2[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg=");
1646         urlSafe2[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A=");
1647 
1648         final byte[][] urlSafe3 = new byte[4][];
1649         // no padding (no '=' signs).
1650         urlSafe3[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA");
1651         urlSafe3[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA");
1652         urlSafe3[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg");
1653         urlSafe3[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A");
1654 
1655         for (int i = 0; i < 4; i++) {
1656             final byte[] encodedStandard = Base64.encodeBase64(ids[i]);
1657             final byte[] encodedUrlSafe = Base64.encodeBase64URLSafe(ids[i]);
1658             final byte[] decodedStandard = Base64.decodeBase64(standard[i]);
1659             final byte[] decodedUrlSafe1 = Base64.decodeBase64(urlSafe1[i]);
1660             final byte[] decodedUrlSafe2 = Base64.decodeBase64(urlSafe2[i]);
1661             final byte[] decodedUrlSafe3 = Base64.decodeBase64(urlSafe3[i]);
1662 
1663             // Very important debugging output should anyone
1664             // ever need to delve closely into this stuff.
1665 //            {
1666 //                System.out.println("reference: [" + Hex.encodeHexString(ids[i]) + "]");
1667 //                System.out.println("standard:  [" + Hex.encodeHexString(decodedStandard) + "] From: ["
1668 //                        + StringUtils.newStringUtf8(standard[i]) + "]");
1669 //                System.out.println("safe1:     [" + Hex.encodeHexString(decodedUrlSafe1) + "] From: ["
1670 //                        + StringUtils.newStringUtf8(urlSafe1[i]) + "]");
1671 //                System.out.println("safe2:     [" + Hex.encodeHexString(decodedUrlSafe2) + "] From: ["
1672 //                        + StringUtils.newStringUtf8(urlSafe2[i]) + "]");
1673 //                System.out.println("safe3:     [" + Hex.encodeHexString(decodedUrlSafe3) + "] From: ["
1674 //                        + StringUtils.newStringUtf8(urlSafe3[i]) + "]");
1675 //            }
1676 
1677             assertArrayEquals(encodedStandard, standard[i], "standard encode uuid");
1678             assertArrayEquals(encodedUrlSafe, urlSafe3[i], "url-safe encode uuid");
1679             assertArrayEquals(decodedStandard, ids[i], "standard decode uuid");
1680             assertArrayEquals(decodedUrlSafe1, ids[i], "url-safe1 decode uuid");
1681             assertArrayEquals(decodedUrlSafe2, ids[i], "url-safe2 decode uuid");
1682             assertArrayEquals(decodedUrlSafe3, ids[i], "url-safe3 decode uuid");
1683         }
1684     }
1685 
1686     private String toString(final byte[] data) {
1687         return org.apache.commons.lang3.StringUtils.join(data, ',');
1688     }
1689 }