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.net.util;
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.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertSame;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  
29  import java.math.BigInteger;
30  import java.nio.charset.StandardCharsets;
31  import java.util.Base64.Decoder;
32  import java.util.Base64.Encoder;
33  
34  import org.apache.commons.lang3.ArrayFill;
35  import org.apache.commons.lang3.ArrayUtils;
36  import org.junit.jupiter.api.Test;
37  
38  @SuppressWarnings({ "deprecation" })
39  class Base64Test {
40  
41      private static String toString(final byte[] encodedData) {
42          return encodedData != null ? new String(encodedData, StandardCharsets.UTF_8) : null;
43      }
44  
45      private void checkDecoders(final String expected, final byte[] actual) {
46          final byte[] decoded = Base64.decodeBase64(actual);
47          assertEquals(expected, toString(decoded));
48          assertEquals(expected, toString(actual != null ? getJreDecoder().decode(actual) : null));
49          assertEquals(expected, toString(new Base64().decode(actual)));
50      }
51  
52      private void checkDecoders(final String expected, final String actual) {
53          final byte[] decoded = Base64.decodeBase64(actual);
54          assertEquals(expected, new String(decoded));
55          assertEquals(expected, toString(decoded));
56          assertEquals(expected, toString(actual != null ? getJreDecoder().decode(actual) : null));
57          assertEquals(expected, toString(new Base64().decode(actual)));
58      }
59  
60      private Decoder getJreDecoder() {
61          return java.util.Base64.getDecoder();
62      }
63  
64      private Encoder getJreEncoder() {
65          return java.util.Base64.getEncoder();
66      }
67  
68      private Encoder getJreMimeEncoder() {
69          return java.util.Base64.getMimeEncoder();
70      }
71  
72      private Encoder getJreMimeEncoder(final int lineLength, final byte[] lineSeparator) {
73          return java.util.Base64.getMimeEncoder(lineLength, lineSeparator);
74      }
75  
76      private Encoder getJreUrlEncoder() {
77          return java.util.Base64.getUrlEncoder();
78      }
79  
80      @Test
81      void testBase64() {
82          final Base64 b64 = new Base64();
83          assertFalse(b64.isUrlSafe());
84      }
85  
86      @Test
87      void testBase64Boolean() {
88          final Base64 b64 = new Base64(true);
89          assertTrue(b64.isUrlSafe());
90          assertArrayEquals(new byte[] {}, b64.getLineSeparator());
91      }
92  
93      @Test
94      void testBase64Int() {
95          Base64 b64;
96          b64 = new Base64(8);
97          assertFalse(b64.isUrlSafe());
98          assertEquals(8, b64.getLineLength());
99          b64 = new Base64(11);
100         assertEquals(8, b64.getLineLength());
101     }
102 
103     @Test
104     void testBase64IntByteArray() {
105         final Base64 b64;
106         b64 = new Base64(8, new byte[] {});
107         assertFalse(b64.isUrlSafe());
108         assertArrayEquals(new byte[] {}, b64.getLineSeparator());
109         final String stringToEncode = "<<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>>";
110         final byte[] encodedData = new Base64(Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR).encode(stringToEncode.getBytes());
111         assertEquals("PDw/Pz8+Pjw8Pz8/Pj48PD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj48PD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj48\r\nPD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj4=\r\n", toString(encodedData));
112         assertEquals(getJreMimeEncoder().encodeToString(stringToEncode.getBytes()) + "\r\n", toString(encodedData));
113         assertEquals("PDw/Pz8+Pjw8Pz8/Pj48PD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj48PD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj48~PD8/Pz4+PDw/Pz8+Pjw8Pz8/Pj4=~",
114                 toString(new Base64(Base64.CHUNK_SIZE, "~".getBytes()).encode(stringToEncode.getBytes())));
115         assertEquals(getJreMimeEncoder(Base64.CHUNK_SIZE, "~".getBytes()).encodeToString(stringToEncode.getBytes()) + "~",
116                 toString(new Base64(Base64.CHUNK_SIZE, "~".getBytes()).encode(stringToEncode.getBytes())));
117         assertEquals(getJreMimeEncoder(Base64.CHUNK_SIZE - 2, "~~".getBytes()).encodeToString(stringToEncode.getBytes()) + "~~",
118                 toString(new Base64(Base64.CHUNK_SIZE - 2, "~~".getBytes()).encode(stringToEncode.getBytes())));
119         assertEquals(getJreMimeEncoder(Base64.CHUNK_SIZE + 2, "~~~".getBytes()).encodeToString(stringToEncode.getBytes()) + "~~~",
120                 toString(new Base64(Base64.CHUNK_SIZE + 2, "~~~".getBytes()).encode(stringToEncode.getBytes())));
121     }
122 
123     @Test
124     void testBase64IntByteArrayBoolean() {
125         Base64 b64;
126         b64 = new Base64(8, new byte[] {}, false);
127         assertFalse(b64.isUrlSafe());
128         b64 = new Base64(8, new byte[] {}, true);
129         assertTrue(b64.isUrlSafe());
130         assertThrows(IllegalArgumentException.class, () -> new Base64(8, new byte[] { 'A' }, false));
131     }
132 
133     @Test
134     void testDecodeBase64ByteArray() {
135         checkDecoders("light w", new byte[] { 'b', 'G', 'l', 'n', 'a', 'H', 'Q', 'g', 'd', 'w', '=', '=' });
136     }
137 
138     @Test
139     void testDecodeBase64String() {
140         checkDecoders("light w", "bGlnaHQgdw==");
141     }
142 
143     @Test
144     void testDecodeByteArray() {
145         checkDecoders("foobar", new byte[] { 'Z', 'm', '9', 'v', 'Y', 'm', 'F', 'y' });
146     }
147 
148     @Test
149     void testDecodeByteArrayEmpty() {
150         checkDecoders("", new byte[] {});
151         checkDecoders(null, (byte[]) null);
152 
153     }
154 
155     @Test
156     void testDecodeByteArrayNull() {
157         assertNull(new Base64().decode((byte[]) null));
158     }
159 
160     @Test
161     void testDecodeInteger() {
162         testDecodeInteger(BigInteger.ONE);
163         testDecodeInteger(BigInteger.TEN);
164         testDecodeInteger(BigInteger.ZERO);
165     }
166 
167     private void testDecodeInteger(final BigInteger bi) {
168         assertEquals(bi, Base64.decodeInteger(getJreEncoder().encode(bi.toByteArray())));
169     }
170 
171     @Test
172     void testDecodeNullString() {
173         final Base64 base64 = new Base64();
174         assertThrows(NullPointerException.class, () -> base64.decode((String) null));
175     }
176 
177     @Test
178     void testDecodeString() {
179         checkDecoders("Hello World!", "SGVsbG8gV29ybGQh");
180     }
181 
182     @Test
183     void testEncodeBase64ByteArrayBoolean() {
184         final byte[] binaryData = { '1', '2', '3' };
185         final byte[] urlUnsafeData = "<<???>>".getBytes();
186         final byte[] urlUnsafeDataChunky = "<<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>>".getBytes();
187         byte[] encoded;
188         // Boolean parameter: "isChunked".
189         //
190         // isChunked false
191         encoded = Base64.encodeBase64(binaryData, false);
192         assertNotNull(encoded);
193         assertEquals(4, encoded.length);
194         assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR) - 2, encoded.length);
195         assertEquals(getJreEncoder().encodeToString(binaryData), toString(encoded));
196         assertEquals("MTIz", toString(encoded));
197         // URL unsafe
198         // <<???>>
199         encoded = Base64.encodeBase64(urlUnsafeData, false);
200         assertEquals("PDw/Pz8+Pg==", toString(encoded));
201         encoded = Base64.encodeBase64(urlUnsafeDataChunky, false);
202         assertEquals(getJreEncoder().encodeToString(urlUnsafeDataChunky), toString(encoded));
203         //
204         // isChunked false
205         encoded = Base64.encodeBase64(binaryData, false);
206         assertNotNull(encoded);
207         assertEquals(4, encoded.length);
208         assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR) - 2, encoded.length);
209         assertEquals("MTIz", toString(encoded));
210         //
211         // isChunked true
212         encoded = Base64.encodeBase64(binaryData, true);
213         assertNotNull(encoded);
214         assertEquals(6, encoded.length); // always adds trailer
215         assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR), encoded.length);
216         assertEquals("MTIz\r\n", toString(encoded));
217         // URL unsafe
218         // <<???>>
219         encoded = Base64.encodeBase64(urlUnsafeData, true);
220         assertEquals("PDw/Pz8+Pg==\r\n", toString(encoded));
221         encoded = Base64.encodeBase64(urlUnsafeDataChunky, true);
222         assertEquals(getJreMimeEncoder().encodeToString(urlUnsafeDataChunky) + "\r\n", toString(encoded));
223         //
224         // isChunked true
225         encoded = Base64.encodeBase64(binaryData, true);
226         assertNotNull(encoded);
227         assertEquals(6, encoded.length);
228         assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR), encoded.length);
229         assertEquals("MTIz\r\n", toString(encoded));
230     }
231 
232     @Test
233     void testEncodeBase64ByteArrayBooleanBoolean() {
234         final byte[] binaryData = { '1', '2', '3' };
235         byte[] encoded;
236         encoded = Base64.encodeBase64(binaryData, false, false);
237         assertNotNull(encoded);
238         assertEquals(4, encoded.length);
239         encoded = Base64.encodeBase64(binaryData, false, false);
240         assertNotNull(encoded);
241         assertEquals(4, encoded.length);
242         encoded = Base64.encodeBase64(binaryData, true, false);
243         assertNotNull(encoded);
244         assertEquals(6, encoded.length); // always adds trailer
245         encoded = Base64.encodeBase64(binaryData, true, false);
246         assertNotNull(encoded);
247         assertEquals(6, encoded.length);
248     }
249 
250     @Test
251     void testEncodeBase64ByteArrayBooleanBooleanInt() {
252         final byte[] binaryData = { '1', '2', '3' };
253         byte[] encoded;
254         encoded = Base64.encodeBase64(binaryData, false, false);
255         assertNotNull(encoded);
256         assertEquals(4, encoded.length);
257         assertThrows(IllegalArgumentException.class, () -> Base64.encodeBase64(binaryData, false, false, 3));
258         encoded = Base64.encodeBase64(binaryData, false, false, 4); // NET-483
259         assertNotNull(encoded);
260         assertEquals(4, encoded.length);
261         encoded = Base64.encodeBase64(binaryData, true, false);
262         assertNotNull(encoded);
263         assertEquals(6, encoded.length); // always adds trailer
264         assertThrows(IllegalArgumentException.class, () -> Base64.encodeBase64(binaryData, true, false, 5));
265         encoded = Base64.encodeBase64(binaryData, true, false, 6);
266         assertNotNull(encoded);
267         assertEquals(6, encoded.length);
268     }
269 
270     @Test
271     void testEncodeBase64ByteArrayEdges() {
272         final byte[] binaryData = null;
273         assertArrayEquals(binaryData, Base64.encodeBase64(binaryData));
274         final byte[] binaryData2 = {};
275         assertArrayEquals(binaryData2, Base64.encodeBase64(binaryData2));
276     }
277 
278     @Test
279     void testEncodeBase64Chunked() {
280         byte[] bytesToEncode = { 'f', 'o', 'o', 'b', 'a', 'r' };
281         byte[] encodedData = Base64.encodeBase64Chunked(bytesToEncode);
282         assertEquals("Zm9vYmFy\r\n", toString(encodedData));
283         // URL unsafe data
284         // <<???>>
285         bytesToEncode = "<<???>>".getBytes();
286         encodedData = Base64.encodeBase64Chunked(bytesToEncode);
287         assertEquals("PDw/Pz8+Pg==\r\n", toString(encodedData));
288         // > 76
289         final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A');
290         final byte[] chunked = Base64.encodeBase64Chunked(chunkMe);
291         assertEquals('\r', chunked[chunked.length - 2]);
292         assertEquals('\n', chunked[chunked.length - 1]);
293         assertArrayEquals(ArrayUtils.addAll(getJreMimeEncoder().encode(chunkMe), Base64.CHUNK_SEPARATOR), chunked);
294     }
295 
296     @Test
297     void testEncodeBase64StringByteArray() {
298         String stringToEncode = "Many hands make light work.";
299         String encodedData = Base64.encodeBase64String(stringToEncode.getBytes());
300         assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu\r\n", encodedData);
301         // URL unsafe data
302         // <<???>>
303         stringToEncode = "<<???>>";
304         encodedData = Base64.encodeBase64String(stringToEncode.getBytes());
305         assertEquals("PDw/Pz8+Pg==\r\n", encodedData);
306         // > 76
307         final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A');
308         final String chunked = Base64.encodeBase64String(chunkMe);
309         assertEquals('\r', chunked.charAt(chunked.length() - 2));
310         assertEquals('\n', chunked.charAt(chunked.length() - 1));
311         assertEquals(getJreMimeEncoder().encodeToString(chunkMe) + "\r\n", chunked);
312     }
313 
314     @Test
315     void testEncodeBase64StringByteArrayBoolean() {
316         final byte[] bytesToEncode = "light work.".getBytes();
317         final String chunkedResult = Base64.encodeBase64String(bytesToEncode, true);
318         assertEquals("bGlnaHQgd29yay4=\r\n", chunkedResult);
319         final String unchunkedResult = Base64.encodeBase64String(bytesToEncode, false);
320         assertEquals("bGlnaHQgd29yay4=", unchunkedResult);
321     }
322 
323     @Test
324     void testEncodeBase64StringUnChunked() {
325         byte[] bytesToEncode = "Many hands make light work.".getBytes();
326         String encodedData = Base64.encodeBase64StringUnChunked(bytesToEncode);
327         assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", encodedData);
328         // URL unsafe data
329         // <<???>>
330         bytesToEncode = "<<???>>".getBytes();
331         encodedData = Base64.encodeBase64StringUnChunked(bytesToEncode);
332         assertEquals("PDw/Pz8+Pg==", encodedData);
333         // > 76
334         final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A');
335         final String chunked = Base64.encodeBase64StringUnChunked(chunkMe);
336         assertEquals(getJreEncoder().encodeToString(chunkMe), chunked);
337     }
338 
339     @Test
340     void testEncodeBase64URLSafe() {
341         byte[] bytesToEncode = "Many hands make light work.".getBytes();
342         byte[] encodedData = Base64.encodeBase64URLSafe(bytesToEncode);
343         assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", toString(encodedData));
344         // URL unsafe data
345         // <<???>>
346         bytesToEncode = "<<???>>".getBytes();
347         encodedData = Base64.encodeBase64URLSafe(bytesToEncode);
348         assertEquals("PDw_Pz8-Pg", toString(encodedData));
349         // > 76 line length
350         bytesToEncode = "<<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>>".getBytes();
351         encodedData = Base64.encodeBase64URLSafe(bytesToEncode);
352         assertEquals(getJreUrlEncoder().withoutPadding().encodeToString(bytesToEncode), toString(encodedData));
353         final String encodedUrlSafe = "PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj4";
354         assertEquals(encodedUrlSafe, toString(encodedData));
355         // instance
356         assertEquals(encodedUrlSafe, toString(new Base64(0, null, true).encode(bytesToEncode)));
357         assertEquals(encodedUrlSafe, toString(new Base64(1, null, true).encode(bytesToEncode)));
358         assertEquals(encodedUrlSafe, toString(new Base64(999, null, true).encode(bytesToEncode)));
359         assertEquals(encodedUrlSafe, toString(new Base64(0, new byte[0], true).encode(bytesToEncode)));
360         assertEquals(encodedUrlSafe, toString(new Base64(0, new byte[10], true).encode(bytesToEncode)));
361         assertEquals(encodedUrlSafe, toString(new Base64(0, Base64.CHUNK_SEPARATOR, true).encode(bytesToEncode)));
362         assertEquals(encodedUrlSafe, toString(new Base64(Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR, true).encode(bytesToEncode)));
363         assertEquals(encodedUrlSafe, toString(new Base64(999, Base64.CHUNK_SEPARATOR, true).encode(bytesToEncode)));
364         assertEquals(encodedUrlSafe, toString(new Base64(true).encode(bytesToEncode)));
365     }
366 
367     @Test
368     void testEncodeBase64URLSafeString() {
369         byte[] bytesToEncode = "Many hands make light work.".getBytes();
370         String encodedData = Base64.encodeBase64URLSafeString(bytesToEncode);
371         assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", encodedData);
372         // URL unsafe data
373         // <<???>>
374         bytesToEncode = "<<???>>".getBytes();
375         encodedData = Base64.encodeBase64URLSafeString(bytesToEncode);
376         assertEquals("PDw_Pz8-Pg", encodedData);
377         // > 76 line length
378         bytesToEncode = "<<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>>".getBytes();
379         encodedData = Base64.encodeBase64URLSafeString(bytesToEncode);
380         assertEquals(getJreUrlEncoder().withoutPadding().encodeToString(bytesToEncode), encodedData);
381         assertEquals("PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj48PD8_Pz4-PDw_Pz8-Pjw8Pz8_Pj4", encodedData);
382     }
383 
384     @Test
385     void testEncodeByteArray() {
386         final Base64 base64 = new Base64();
387         final byte[] bytesToEncode = { 'l', 'i', 'g', 'h', 't', ' ', 'w', 'o', 'r' };
388         assertEquals("bGlnaHQgd29y\r\n", new String(base64.encode(bytesToEncode), StandardCharsets.UTF_8));
389     }
390 
391     @Test
392     void testEncodeByteArrayEmpty() {
393         assertNull(new Base64().encode((byte[]) null));
394         final byte[] empty = {};
395         assertSame(empty, new Base64().encode(empty));
396     }
397 
398     @Test
399     void testEncodeByteArrayNull() {
400         assertNull(new Base64().encode((byte[]) null));
401     }
402 
403     @Test
404     void testEncodeInteger() {
405         testEncodeInteger(BigInteger.ONE);
406         testEncodeInteger(BigInteger.TEN);
407         testEncodeInteger(BigInteger.ZERO);
408     }
409 
410     private void testEncodeInteger(final BigInteger bi) {
411         final byte[] decodedBytes = getJreDecoder().decode(Base64.encodeInteger(bi));
412         final BigInteger decoded = decodedBytes.length == 0 ? BigInteger.ZERO : new BigInteger(decodedBytes);
413         assertEquals(bi, decoded);
414     }
415 
416     @Test
417     void testEncodeToString() {
418         final Base64 base64 = new Base64();
419         final byte[] bytesToEncode = { 'l', 'i', 'g', 'h', 't', ' ', 'w', 'o', 'r' };
420         assertEquals("bGlnaHQgd29y\r\n", base64.encodeToString(bytesToEncode));
421     }
422 
423     @Test
424     void testIsArrayByteBase64() {
425         assertTrue(Base64.isArrayByteBase64(new byte[] { 'b', ' ' }));
426         assertFalse(Base64.isArrayByteBase64(new byte[] { '?' }));
427     }
428 
429     @Test
430     void testIsBase64() {
431         assertTrue(Base64.isBase64((byte) '='));
432         assertTrue(Base64.isBase64((byte) 'b'));
433         assertFalse(Base64.isBase64((byte) ' '));
434         assertFalse(Base64.isBase64((byte) -1));
435     }
436 
437 }