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