View Javadoc
1    /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.crypto.stream;
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.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.junit.jupiter.api.Assertions.fail;
27  import static org.junit.jupiter.api.Assumptions.assumeFalse;
28  
29  import java.io.BufferedInputStream;
30  import java.io.ByteArrayInputStream;
31  import java.io.ByteArrayOutputStream;
32  import java.io.DataInputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.OutputStream;
36  import java.nio.ByteBuffer;
37  import java.nio.channels.Channels;
38  import java.nio.channels.ReadableByteChannel;
39  import java.security.Key;
40  import java.security.SecureRandom;
41  import java.security.spec.AlgorithmParameterSpec;
42  import java.util.Properties;
43  import java.util.Random;
44  import java.util.concurrent.TimeUnit;
45  
46  import javax.crypto.spec.GCMParameterSpec;
47  import javax.crypto.spec.IvParameterSpec;
48  
49  import org.apache.commons.crypto.Crypto;
50  import org.apache.commons.crypto.cipher.AbstractCipherTest;
51  import org.apache.commons.crypto.cipher.CryptoCipher;
52  import org.apache.commons.crypto.utils.AES;
53  import org.apache.commons.crypto.utils.ReflectionUtils;
54  import org.junit.jupiter.api.BeforeEach;
55  import org.junit.jupiter.api.Test;
56  import org.junit.jupiter.api.Timeout;
57  
58  public abstract class AbstractCipherStreamTest {
59  
60      protected static int defaultBufferSize = 8192;
61      protected static int smallBufferSize = 1024;
62      protected final int dataLen = 20000;
63      protected final byte[] data = new byte[dataLen];
64      protected byte[] encData;
65      private final Properties props = new Properties();
66      protected byte[] key = new byte[16];
67      protected byte[] iv = new byte[16];
68      protected int count = 10000;
69  
70      protected String transformation;
71  
72      public void assumeJniPresence(final String cipherClass) {
73          assumeFalse(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME.equals(cipherClass) && !Crypto.isNativeCodeLoaded());
74      }
75  
76      @BeforeEach
77      public void before() throws Exception {
78          final Random random = new SecureRandom();
79          random.nextBytes(data);
80          random.nextBytes(key);
81          random.nextBytes(iv);
82          setUp();
83          prepareData();
84      }
85  
86      private void byteBufferFinalReadCheck(final InputStream in, final ByteBuffer buf, final int bufPos)
87              throws Exception {
88          buf.position(bufPos);
89          int len = 0;
90          int n = 0;
91          do {
92              n = ((ReadableByteChannel) in).read(buf);
93              len += n;
94          } while (n > 0);
95          buf.rewind();
96          final byte[] readData = new byte[len + 1];
97          buf.get(readData);
98          final byte[] expectedData = new byte[len + 1];
99          System.arraycopy(data, 0, expectedData, 0, len + 1);
100         assertArrayEquals(readData, expectedData);
101     }
102 
103     private void byteBufferReadCheck(final InputStream in, final ByteBuffer buf, final int bufPos)
104             throws Exception {
105         buf.position(bufPos);
106         final int n = ((ReadableByteChannel) in).read(buf);
107         assertEquals(bufPos + n, buf.position());
108         final byte[] readData = new byte[n];
109         buf.rewind();
110         buf.position(bufPos);
111         buf.get(readData);
112         final byte[] expectedData = new byte[n];
113         System.arraycopy(data, 0, expectedData, 0, n);
114         assertArrayEquals(readData, expectedData);
115 
116         assertThrows(IndexOutOfBoundsException.class, () -> in.read(readData, -1, 0));
117     }
118 
119     protected void doByteBufferRead(final String cipherClass, final boolean withChannel)
120         throws Exception {
121         // Skip this test if no JNI
122         assumeJniPresence(cipherClass);
123         ByteBuffer buf = ByteBuffer.allocate(dataLen + 100);
124         // Default buffer size, initial buffer position is 0
125         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
126             defaultBufferSize, iv, withChannel)) {
127             byteBufferReadCheck(in, buf, 0);
128         }
129 
130         // Default buffer size, initial buffer position is not 0
131         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
132             defaultBufferSize, iv, withChannel)) {
133             buf.clear();
134             byteBufferReadCheck(in, buf, 11);
135         }
136 
137         // Small buffer size, initial buffer position is 0
138         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
139             smallBufferSize, iv, withChannel)) {
140             buf.clear();
141             byteBufferReadCheck(in, buf, 0);
142         }
143 
144         // Small buffer size, initial buffer position is not 0
145         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
146             smallBufferSize, iv, withChannel)) {
147             buf.clear();
148             byteBufferReadCheck(in, buf, 11);
149         }
150 
151         // Direct buffer, default buffer size, initial buffer position is 0
152         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
153             defaultBufferSize, iv, withChannel)) {
154             buf = ByteBuffer.allocateDirect(dataLen + 100);
155             byteBufferReadCheck(in, buf, 0);
156         }
157 
158         // Direct buffer, default buffer size, initial buffer position is not 0
159         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
160             defaultBufferSize, iv, withChannel)) {
161             buf.clear();
162             byteBufferReadCheck(in, buf, 11);
163         }
164 
165         // Direct buffer, small buffer size, initial buffer position is 0
166         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
167             smallBufferSize, iv, withChannel)) {
168             buf.clear();
169             byteBufferReadCheck(in, buf, 0);
170         }
171 
172         // Direct buffer, small buffer size, initial buffer position is not 0
173         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
174             smallBufferSize, iv, withChannel)) {
175             buf.clear();
176             byteBufferReadCheck(in, buf, 11);
177         }
178 
179         // Direct buffer, small buffer size, initial buffer position is 0, final read
180         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
181             smallBufferSize, iv, withChannel)) {
182             buf.clear();
183             byteBufferFinalReadCheck(in, buf, 0);
184         }
185 
186         // Default buffer size, initial buffer position is 0, insufficient dest buffer length
187         try (InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), getCipher(cipherClass),
188             defaultBufferSize, iv, withChannel)) {
189             buf = ByteBuffer.allocate(100);
190             byteBufferReadCheck(in, buf, 0);
191         }
192 
193         // Default buffer size, initial buffer position is 0
194         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
195             new IvParameterSpec(iv), withChannel)) {
196             buf = ByteBuffer.allocate(dataLen + 100);
197             byteBufferReadCheck(in, buf, 0);
198         }
199 
200         // Default buffer size, initial buffer position is not 0
201         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
202             new IvParameterSpec(iv), withChannel)) {
203             buf.clear();
204             byteBufferReadCheck(in, buf, 11);
205         }
206 
207         // Small buffer size, initial buffer position is 0
208         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
209             new IvParameterSpec(iv), withChannel)) {
210             buf.clear();
211             byteBufferReadCheck(in, buf, 0);
212         }
213 
214         // Small buffer size, initial buffer position is not 0
215         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
216             new IvParameterSpec(iv), withChannel)) {
217             buf.clear();
218             byteBufferReadCheck(in, buf, 11);
219         }
220 
221         // Direct buffer, default buffer size, initial buffer position is 0
222         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
223             new IvParameterSpec(iv), withChannel)) {
224             buf = ByteBuffer.allocateDirect(dataLen + 100);
225             byteBufferReadCheck(in, buf, 0);
226         }
227 
228         // Direct buffer, default buffer size, initial buffer position is not 0
229         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
230             new IvParameterSpec(iv), withChannel)) {
231             buf.clear();
232             byteBufferReadCheck(in, buf, 11);
233         }
234 
235         // Direct buffer, small buffer size, initial buffer position is 0
236         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
237             new IvParameterSpec(iv), withChannel)) {
238             buf.clear();
239             byteBufferReadCheck(in, buf, 0);
240         }
241 
242         // Direct buffer, small buffer size, initial buffer position is not 0
243         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
244             new IvParameterSpec(iv), withChannel)) {
245             buf.clear();
246             byteBufferReadCheck(in, buf, 11);
247         }
248 
249         // Direct buffer, default buffer size, initial buffer position is 0, final read
250         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
251             new IvParameterSpec(iv), withChannel)) {
252             buf.clear();
253             byteBufferFinalReadCheck(in, buf, 0);
254         }
255 
256         // Default buffer size, initial buffer position is 0, insufficient dest buffer length
257         try (InputStream in = newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData), key,
258             new IvParameterSpec(iv), withChannel)) {
259             buf = ByteBuffer.allocate(100);
260             byteBufferReadCheck(in, buf, 0);
261         }
262     }
263 
264     private void doByteBufferWrite(final CryptoOutputStream out, final boolean withChannel) throws Exception {
265         ByteBuffer buf = ByteBuffer.allocateDirect(dataLen / 2);
266         buf.put(data, 0, dataLen / 2);
267         buf.flip();
268         final int n1 = out.write(buf);
269 
270         buf.clear();
271         buf.put(data, n1, dataLen / 3);
272         buf.flip();
273         final int n2 = out.write(buf);
274 
275         buf.clear();
276         buf.put(data, n1 + n2, dataLen - n1 - n2 - 1);
277         buf.flip();
278         final int n3 = out.write(buf);
279 
280         out.write(1);
281 
282         assertEquals(dataLen, n1 + n2 + n3 + 1);
283 
284         assertThrows(IndexOutOfBoundsException.class, () -> out.write(data, 0, data.length + 1));
285         out.flush();
286 
287         try (InputStream in = newCryptoInputStream(
288                 new ByteArrayInputStream(encData), out.getCipher(),
289                 defaultBufferSize, iv, withChannel)) {
290             buf = ByteBuffer.allocate(dataLen + 100);
291             byteBufferReadCheck(in, buf, 0);
292         }
293     }
294 
295     protected void doByteBufferWrite(final String cipherClass,
296             final ByteArrayOutputStream baos, final boolean withChannel)
297             throws Exception {
298         assumeJniPresence(cipherClass);
299         baos.reset();
300         CryptoOutputStream out = newCryptoOutputStream(baos, getCipher(cipherClass), defaultBufferSize, iv, withChannel);
301         doByteBufferWrite(out, withChannel);
302 
303         baos.reset();
304         try (final CryptoCipher cipher = getCipher(cipherClass)) {
305             final String transformation = cipher.getAlgorithm();
306             out = newCryptoOutputStream(transformation, props, baos, key, new IvParameterSpec(iv), withChannel);
307             doByteBufferWrite(out, withChannel);
308             out.write(1);
309             assertTrue(out.isOpen());
310 
311             out = newCryptoOutputStream(transformation, props, baos, key, new IvParameterSpec(iv), withChannel);
312             out.close();
313             assertFalse(out.isOpen());
314         }
315     }
316 
317     protected void doExceptionTest(final String cipherClass, final ByteArrayOutputStream baos,
318             final boolean withChannel) throws IOException {
319         assumeJniPresence(cipherClass);
320 
321         // Test InvalidAlgorithmParameters
322        Exception ex = assertThrows(IOException.class, () -> newCryptoInputStream(transformation, props, new ByteArrayInputStream(encData),
323                AES.newSecretKeySpec(key), new GCMParameterSpec(0, new byte[0]), withChannel));
324         assertEquals(ex.getMessage(),"Illegal parameters");
325         // Test InvalidAlgorithmParameters
326         ex =  assertThrows(IOException.class, () -> newCryptoOutputStream(transformation, props, baos,
327                 AES.newSecretKeySpec(key), new GCMParameterSpec(0,
328                         new byte[0]), withChannel));
329         assertEquals(ex.getMessage(),"Illegal parameters");
330 
331         // Test Invalid Key
332         assertThrows(IOException.class, () -> newCryptoInputStream(transformation,props, new ByteArrayInputStream(encData),
333                 AES.newSecretKeySpec(new byte[10]), new IvParameterSpec(iv), withChannel));
334         // Test Invalid Key
335         assertThrows(IOException.class, () -> newCryptoOutputStream(transformation, props, baos, new byte[10],
336                 new IvParameterSpec(iv), withChannel));
337 
338         // Test reading a closed stream.
339         InputStream closedIn;
340         try (final InputStream in = newCryptoInputStream(new ByteArrayInputStream(encData),
341                 getCipher(cipherClass), defaultBufferSize, iv, withChannel)) {
342             closedIn = in;
343         }
344         // Throw exception.
345         ex = assertThrows(IOException.class, closedIn::read);
346         assertEquals(ex.getMessage(), "Stream closed");
347 
348         // Test closing a closed stream.
349         try {
350             closedIn.close(); // Don't throw exception on double-close.
351         } catch (final IOException ioEx) {
352             fail("Should not throw exception closing a closed stream.");
353         }
354 
355         // Test checking a closed stream.
356         final OutputStream out = newCryptoOutputStream(transformation, props, baos, key, new IvParameterSpec(iv),
357                 withChannel);
358         out.close();
359         // Throw exception.
360         assertThrows(IOException.class, ((CryptoOutputStream) out)::checkStream);
361 
362         // Test closing a closed stream.
363         try {
364             out.close(); // Don't throw exception.
365         } catch (final IOException ioEx) {
366             fail("Should not throw exception closing a closed stream.");
367         }
368 
369         // Test checkStreamCipher
370         try {
371             CryptoInputStream.checkStreamCipher(getCipher(cipherClass));
372         } catch (final IOException ioEx) {
373             assertEquals(ioEx.getMessage(), "AES/CTR/NoPadding is required");
374         } finally {
375             closedIn.close();
376         }
377 
378         // Test unsupported operation handling.
379         try (final InputStream inNewCrytptoStr = newCryptoInputStream(new ByteArrayInputStream(encData),
380                 getCipher(cipherClass), defaultBufferSize, iv, false)) {
381             closedIn.mark(0);
382             assertFalse(closedIn.markSupported());
383             ex = assertThrows(IOException.class, inNewCrytptoStr::reset);
384             assertEquals(ex.getMessage(), "mark/reset not supported");
385         }
386     }
387 
388     protected void doFieldGetterTest(final String cipherClass, final ByteArrayOutputStream baos,
389             final boolean withChannel) throws Exception {
390         assumeJniPresence(cipherClass);
391 
392         try (final CryptoCipher cipher = getCipher(cipherClass);
393                 final CryptoInputStream in = newCryptoInputStream(new ByteArrayInputStream(encData), cipher, defaultBufferSize, iv, withChannel)) {
394 
395             final Properties props = new Properties();
396             final String bufferSize = Integer.toString(defaultBufferSize / 2);
397             props.put(CryptoInputStream.STREAM_BUFFER_SIZE_KEY, bufferSize);
398 
399             assertEquals(CryptoInputStream.getBufferSize(props), Integer.parseInt(bufferSize));
400             assertEquals(in.getBufferSize(), defaultBufferSize);
401             assertEquals(in.getCipher().getClass(), Class.forName(cipherClass));
402             assertEquals(in.getKey().getAlgorithm(), AES.ALGORITHM);
403             assertEquals(in.getParams().getClass(), IvParameterSpec.class);
404             assertNotNull(in.getInput());
405 
406             try (final CryptoOutputStream out = newCryptoOutputStream(baos, getCipher(cipherClass), defaultBufferSize, iv, withChannel)) {
407                 assertEquals(out.getOutBuffer().capacity(), defaultBufferSize + cipher.getBlockSize());
408                 assertEquals(out.getInBuffer().capacity(), defaultBufferSize);
409                 assertEquals(out.getBufferSize(), defaultBufferSize);
410             }
411         }
412     }
413 
414     protected void doReadWriteTest(final int count, final String encCipherClass,
415             final String decCipherClass, final byte[] iv) throws IOException {
416         doReadWriteTestForInputStream(count, encCipherClass, decCipherClass, iv);
417         doReadWriteTestForReadableByteChannel(count, encCipherClass,
418                 decCipherClass, iv);
419     }
420 
421     private void doReadWriteTestForInputStream(final int count,
422             final String encCipherClass, final String decCipherClass, final byte[] iv)
423             throws IOException {
424         assumeJniPresence(encCipherClass);
425         assumeJniPresence(decCipherClass);
426         // Created a cipher object of type encCipherClass;
427         try (final CryptoCipher encCipher = getCipher(encCipherClass)) {
428 
429             // Generate data
430             final SecureRandom random = new SecureRandom();
431             final byte[] originalData = new byte[count];
432             final byte[] decryptedData = new byte[count];
433             random.nextBytes(originalData);
434 
435             // Encrypt data
436             final ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
437             try (CryptoOutputStream out = newCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv, false)) {
438                 out.write(originalData, 0, originalData.length);
439                 out.flush();
440             }
441 
442             // Created a cipher object of type decCipherClass;
443             try (final CryptoCipher decCipher = getCipher(decCipherClass)) {
444 
445                 // Decrypt data
446                 try (CryptoInputStream in = newCryptoInputStream(new ByteArrayInputStream(encryptedData.toByteArray()), decCipher, defaultBufferSize, iv,
447                         false)) {
448 
449                     // Check
450                     int remainingToRead = count;
451                     int offset = 0;
452                     while (remainingToRead > 0) {
453                         final int n = in.read(decryptedData, offset, decryptedData.length - offset);
454                         if (n >= 0) {
455                             remainingToRead -= n;
456                             offset += n;
457                         }
458                     }
459 
460                     assertArrayEquals(originalData, decryptedData, "originalData and decryptedData not equal");
461                 }
462 
463                 // Decrypt data byte-at-a-time
464                 try (CryptoInputStream in = newCryptoInputStream(new ByteArrayInputStream(encryptedData.toByteArray()), decCipher, defaultBufferSize, iv,
465                         false)) {
466 
467                     // Check
468                     final DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
469                     int expected;
470                     do {
471                         expected = originalIn.read();
472                         assertEquals(expected, in.read(), "Decrypted stream read by byte does not match");
473                     } while (expected != -1);
474 
475                     // Completed checking records;
476                 }
477             }
478         }
479     }
480 
481     private void doReadWriteTestForReadableByteChannel(final int count,
482             final String encCipherClass, final String decCipherClass, final byte[] iv)
483             throws IOException {
484         assumeJniPresence(encCipherClass);
485         assumeJniPresence(decCipherClass);
486         // Creates a cipher object of type encCipherClass;
487         try (final CryptoCipher encCipher = getCipher(encCipherClass)) {
488 
489             // Generate data
490             final SecureRandom random = new SecureRandom();
491             final byte[] originalData = new byte[count];
492             final byte[] decryptedData = new byte[count];
493             random.nextBytes(originalData);
494 
495             // Encrypt data
496             final ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
497             try (CryptoOutputStream out = newCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv, true)) {
498                 out.write(originalData, 0, originalData.length);
499                 out.flush();
500             }
501 
502             // Creates a cipher object of type decCipherClass
503             try (final CryptoCipher decCipher = getCipher(decCipherClass)) {
504 
505                 // Decrypt data
506                 try (CryptoInputStream in = newCryptoInputStream(new ByteArrayInputStream(encryptedData.toByteArray()), decCipher, defaultBufferSize, iv,
507                         true)) {
508 
509                     // Check
510                     int remainingToRead = count;
511                     int offset = 0;
512                     while (remainingToRead > 0) {
513                         final int n = in.read(decryptedData, offset, decryptedData.length - offset);
514                         if (n >= 0) {
515                             remainingToRead -= n;
516                             offset += n;
517                         }
518                     }
519 
520                     assertArrayEquals(originalData, decryptedData);
521                 }
522 
523                 // Decrypt data byte-at-a-time
524                 try (CryptoInputStream in = newCryptoInputStream(new ByteArrayInputStream(encryptedData.toByteArray()), decCipher, defaultBufferSize, iv,
525                         true)) {
526 
527                     // Check
528                     final DataInputStream originalIn = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(originalData)));
529                     int expected;
530                     do {
531                         expected = originalIn.read();
532                         assertEquals(expected, in.read());
533                     } while (expected != -1);
534 
535                     // Completed checking records
536                 }
537             }
538         }
539     }
540 
541     protected void doSkipTest(final String cipherClass, final boolean withChannel)
542             throws IOException {
543         assumeJniPresence(cipherClass);
544         try (@SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream.
545         InputStream in = newCryptoInputStream(
546                 new ByteArrayInputStream(encData), getCipher(cipherClass),
547                 defaultBufferSize, iv, withChannel)) {
548             final byte[] result = new byte[dataLen];
549             final int n1 = readAll(in, result, 0, dataLen / 5);
550 
551             assertEquals(in.skip(0), 0);
552 
553             long skipped = in.skip(dataLen / 5);
554             final int n2 = readAll(in, result, 0, dataLen);
555 
556             assertEquals(dataLen, n1 + skipped + n2);
557             final byte[] readData = new byte[n2];
558             System.arraycopy(result, 0, readData, 0, n2);
559             final byte[] expectedData = new byte[n2];
560             System.arraycopy(data, dataLen - n2, expectedData, 0, n2);
561             assertArrayEquals(readData, expectedData);
562 
563             final Exception e = assertThrows(IllegalArgumentException.class, () -> in.skip(-3));
564             assertTrue(e.getMessage().contains("Negative skip length"));
565 
566             // Skip after EOF
567             skipped = in.skip(3);
568             assertEquals(skipped, 0);
569         }
570     }
571 
572     protected CryptoCipher getCipher(final String cipherClass) throws IOException {
573         try {
574             return (CryptoCipher) ReflectionUtils.newInstance(
575                     ReflectionUtils.getClassByName(cipherClass), props,
576                     transformation);
577         } catch (final ClassNotFoundException cnfe) {
578             throw new IOException("Illegal crypto cipher!");
579         }
580     }
581 
582     protected CryptoInputStream newCryptoInputStream(final ByteArrayInputStream bais,
583             final CryptoCipher cipher, final int bufferSize, final byte[] iv, final boolean withChannel)
584             throws IOException {
585         if (withChannel) {
586             return new CryptoInputStream(Channels.newChannel(bais), cipher,
587                     bufferSize, AES.newSecretKeySpec(key),
588                     new IvParameterSpec(iv));
589         }
590         return new CryptoInputStream(bais, cipher, bufferSize,
591                 AES.newSecretKeySpec(key), new IvParameterSpec(iv));
592     }
593 
594     protected CryptoInputStream newCryptoInputStream(final String transformation, final Properties props,
595     	    final ByteArrayInputStream bais, final byte[] key, final AlgorithmParameterSpec params,
596     	    final boolean withChannel) throws IOException {
597         if (withChannel) {
598     	    return new CryptoInputStream(transformation, props, Channels.newChannel(bais), AES.newSecretKeySpec(key), params);
599     	}
600         return new CryptoInputStream(transformation, props, bais, AES.newSecretKeySpec(key), params);
601     }
602 
603     protected CryptoInputStream newCryptoInputStream(final String transformation,
604             final Properties props, final ByteArrayInputStream bais, final Key key,
605             final AlgorithmParameterSpec params, final boolean withChannel) throws IOException {
606         if (withChannel) {
607             return new CryptoInputStream(transformation, props, Channels.newChannel(bais), key, params);
608         }
609         return new CryptoInputStream(transformation, props, bais, key, params);
610     }
611 
612     protected CryptoOutputStream newCryptoOutputStream(
613             final ByteArrayOutputStream baos, final CryptoCipher cipher, final int bufferSize,
614             final byte[] iv, final boolean withChannel) throws IOException {
615         if (withChannel) {
616             return new CryptoOutputStream(Channels.newChannel(baos), cipher,
617                     bufferSize, AES.newSecretKeySpec(key),
618                     new IvParameterSpec(iv));
619         }
620         return new CryptoOutputStream(baos, cipher, bufferSize,
621                 AES.newSecretKeySpec(key), new IvParameterSpec(iv));
622     }
623 
624     protected CryptoOutputStream newCryptoOutputStream(final String transformation,
625             final Properties props, final ByteArrayOutputStream baos, final byte[] key,
626             final AlgorithmParameterSpec param, final boolean withChannel) throws IOException {
627         if (withChannel) {
628             return new CryptoOutputStream(transformation, props, Channels.newChannel(baos),
629                     AES.newSecretKeySpec(key), param);
630         }
631         return new CryptoOutputStream(transformation, props, baos, AES.newSecretKeySpec(key),
632                 param);
633     }
634 
635     protected CryptoOutputStream newCryptoOutputStream(final String transformation,
636             final Properties props, final ByteArrayOutputStream baos, final Key key,
637             final AlgorithmParameterSpec params, final boolean withChannel) throws IOException {
638         if (withChannel) {
639             return new CryptoOutputStream(transformation, props, Channels.newChannel(baos), key, params);
640         }
641         return new CryptoOutputStream(transformation, props, baos, key, params);
642     }
643 
644     private void prepareData() throws IOException, ClassNotFoundException {
645         try (CryptoCipher cipher = (CryptoCipher) ReflectionUtils.newInstance(ReflectionUtils.getClassByName(AbstractCipherTest.JCE_CIPHER_CLASSNAME), props,
646                 transformation)) {
647             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
648             try (OutputStream out = new CryptoOutputStream(baos, cipher, defaultBufferSize, AES.newSecretKeySpec(key), new IvParameterSpec(iv))) {
649                 out.write(data);
650                 out.flush();
651             }
652             encData = baos.toByteArray();
653         }
654     }
655 
656     private int readAll(final InputStream in, final byte[] b, final int offset, final int len)
657             throws IOException {
658         int n = 0;
659         int total = 0;
660         while (n != -1) {
661             total += n;
662             if (total >= len) {
663                 break;
664             }
665             n = in.read(b, offset + total, len - total);
666         }
667 
668         return total;
669     }
670 
671     public abstract void setUp() throws IOException;
672 
673     /** Test byte buffer read with different buffer size. */
674     @Test
675     @Timeout(value = 120000, unit = TimeUnit.MILLISECONDS)
676     public void testByteBufferRead() throws Exception {
677         doByteBufferRead(AbstractCipherTest.JCE_CIPHER_CLASSNAME, false);
678         doByteBufferRead(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, false);
679 
680         doByteBufferRead(AbstractCipherTest.JCE_CIPHER_CLASSNAME, true);
681         doByteBufferRead(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, true);
682     }
683 
684     /** Test byte buffer write. */
685     @Test
686     @Timeout(value = 120000, unit = TimeUnit.MILLISECONDS)
687     public void testByteBufferWrite() throws Exception {
688         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
689         doByteBufferWrite(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, false);
690         doByteBufferWrite(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, false);
691 
692         doByteBufferWrite(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, true);
693         doByteBufferWrite(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, true);
694     }
695 
696     @Test
697     @Timeout(value = 120000, unit = TimeUnit.MILLISECONDS)
698     public void testExceptions() throws Exception {
699         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
700         doExceptionTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, false);
701         doExceptionTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, false);
702 
703         doExceptionTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, true);
704         doExceptionTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, true);
705     }
706 
707     @Test
708     @Timeout(value = 120000, unit = TimeUnit.MILLISECONDS)
709     public void testFieldGetters() throws Exception {
710         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
711         doFieldGetterTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, false);
712         doFieldGetterTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, false);
713 
714         doFieldGetterTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, true);
715         doFieldGetterTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, true);
716     }
717 
718     @Test
719     public void testReadWrite() throws Exception {
720         doReadWriteTest(0, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
721         doReadWriteTest(0, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
722         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
723         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
724         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
725         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
726         // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff
727         for (int i = 0; i < 8; i++) {
728             iv[8 + i] = (byte) 0xff;
729         }
730         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
731         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
732         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
733         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
734     }
735 
736     /** Test skip. */
737     @Test
738     @Timeout(value = 120000, unit = TimeUnit.MILLISECONDS)
739     public void testSkip() throws Exception {
740         doSkipTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, false);
741         doSkipTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, false);
742 
743         doSkipTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, true);
744         doSkipTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, true);
745     }
746 }