Repository: commons-crypto
Updated Branches:
  refs/heads/master 92d129aa5 -> 4920d2729


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java 
b/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
new file mode 100644
index 0000000..e48d548
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
@@ -0,0 +1,242 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.cipher;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Properties;
+import java.util.Random;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.commons.crypto.conf.ConfigurationKeys;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+import org.apache.commons.crypto.utils.Utils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractCipherTest {
+
+  // data
+  public static final int BYTEBUFFER_SIZE = 1000;
+  public String[] cipherTests = null;
+  Properties props = null;
+  String cipherClass = null;
+  CipherTransformation[] transformations = null;
+
+  // cipher
+  static final byte[] KEY = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+      0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
+  static final byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+      0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+  Cipher enc, dec;
+
+  @Before
+  public void setup() {
+    init();
+    Utils.checkNotNull(cipherClass);
+    Utils.checkNotNull(transformations);
+    props = new Properties();
+    props.setProperty(ConfigurationKeys.CHIMERA_CRYPTO_CIPHER_CLASSES_KEY,
+        cipherClass);
+  }
+
+  protected abstract void init() ;
+
+  @Test
+  public void cryptoTest() throws GeneralSecurityException, IOException {
+    for (CipherTransformation tran : transformations) {
+      /** uses the small data set in {@link TestData} */
+      cipherTests = TestData.getTestData(tran);
+      for (int i = 0; i != cipherTests.length; i += 5) {
+        byte[] key = DatatypeConverter.parseHexBinary(cipherTests[i + 1]);
+        byte[] iv = DatatypeConverter.parseHexBinary(cipherTests[i + 2]);
+
+        byte[] inputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 
3]);
+        byte[] outputBytes = DatatypeConverter.parseHexBinary(cipherTests[i + 
4]);
+
+        ByteBuffer inputBuffer = ByteBuffer.allocateDirect(inputBytes.length);
+        ByteBuffer outputBuffer = 
ByteBuffer.allocateDirect(outputBytes.length);
+        inputBuffer.put(inputBytes);
+        inputBuffer.flip();
+        outputBuffer.put(outputBytes);
+        outputBuffer.flip();
+
+        byteBufferTest(tran,key, iv, inputBuffer, outputBuffer);
+        byteArrayTest(tran, key, iv, inputBytes, outputBytes);
+      }
+
+      /** uses randomly generated big data set */
+      byteArrayTest(tran, KEY, IV);
+    }
+  }
+
+  private void byteBufferTest(CipherTransformation transformation, byte[] key,
+                              byte[] iv,
+                                ByteBuffer input, ByteBuffer output) throws
+      GeneralSecurityException, IOException {
+    ByteBuffer decResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
+    ByteBuffer encResult = ByteBuffer.allocateDirect(BYTEBUFFER_SIZE);
+    Cipher enc, dec;
+
+    enc = getCipher(transformation);
+    dec = getCipher(transformation);
+
+    try {
+      enc.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (Exception e) {
+      Assert.fail("AES failed initialisation - " + e.toString());
+    }
+
+    try {
+      dec.init(Cipher.DECRYPT_MODE, key, iv);
+    } catch (Exception e) {
+      Assert.fail("AES failed initialisation - " + e.toString());
+    }
+
+    //
+    // encryption pass
+    //
+    enc.doFinal(input, encResult);
+    input.flip();
+    encResult.flip();
+    if (!output.equals(encResult)) {
+      byte[] b = new byte[output.remaining()];
+      output.get(b);
+      byte[] c = new byte[encResult.remaining()];
+      encResult.get(c);
+      Assert.fail("AES failed encryption - expected " + new String(
+          DatatypeConverter
+              .printHexBinary(b)) + " got " + new String(
+          DatatypeConverter.printHexBinary(c)));
+    }
+
+    //
+    // decryption pass
+    //
+    dec.doFinal(encResult, decResult);
+    decResult.flip();
+
+    if (!input.equals(decResult)) {
+      byte[] inArray = new byte[input.remaining()];
+      byte[] decResultArray = new byte[decResult.remaining()];
+      input.get(inArray);
+      decResult.get(decResultArray);
+      Assert.fail();
+    }
+  }
+
+  /** test byte array whose data is planned in {@link TestData} */
+  private void byteArrayTest(CipherTransformation transformation, byte[] key,
+      byte[] iv, byte[] input, byte[] output) throws GeneralSecurityException {
+    resetCipher(transformation, key, iv);
+    int blockSize = transformation.getAlgorithmBlockSize();
+
+    byte[] temp = new byte[input.length + blockSize];
+    int n = enc.doFinal(input, 0, input.length, temp, 0);
+    byte[] cipherText = new byte[n];
+    System.arraycopy(temp, 0, cipherText, 0, n);
+    Assert.assertArrayEquals("byte array encryption error.", output, 
cipherText);
+
+    temp = new byte[cipherText.length + blockSize];
+    int m = dec.doFinal(cipherText, 0, cipherText.length, temp, 0);
+    byte[] plainText = new byte[m];
+    System.arraycopy(temp, 0, plainText, 0, m);
+    Assert.assertArrayEquals("byte array decryption error.", input, plainText);
+  }
+
+  /** test byte array whose data is randomly generated */
+  private void byteArrayTest(CipherTransformation transformation, byte[] key,
+      byte[] iv) throws GeneralSecurityException {
+    int blockSize = transformation.getAlgorithmBlockSize();
+
+    // AES_CBC_NOPADDING only accepts data whose size is the multiple of block 
size
+    int[] dataLenList = (transformation == 
CipherTransformation.AES_CBC_NOPADDING)
+        ? new int[] {10 * 1024} : new int[] {10 * 1024, 10 * 1024 - 3};
+    for (int dataLen : dataLenList) {
+      byte[] plainText = new byte[dataLen];
+      Random random = new SecureRandom();
+      random.nextBytes(plainText);
+      byte[] cipherText = new byte[dataLen + blockSize];
+
+      // check update method with inputs whose sizes are the multiple of block 
size or not
+      int[] bufferLenList = new int[] {2 * 1024 - 128, 2 * 1024 - 125};
+      for (int bufferLen : bufferLenList) {
+        resetCipher(transformation, key, iv);
+        
+        int offset = 0;
+        // encrypt (update + doFinal) the data
+        int cipherPos = 0;
+        for (int i = 0; i < dataLen / bufferLen; i ++) {
+          cipherPos += enc.update(plainText, offset, bufferLen, cipherText, 
cipherPos);
+          offset += bufferLen;
+        }
+        cipherPos += enc.doFinal(plainText, offset, dataLen % bufferLen, 
cipherText, cipherPos);
+
+        offset = 0;
+        // decrypt (update + doFinal) the data
+        byte[] realPlainText = new byte[cipherPos + blockSize];
+        int plainPos = 0;
+        for (int i = 0; i < cipherPos / bufferLen; i ++) {
+          plainPos += dec.update(cipherText, offset, bufferLen, realPlainText, 
plainPos);
+          offset += bufferLen;
+        }
+        plainPos += dec.doFinal(cipherText, offset, cipherPos % bufferLen, 
realPlainText, plainPos);
+
+        // verify
+        Assert.assertEquals("random byte array length changes after 
transformation",
+            dataLen, plainPos);
+
+        byte[] shrinkPlainText = new byte[plainPos];
+        System.arraycopy(realPlainText, 0, shrinkPlainText, 0, plainPos);
+        Assert.assertArrayEquals("random byte array contents changes after 
transformation",
+            plainText, shrinkPlainText);
+      }
+    }
+  }
+
+  private void resetCipher(CipherTransformation transformation, byte[] key, 
byte[] iv) {
+    enc = getCipher(transformation);
+    dec = getCipher(transformation);
+
+    try {
+      enc.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (Exception e) {
+      Assert.fail("AES failed initialisation - " + e.toString());
+    }
+
+    try {
+      dec.init(Cipher.DECRYPT_MODE, key, iv);
+    } catch (Exception e) {
+      Assert.fail("AES failed initialisation - " + e.toString());
+    }
+  }
+
+  private Cipher getCipher(CipherTransformation transformation) {
+    try {
+      return (Cipher) ReflectionUtils
+          .newInstance(ReflectionUtils.getClassByName(cipherClass), props,
+              transformation);
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(e);
+    }
+
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/cipher/JceCipherTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/cipher/JceCipherTest.java 
b/src/test/java/org/apache/commons/crypto/cipher/JceCipherTest.java
new file mode 100644
index 0000000..c40aaa1
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/cipher/JceCipherTest.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.crypto.cipher;
+
+public class JceCipherTest extends AbstractCipherTest {
+
+  @Override
+  public void init() {
+    transformations = new CipherTransformation[]{
+        CipherTransformation.AES_CBC_NOPADDING,
+        CipherTransformation.AES_CBC_PKCS5PADDING,
+        CipherTransformation.AES_CTR_NOPADDING};
+    cipherClass = JceCipher.class.getName();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/cipher/OpensslCipherTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/cipher/OpensslCipherTest.java 
b/src/test/java/org/apache/commons/crypto/cipher/OpensslCipherTest.java
new file mode 100644
index 0000000..c1ef849
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/cipher/OpensslCipherTest.java
@@ -0,0 +1,165 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.crypto.cipher;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class OpensslCipherTest extends AbstractCipherTest {
+
+  @Override
+  public void init() {
+    transformations = new CipherTransformation[]{
+        CipherTransformation.AES_CBC_NOPADDING,
+        CipherTransformation.AES_CBC_PKCS5PADDING,
+        CipherTransformation.AES_CTR_NOPADDING};
+    cipherClass = OpensslCipher.class.getName();
+  }
+
+  @Test(expected = NoSuchAlgorithmException.class, timeout=120000)
+  public void testInvalidAlgorithm() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+
+    try {
+      Openssl cipher = Openssl.getInstance("AES2/CTR/NoPadding");
+      Assert.fail("Should specify correct algorithm.");
+    } catch (NoSuchAlgorithmException e) {
+      Assert.assertTrue(e.getMessage().contains("Doesn't support algorithm: 
AES2 and mode: CTR"));
+      throw e;
+    }
+  }
+
+  @Test(expected = NoSuchPaddingException.class, timeout=120000)
+  public void testInvalidPadding() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+
+    try {
+      Openssl cipher = Openssl.getInstance("AES/CTR/NoPadding2");
+      Assert.fail("Should specify correct padding.");
+    } catch (NoSuchPaddingException e) {
+      Assert.assertTrue(e.getMessage().contains("Doesn't support padding: 
NoPadding2"));
+      throw e;
+    }
+  }
+
+  @Test(expected = NoSuchAlgorithmException.class, timeout=120000)
+  public void testInvalidMode() throws Exception {
+    try {
+      Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+      Openssl cipher = Openssl.getInstance("AES/CTR2/NoPadding");
+      Assert.fail("java.security.NoSuchAlgorithmException should be thrown.");
+    } catch (NoSuchAlgorithmException e) {
+      Assert.assertTrue(e.getMessage().contains("Doesn't support algorithm: 
AES and mode: CTR2"));
+      throw e;
+    }
+  }
+
+  @Test(timeout=120000)
+  public void testUpdateArguments() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+    Openssl cipher = 
Openssl.getInstance(CipherTransformation.AES_CTR_NOPADDING.getName());
+    Assert.assertNotNull(cipher);
+
+    cipher.init(Openssl.ENCRYPT_MODE, KEY, IV);
+
+    // Require direct buffers
+    ByteBuffer input = ByteBuffer.allocate(1024);
+    ByteBuffer output = ByteBuffer.allocate(1024);
+
+    try {
+      cipher.update(input, output);
+      Assert.fail("Input and output buffer should be direct buffer.");
+    } catch (IllegalArgumentException e) {
+      Assert.assertTrue(e.getMessage().contains("Direct buffers are 
required"));
+    }
+
+    // Output buffer length should be sufficient to store output data
+    input = ByteBuffer.allocateDirect(1024);
+    output = ByteBuffer.allocateDirect(1000);
+    try {
+      cipher.update(input, output);
+      Assert.fail("Output buffer length should be sufficient " +
+          "to store output data");
+    } catch (ShortBufferException e) {
+      Assert.assertTrue(e.getMessage().contains("Output buffer is not 
sufficient"));
+    }
+  }
+
+  @Test(timeout=120000)
+  public void testDoFinalArguments() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+    Openssl cipher = 
Openssl.getInstance(CipherTransformation.AES_CTR_NOPADDING.getName());
+    Assert.assertNotNull(cipher);
+
+    cipher.init(Openssl.ENCRYPT_MODE, KEY, IV);
+
+    // Require direct buffer
+    ByteBuffer output = ByteBuffer.allocate(1024);
+
+    try {
+      cipher.doFinal(output);
+      Assert.fail("Output buffer should be direct buffer.");
+    } catch (IllegalArgumentException e) {
+      Assert.assertTrue(e.getMessage().contains("Direct buffer is required"));
+    }
+  }
+
+  @Test(expected = InvalidKeyException.class, timeout=120000)
+  public void testInvalidKey() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+    Openssl cipher = 
Openssl.getInstance(CipherTransformation.AES_CTR_NOPADDING.getName());
+    Assert.assertNotNull(cipher);
+
+    final byte[] invalidKey = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+            0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11};
+    try {
+      cipher.init(Openssl.ENCRYPT_MODE, invalidKey, IV);
+      Assert.fail("java.security.InvalidKeyException should be thrown.");
+    } catch (Exception e) {
+      Assert.assertTrue(e.getMessage().contains("Invalid key length."));
+      throw e;
+    }
+  }
+
+  @Test(expected = InvalidAlgorithmParameterException.class, timeout=120000)
+  public void testInvalidIV() throws Exception {
+    Assume.assumeTrue(Openssl.getLoadingFailureReason() == null);
+    Openssl cipher = 
Openssl.getInstance(CipherTransformation.AES_CTR_NOPADDING.getName());
+    Assert.assertNotNull(cipher);
+
+    final byte[] invalidIV = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+            0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11};
+    try {
+      cipher.init(Openssl.ENCRYPT_MODE, KEY, invalidIV);
+      Assert.fail("java.security.InvalidAlgorithmParameterException should be 
thrown.");
+    } catch (Exception e) {
+      Assert.assertTrue(e.getMessage().contains("Wrong IV length."));
+      throw e;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/cipher/TestData.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/cipher/TestData.java 
b/src/test/java/org/apache/commons/crypto/cipher/TestData.java
new file mode 100644
index 0000000..2f8da8f
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/cipher/TestData.java
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.cipher;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestData {
+
+  private static String[] CBCNoPaddingTests = {
+      /*
+       * key_len,key,iv,plainText,cipherText
+       */
+      "128",
+      "2b7e151628aed2a6abf7158809cf4f3c",
+      "000102030405060708090a0b0c0d0e0f",
+      "6bc1bee22e409f96e93d7e117393172a",
+      "7649abac8119b246cee98e9b12e9197d",
+
+      "128",
+      "2b7e151628aed2a6abf7158809cf4f3c",
+      "7649ABAC8119B246CEE98E9B12E9197D",
+      "ae2d8a571e03ac9c9eb76fac45af8e51",
+      "5086cb9b507219ee95db113a917678b2",
+
+      "192",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d7",
+      "9CFC4E967EDB808D679F777BC6702C7D",
+      "30c81c46a35ce411e5fbc1191a0a52ef",
+      "78C57E3F543A18F472756DAC2F018523",
+
+      "192",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d7",
+      "39F23369A9D9BACFA530E26304231461",
+      "f69f2445df4f9b17ad2b417be66c3710",
+      "79ECA9610F0B9AAFB8C7C2D655047A41",
+
+      "256",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+      "000102030405060708090a0b0c0d0e0f",
+      "6bc1bee22e409f96e93d7e117393172a",
+      "f58c4c04d6e5f1ba779eabfb5f7bfbd6",
+
+      "256",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+      "F58C4C04D6E5F1BA779EABFB5F7BFBD6",
+      "ae2d8a571e03ac9c9eb76fac45af8e51",
+      "9cfc4e967edb808d679f777bc6702c7d"
+  };
+
+  private static String[] CBCPKCS5PaddingTests = {
+      /*
+       * key_len,key,iv,plainText,cipherText
+       */
+      //Test#0 for input of 6 bytes
+      "128",
+      "ac5800ac3cb59c7c14f36019e43b44fe",
+      "f013ce1ec901b5b60a85a986b3b72eba",
+      "f6cee5ff28fd",
+      "e8a846fd9718507371604504d4ca1ac7",
+
+      //Test#0 for input of 15 bytes
+      "128",
+      "0784fa652e733cb699f250b0df2c4b41",
+      "106519760fb3ef97e1ccea073b27122d",
+      "6842455a2992c2e5193056a5524075",
+      "56a8e0c3ee3315f913693c0ca781e917",
+
+      //Test#0 for input of 16 bytes
+      "128",
+      "04952c3fcf497a4d449c41e8730c5d9a",
+      "53549bf7d5553b727458c1abaf0ba167",
+      "c9a44f6f75e98ddbca7332167f5c45e3",
+      "7fa290322ca7a1a04b61a1147ff20fe66fde58510a1d0289d11c0ddf6f4decfd",
+
+      //Test#0 for input of 32 bytes
+      "128",
+      "2ae7081caebe54909820620a44a60a0f",
+      "fc5e783fbe7be12f58b1f025d82ada50",
+      "1ba93ee6f83752df47909585b3f28e56693f89e169d3093eee85175ea3a46cd3",
+      
"7944957a99e473e2c07eb496a83ec4e55db2fb44ebdd42bb611e0def29b23a73ac37eb0f4f5d86f090f3ddce3980425a",
+
+      //Test#0 for input of 33 bytes
+      "128",
+      "898be9cc5004ed0fa6e117c9a3099d31",
+      "9dea7621945988f96491083849b068df",
+      "0397f4f6820b1f9386f14403be5ac16e50213bd473b4874b9bcbf5f318ee686b1d",
+      
"e232cd6ef50047801ee681ec30f61d53cfd6b0bca02fd03c1b234baa10ea82ac9dab8b960926433a19ce6dea08677e34"};
+
+  private static String[] cipherCTRTests = {
+      /*
+       * key_len,key,iv,plainText,cipherText
+       */
+      "128",
+      "2b7e151628aed2a6abf7158809cf4f3c",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+      "6bc1bee22e409f96e93d7e117393172a",
+      "874d6191b620e3261bef6864990db6ce",
+
+      "128",
+      "2b7e151628aed2a6abf7158809cf4f3c",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00",
+      "ae2d8a571e03ac9c9eb76fac45af8e51",
+      "9806f66b7970fdff8617187bb9fffdff",
+
+      //Test for input of 15 bytes
+      "128",
+      "2b7e151628aed2a6abf7158809cf4f3c",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01",
+      "30c81c46a35ce411e5fbc1191a0a52",
+      "5ae4df3edbd5d35e5b4f09020db03e",
+
+      "256",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+      "6bc1bee22e409f96e93d7e117393172a",
+      "601ec313775789a5b7a7f504bbf3d228",
+
+      "256",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00",
+      "ae2d8a571e03ac9c9eb76fac45af8e51",
+      "f443e3ca4d62b59aca84e990cacaf5c5",
+
+      //Test for input of 15 bytes
+      "256",
+      "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+      "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01",
+      "30c81c46a35ce411e5fbc1191a0a52",
+      "2b0930daa23de94ce87017ba2d8498" };
+
+    private static Map<CipherTransformation, String[]> testData =
+        new HashMap<CipherTransformation, String[]>();
+
+    static {
+        testData.put(CipherTransformation.AES_CBC_NOPADDING, 
CBCNoPaddingTests);
+        testData.put(CipherTransformation.AES_CBC_PKCS5PADDING,
+            CBCPKCS5PaddingTests);
+        testData.put(CipherTransformation.AES_CTR_NOPADDING, cipherCTRTests);
+    }
+
+    public static String[] getTestData(CipherTransformation transformation) {
+        return testData.get(transformation);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/random/AbstractRandomTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/random/AbstractRandomTest.java 
b/src/test/java/org/apache/commons/crypto/random/AbstractRandomTest.java
new file mode 100644
index 0000000..6c6f692
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/random/AbstractRandomTest.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public abstract class AbstractRandomTest {
+
+  public abstract SecureRandom getSecureRandom() throws IOException;
+
+  @Test(timeout=120000)
+  public void testRandomBytes() throws Exception {
+    SecureRandom random = getSecureRandom();
+    // len = 16
+    checkRandomBytes(random, 16);
+    // len = 32
+    checkRandomBytes(random, 32);
+    // len = 128
+    checkRandomBytes(random, 128);
+    // len = 256
+    checkRandomBytes(random, 256);
+    random.close();
+  }
+
+  /**
+   * Test will timeout if secure random implementation always returns a
+   * constant value.
+   */
+  private void checkRandomBytes(SecureRandom random, int len) {
+    byte[] bytes = new byte[len];
+    byte[] bytes1 = new byte[len];
+    random.nextBytes(bytes);
+    random.nextBytes(bytes1);
+
+    while (Arrays.equals(bytes, bytes1)) {
+      random.nextBytes(bytes1);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/random/TestJavaSecureRandom.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/random/TestJavaSecureRandom.java 
b/src/test/java/org/apache/commons/crypto/random/TestJavaSecureRandom.java
new file mode 100644
index 0000000..a85b7f7
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/random/TestJavaSecureRandom.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.crypto.conf.ConfigurationKeys;
+import static junit.framework.Assert.fail;
+
+public class TestJavaSecureRandom extends AbstractRandomTest {
+
+  @Override
+  public SecureRandom getSecureRandom() throws IOException {
+    Properties props = new Properties();
+    
props.setProperty(ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY,
+        JavaSecureRandom.class.getName());
+    SecureRandom random = SecureRandomFactory.getSecureRandom(props);
+    if ( !(random instanceof JavaSecureRandom)) {
+      fail("The SecureRandom should be: " + JavaSecureRandom.class.getName());
+    }
+    return random;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/random/TestOpensslSecureRandom.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/random/TestOpensslSecureRandom.java 
b/src/test/java/org/apache/commons/crypto/random/TestOpensslSecureRandom.java
new file mode 100644
index 0000000..514d547
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/random/TestOpensslSecureRandom.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.crypto.conf.ConfigurationKeys;
+import static junit.framework.Assert.fail;
+
+public class TestOpensslSecureRandom extends AbstractRandomTest {
+
+  @Override
+  public SecureRandom getSecureRandom() throws IOException {
+    Properties props = new Properties();
+    
props.setProperty(ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY,
+        OpensslSecureRandom.class.getName());
+    SecureRandom random = SecureRandomFactory.getSecureRandom(props);
+    if ( !(random instanceof OpensslSecureRandom)) {
+      fail("The SecureRandom should be: " + 
OpensslSecureRandom.class.getName());
+    }
+    return random;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/random/TestOsSecureRandom.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/random/TestOsSecureRandom.java 
b/src/test/java/org/apache/commons/crypto/random/TestOsSecureRandom.java
new file mode 100644
index 0000000..138f766
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/random/TestOsSecureRandom.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class TestOsSecureRandom extends AbstractRandomTest{
+
+  @Override
+  public SecureRandom getSecureRandom() throws IOException {
+    return new OsSecureRandom(new Properties());
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java 
b/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
new file mode 100644
index 0000000..fe73f00
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
@@ -0,0 +1,456 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.security.SecureRandom;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.cipher.JceCipher;
+import org.apache.commons.crypto.cipher.Openssl;
+import org.apache.commons.crypto.cipher.OpensslCipher;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractCryptoStreamTest {
+  private static final Log LOG= 
LogFactory.getLog(AbstractCryptoStreamTest.class);
+
+  private final int dataLen = 20000;
+  private byte[] data = new byte[dataLen];
+  private byte[] encData;
+  private Properties props = new Properties();
+  protected byte[] key = new byte[16];
+  private byte[] iv = new byte[16];
+  private int count = 10000;
+  protected static int defaultBufferSize = 8192;
+  protected static int smallBufferSize = 1024;
+
+  private final String jceCipherClass = JceCipher.class.getName();
+  private final String opensslCipherClass = OpensslCipher.class.getName();
+  protected CipherTransformation transformation;
+
+  public abstract void setUp() throws IOException;
+
+  @Before
+  public void before() throws IOException {
+    Random random = new SecureRandom();
+    random.nextBytes(data);
+    random.nextBytes(key);
+    random.nextBytes(iv);
+    setUp();
+    prepareData();
+  }
+
+  /** Test skip. */
+  @Test(timeout=120000)
+  public void testSkip() throws Exception {
+    doSkipTest(jceCipherClass, false);
+    doSkipTest(opensslCipherClass, false);
+
+    doSkipTest(jceCipherClass, true);
+    doSkipTest(opensslCipherClass, true);
+  }
+
+  /** Test byte buffer read with different buffer size. */
+  @Test(timeout=120000)
+  public void testByteBufferRead() throws Exception {
+    doByteBufferRead(jceCipherClass, false);
+    doByteBufferRead(opensslCipherClass, false);
+
+    doByteBufferRead(jceCipherClass, true);
+    doByteBufferRead(opensslCipherClass, true);
+  }
+
+  /** Test byte buffer write. */
+  @Test(timeout=120000)
+  public void testByteBufferWrite() throws Exception {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    doByteBufferWrite(jceCipherClass, baos, false);
+    doByteBufferWrite(opensslCipherClass, baos, false);
+
+    doByteBufferWrite(jceCipherClass, baos, true);
+    doByteBufferWrite(opensslCipherClass, baos, true);
+  }
+
+  private void doSkipTest(String cipherClass, boolean withChannel) throws 
IOException {
+    InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    byte[] result = new byte[dataLen];
+    int n1 = readAll(in, result, 0, dataLen / 3);
+
+    long skipped = in.skip(dataLen / 3);
+    int n2 = readAll(in, result, 0, dataLen);
+
+    Assert.assertEquals(dataLen, n1 + skipped + n2);
+    byte[] readData = new byte[n2];
+    System.arraycopy(result, 0, readData, 0, n2);
+    byte[] expectedData = new byte[n2];
+    System.arraycopy(data, dataLen - n2, expectedData, 0, n2);
+    Assert.assertArrayEquals(readData, expectedData);
+
+    try {
+      skipped = in.skip(-3);
+      Assert.fail("Skip Negative length should fail.");
+    } catch (IllegalArgumentException e) {
+      Assert.assertTrue(e.getMessage().contains("Negative skip length"));
+    }
+
+    // Skip after EOF
+    skipped = in.skip(3);
+    Assert.assertEquals(skipped, 0);
+
+    in.close();
+  }
+
+  private void doByteBufferRead(String cipherClass, boolean withChannel) 
throws Exception {
+    // Default buffer size, initial buffer position is 0
+    InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    ByteBuffer buf = ByteBuffer.allocate(dataLen + 100);
+    byteBufferReadCheck(in, buf, 0);
+    in.close();
+
+    // Default buffer size, initial buffer position is not 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 11);
+    in.close();
+
+    // Small buffer size, initial buffer position is 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), smallBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 0);
+    in.close();
+
+    // Small buffer size, initial buffer position is not 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), smallBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 11);
+    in.close();
+
+    // Direct buffer, default buffer size, initial buffer position is 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    buf = ByteBuffer.allocateDirect(dataLen + 100);
+    byteBufferReadCheck(in, buf, 0);
+    in.close();
+
+    // Direct buffer, default buffer size, initial buffer position is not 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 11);
+    in.close();
+
+    // Direct buffer, small buffer size, initial buffer position is 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), smallBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 0);
+    in.close();
+
+    // Direct buffer, small buffer size, initial buffer position is not 0
+    in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), smallBufferSize, iv, withChannel);
+    buf.clear();
+    byteBufferReadCheck(in, buf, 11);
+    in.close();
+  }
+
+  private void doByteBufferWrite(String cipherClass, ByteArrayOutputStream 
baos,
+                                 boolean withChannel)
+      throws Exception {
+    baos.reset();
+    CryptoOutputStream out =
+        getCryptoOutputStream(baos, getCipher(cipherClass), defaultBufferSize,
+            iv, withChannel);
+    ByteBuffer buf = ByteBuffer.allocateDirect(dataLen / 2);
+    buf.put(data, 0, dataLen / 2);
+    buf.flip();
+    int n1 = out.write(buf);
+
+    buf.clear();
+    buf.put(data, n1, dataLen / 3);
+    buf.flip();
+    int n2 = out.write(buf);
+
+    buf.clear();
+    buf.put(data, n1 + n2, dataLen - n1 - n2);
+    buf.flip();
+    int n3 = out.write(buf);
+
+    Assert.assertEquals(dataLen, n1 + n2 + n3);
+
+    out.flush();
+
+    InputStream in = getCryptoInputStream(new ByteArrayInputStream(encData),
+        getCipher(cipherClass), defaultBufferSize, iv, withChannel);
+    buf = ByteBuffer.allocate(dataLen + 100);
+    byteBufferReadCheck(in, buf, 0);
+    in.close();
+  }
+
+  private void byteBufferReadCheck(InputStream in, ByteBuffer buf,
+      int bufPos) throws Exception {
+    buf.position(bufPos);
+    int n = ((ReadableByteChannel) in).read(buf);
+    Assert.assertEquals(bufPos + n, buf.position());
+    byte[] readData = new byte[n];
+    buf.rewind();
+    buf.position(bufPos);
+    buf.get(readData);
+    byte[] expectedData = new byte[n];
+    System.arraycopy(data, 0, expectedData, 0, n);
+    Assert.assertArrayEquals(readData, expectedData);
+  }
+
+  private void prepareData() throws IOException {
+    Cipher cipher = null;
+    try {
+      cipher = (Cipher)ReflectionUtils.newInstance(
+          ReflectionUtils.getClassByName(jceCipherClass), props, 
transformation);
+    } catch (ClassNotFoundException cnfe) {
+      throw new IOException("Illegal crypto cipher!");
+    }
+
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    OutputStream out = new CryptoOutputStream(baos, cipher, defaultBufferSize, 
key, iv);
+    out.write(data);
+    out.flush();
+    out.close();
+    encData = baos.toByteArray();
+  }
+
+  protected CryptoInputStream getCryptoInputStream(ByteArrayInputStream bais,
+                                                   Cipher cipher,
+                                                   int bufferSize, byte[] iv,
+                                                   boolean withChannel) throws
+      IOException {
+    if (withChannel) {
+      return new CryptoInputStream(Channels.newChannel(bais), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CryptoInputStream(bais, cipher, bufferSize, key, iv);
+    }
+  }
+
+  protected CryptoOutputStream getCryptoOutputStream(ByteArrayOutputStream 
baos,
+                                                   Cipher cipher,
+                                                   int bufferSize, byte[] iv,
+                                                   boolean withChannel) throws
+      IOException {
+    if (withChannel) {
+      return new CryptoOutputStream(Channels.newChannel(baos), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CryptoOutputStream(baos, cipher, bufferSize, key, iv);
+    }
+  }
+
+  private int readAll(InputStream in, byte[] b, int offset, int len)
+      throws IOException {
+    int n = 0;
+    int total = 0;
+    while (n != -1) {
+      total += n;
+      if (total >= len) {
+        break;
+      }
+      n = in.read(b, offset + total, len - total);
+    }
+
+    return total;
+  }
+
+  protected Cipher getCipher(String cipherClass) throws IOException {
+    try {
+      return (Cipher)ReflectionUtils.newInstance(
+          ReflectionUtils.getClassByName(cipherClass), props, transformation);
+    } catch (ClassNotFoundException cnfe) {
+      throw new IOException("Illegal crypto cipher!");
+    }
+  }
+
+  @Test
+  public void testReadWrite() throws Exception {
+    Assert.assertEquals(null, Openssl.getLoadingFailureReason());
+    doReadWriteTest(0, jceCipherClass, jceCipherClass, iv);
+    doReadWriteTest(0, opensslCipherClass, opensslCipherClass, iv);
+    doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
+    doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
+    doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
+    doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
+    // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff
+    for(int i = 0; i < 8; i++) {
+      iv[8 + i] = (byte) 0xff;
+    }
+    doReadWriteTest(count, jceCipherClass, jceCipherClass, iv);
+    doReadWriteTest(count, opensslCipherClass, opensslCipherClass, iv);
+    doReadWriteTest(count, jceCipherClass, opensslCipherClass, iv);
+    doReadWriteTest(count, opensslCipherClass, jceCipherClass, iv);
+  }
+
+  private void doReadWriteTest(int count, String encCipherClass,
+                               String decCipherClass, byte[] iv) throws 
IOException {
+    doReadWriteTestForInputStream(count, encCipherClass, decCipherClass, iv);
+    doReadWriteTestForReadableByteChannel(count, encCipherClass, 
decCipherClass,
+        iv);
+  }
+
+  private void doReadWriteTestForInputStream(int count, String encCipherClass,
+                                             String decCipherClass, byte[] iv) 
throws IOException {
+    Cipher encCipher = getCipher(encCipherClass);
+    LOG.debug("Created a cipher object of type: " + encCipherClass);
+
+    // Generate data
+    SecureRandom random = new SecureRandom();
+    byte[] originalData = new byte[count];
+    byte[] decryptedData = new byte[count];
+    random.nextBytes(originalData);
+    LOG.debug("Generated " + count + " records");
+
+    // Encrypt data
+    ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
+    CryptoOutputStream out =
+        getCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
+            false);
+    out.write(originalData, 0, originalData.length);
+    out.flush();
+    out.close();
+    LOG.debug("Finished encrypting data");
+
+    Cipher decCipher = getCipher(decCipherClass);
+    LOG.debug("Created a cipher object of type: " + decCipherClass);
+
+    // Decrypt data
+    CryptoInputStream in = getCryptoInputStream(
+        new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+        defaultBufferSize, iv, false);
+
+    // Check
+    int remainingToRead = count;
+    int offset = 0;
+    while (remainingToRead > 0) {
+      int n = in.read(decryptedData, offset, decryptedData.length - offset);
+      if (n >=0) {
+        remainingToRead -= n;
+        offset += n;
+      }
+    }
+
+    Assert.assertArrayEquals("originalData and decryptedData not equal",
+        originalData, decryptedData);
+
+    // Decrypt data byte-at-a-time
+    in = getCryptoInputStream(
+        new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+        defaultBufferSize, iv, false);
+
+    // Check
+    DataInputStream originalIn = new DataInputStream(new 
BufferedInputStream(new ByteArrayInputStream(originalData)));
+    int expected;
+    do {
+      expected = originalIn.read();
+      Assert.assertEquals("Decrypted stream read by byte does not match",
+          expected, in.read());
+    } while (expected != -1);
+
+    LOG.debug("SUCCESS! Completed checking " + count + " records");
+  }
+
+  private void doReadWriteTestForReadableByteChannel(int count,
+                                                     String encCipherClass,
+                                                     String decCipherClass,
+                                                     byte[] iv) throws 
IOException {
+    Cipher encCipher = getCipher(encCipherClass);
+    LOG.debug("Created a cipher object of type: " + encCipherClass);
+
+    // Generate data
+    SecureRandom random = new SecureRandom();
+    byte[] originalData = new byte[count];
+    byte[] decryptedData = new byte[count];
+    random.nextBytes(originalData);
+    LOG.debug("Generated " + count + " records");
+
+    // Encrypt data
+    ByteArrayOutputStream encryptedData = new ByteArrayOutputStream();
+    CryptoOutputStream out =
+        getCryptoOutputStream(encryptedData, encCipher, defaultBufferSize, iv,
+            true);
+    out.write(originalData, 0, originalData.length);
+    out.flush();
+    out.close();
+    LOG.debug("Finished encrypting data");
+
+    Cipher decCipher = getCipher(decCipherClass);
+    LOG.debug("Created a cipher object of type: " + decCipherClass);
+
+    // Decrypt data
+    CryptoInputStream in = getCryptoInputStream(
+        new ByteArrayInputStream(encryptedData.toByteArray()), decCipher,
+        defaultBufferSize, iv, true);
+
+    // Check
+    int remainingToRead = count;
+    int offset = 0;
+    while (remainingToRead > 0) {
+      int n = in.read(decryptedData, offset, decryptedData.length - offset);
+      if (n >=0) {
+        remainingToRead -= n;
+        offset += n;
+      }
+    }
+
+    Assert.assertArrayEquals("originalData and decryptedData not equal",
+        originalData, decryptedData);
+
+    // Decrypt data byte-at-a-time
+    in = getCryptoInputStream(new ByteArrayInputStream(
+        encryptedData.toByteArray()),decCipher,defaultBufferSize,iv,true);
+
+    // Check
+    DataInputStream originalIn = new DataInputStream(new 
BufferedInputStream(new ByteArrayInputStream(originalData)));
+    int expected;
+    do {
+      expected = originalIn.read();
+      Assert.assertEquals("Decrypted stream read by byte does not match",
+          expected, in.read());
+    } while (expected != -1);
+
+    LOG.debug("SUCCESS! Completed checking " + count + " records");
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
 
b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
new file mode 100644
index 0000000..743a3ac
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+
+import org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CBCNoPaddingCryptoStreamTest extends AbstractCryptoStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CBC_NOPADDING;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
 
b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
new file mode 100644
index 0000000..cb65dac
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+
+import org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CBCPKCS5PaddingCryptoStreamTest extends AbstractCryptoStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CBC_PKCS5PADDING;
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java 
b/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
new file mode 100644
index 0000000..1a50382
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CTRCryptoStreamTest extends AbstractCryptoStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CTR_NOPADDING;
+  }
+
+  @Override
+  protected CTRCryptoInputStream getCryptoInputStream
+      (ByteArrayInputStream bais, Cipher cipher, int
+      bufferSize,byte[] iv, boolean withChannel)
+      throws IOException {
+    if (withChannel) {
+      return new CTRCryptoInputStream(Channels.newChannel(bais), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CTRCryptoInputStream(bais, cipher, bufferSize, key, iv);
+    }
+  }
+
+  @Override
+  protected CTRCryptoOutputStream getCryptoOutputStream(ByteArrayOutputStream 
baos,Cipher cipher, int
+      bufferSize,  byte[] iv, boolean withChannel)
+      throws IOException {
+    if (withChannel) {
+      return new CTRCryptoOutputStream(Channels.newChannel(baos), cipher, 
bufferSize, key, iv);
+    } else {
+      return new CTRCryptoOutputStream(baos, cipher, bufferSize, key, iv);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
 
b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
new file mode 100644
index 0000000..1bbf600
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+
+import org.apache.commons.crypto.cipher.CipherTransformation;
+
+public class CTRNoPaddingCryptoStreamTest extends AbstractCryptoStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CTR_NOPADDING;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
 
b/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
new file mode 100644
index 0000000..5907ba8
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
@@ -0,0 +1,381 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.crypto.stream;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.cipher.JceCipher;
+import org.apache.commons.crypto.cipher.OpensslCipher;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.Random;
+
+public class PositionedCryptoInputStreamTest {
+
+  private final int dataLen = 20000;
+  private byte[] testData = new byte[dataLen];
+  private byte[] encData;
+  private Properties props = new Properties();
+  private byte[] key = new byte[16];
+  private byte[] iv = new byte[16];
+  int bufferSize = 2048;
+  int bufferSizeLess = bufferSize - 1;
+  int bufferSizeMore = bufferSize + 1;
+  int length = 1024;
+  int lengthLess = length - 1;
+  int lengthMore = length + 1;
+
+  private final String jceCipherClass = JceCipher.class.getName();
+  private final String opensslCipherClass = OpensslCipher.class.getName();
+  private CipherTransformation transformation =
+                                CipherTransformation.AES_CTR_NOPADDING;
+
+  @Before
+  public void before() throws IOException {
+    Random random = new SecureRandom();
+    random.nextBytes(testData);
+    random.nextBytes(key);
+    random.nextBytes(iv);
+    prepareData();
+  }
+
+  private void prepareData() throws IOException {
+    Cipher cipher = null;
+    try {
+      cipher = (Cipher)ReflectionUtils.newInstance(
+              ReflectionUtils.getClassByName(jceCipherClass),
+              props, transformation);
+    } catch (ClassNotFoundException cnfe) {
+      throw new IOException("Illegal crypto cipher!");
+    }
+
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    // encryption data
+    OutputStream out = new CryptoOutputStream(baos, cipher, bufferSize, key, 
iv);
+    out.write(testData);
+    out.flush();
+    out.close();
+    encData = baos.toByteArray();
+  }
+
+  public void setUp() throws IOException {}
+
+  private PositionedCryptoInputStream getCryptoInputStream(Cipher cipher,
+      int bufferSize) throws IOException {
+    return new PositionedCryptoInputStream(new PositionedInputForTest(
+      Arrays.copyOf(encData, encData.length)), cipher, bufferSize, key, iv, 0);
+  }
+
+  @Test
+  public void doTest() throws Exception {
+    testCipher(jceCipherClass);
+    testCipher(opensslCipherClass);
+  }
+
+  private void testCipher(String cipherClass) throws Exception {
+    doPositionedReadTests(cipherClass);
+    doReadFullyTests(cipherClass);
+    doSeekTests(cipherClass);
+    doMultipleReadTest(cipherClass);
+  }
+
+  // when there are multiple positioned read actions and one read action,
+  // they will not interfere each other.
+  private void doMultipleReadTest(String cipherClass) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    int position = 0;
+    while (in.available() > 0) {
+      ByteBuffer buf = ByteBuffer.allocate(length);
+      byte[] bytes1 = new byte[length];
+      byte[] bytes2 = new byte[lengthLess];
+      // do the read and position read
+      int pn1 = in.read(position, bytes1, 0, length);
+      int n = in.read(buf);
+      int pn2 = in.read(position, bytes2, 0, lengthLess);
+
+      // verify the result
+      if (pn1 > 0) {
+        compareByteArray(testData, position, bytes1, pn1);
+      }
+
+      if (pn2 > 0) {
+        compareByteArray(testData, position, bytes2, pn2);
+      }
+
+      if (n > 0) {
+        compareByteArray(testData, position, buf.array(), n);
+        position += n;
+      } else {
+        break;
+      }
+    }
+    in.close();
+  }
+
+  private void doPositionedReadTests(String cipherClass) throws Exception {
+    // test with different bufferSize when position = 0
+    testPositionedReadLoop(cipherClass, 0, length, bufferSize, dataLen);
+    testPositionedReadLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
+    testPositionedReadLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
+    // test with different position when bufferSize = 2048
+    testPositionedReadLoop(cipherClass, dataLen / 2, length, bufferSize, 
dataLen);
+    testPositionedReadLoop(cipherClass, dataLen / 2 - 1, length,
+            bufferSizeLess, dataLen);
+    testPositionedReadLoop(cipherClass, dataLen / 2 + 1, length,
+            bufferSizeMore, dataLen);
+    // position = -1 or position = max length, read nothing and return -1
+    testPositionedReadNone(cipherClass, -1, length, bufferSize);
+    testPositionedReadNone(cipherClass, dataLen, length, bufferSize);
+  }
+
+  private void doReadFullyTests(String cipherClass) throws Exception {
+    // test with different bufferSize when position = 0
+    testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
+    testReadFullyLoop(cipherClass, 0, length, bufferSizeLess, dataLen);
+    testReadFullyLoop(cipherClass, 0, length, bufferSizeMore, dataLen);
+    // test with different length when position = 0
+    testReadFullyLoop(cipherClass, 0, length, bufferSize, dataLen);
+    testReadFullyLoop(cipherClass, 0, lengthLess, bufferSize, dataLen);
+    testReadFullyLoop(cipherClass, 0, lengthMore, bufferSize, dataLen);
+    // test read fully failed
+    testReadFullyFailed(cipherClass, -1, length, bufferSize);
+    testReadFullyFailed(cipherClass, dataLen, length, bufferSize);
+    testReadFullyFailed(cipherClass, dataLen - length + 1, length, bufferSize);
+  }
+
+  private void doSeekTests(String cipherClass) throws Exception {
+    // test with different length when position = 0
+    testSeekLoop(cipherClass, 0, length, bufferSize);
+    testSeekLoop(cipherClass, 0, lengthLess, bufferSize);
+    testSeekLoop(cipherClass, 0, lengthMore, bufferSize);
+    // there should be none data read when position = dataLen
+    testSeekLoop(cipherClass, dataLen, length, bufferSize);
+    // test exception when position = -1
+    testSeekFailed(cipherClass, -1, bufferSize);
+  }
+
+  private void testSeekLoop(String cipherClass, int position, int length,
+      int bufferSize) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    while (in.available() > 0) {
+      in.seek(position);
+      ByteBuffer buf = ByteBuffer.allocate(length);
+      int n = in.read(buf);
+      if (n > 0) {
+        compareByteArray(testData, position, buf.array(), n);
+        position += n;
+      } else {
+        break;
+      }
+    }
+    in.close();
+  }
+
+  // test for the out of index position, eg, -1.
+  private void testSeekFailed(String cipherClass, int position,
+      int bufferSize) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    try {
+      in.seek(position);
+      Assert.fail("Excepted exception for cannot seek to negative offset.");
+    } catch (IllegalArgumentException iae) {
+    }
+    in.close();
+  }
+
+  private void testPositionedReadLoop(String cipherClass, int position,
+      int length, int bufferSize, int total) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    // do the position read until the end of data
+    while (position < total) {
+      byte[] bytes = new byte[length];
+      int n = in.read(position, bytes, 0, length);
+      if (n >= 0) {
+        compareByteArray(testData, position, bytes, n);
+        position += n;
+      } else {
+        break;
+      }
+    }
+    in.close();
+  }
+
+  // test for the out of index position, eg, -1.
+  private void testPositionedReadNone(String cipherClass, int position,
+      int length, int bufferSize) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    byte[] bytes = new byte[length];
+    int n = in.read(position, bytes, 0, length);
+    Assert.assertEquals(n, -1);
+    in.close();
+  }
+
+  private void testReadFullyLoop(String cipherClass,int position,
+      int length, int bufferSize, int total) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+
+    // do the position read full until remain < length
+    while (position + length <= total) {
+      byte[] bytes = new byte[length];
+      in.readFully(position, bytes, 0, length);
+      compareByteArray(testData, position, bytes, length);
+      position += length;
+    }
+
+    in.close();
+  }
+
+  // test for the End of file reached before reading fully
+  private void testReadFullyFailed(String cipherClass, int position,
+      int length, int bufferSize) throws Exception {
+    PositionedCryptoInputStream in = getCryptoInputStream(
+            getCipher(cipherClass), bufferSize);
+    byte[] bytes = new byte[length];
+    try {
+      in.readFully(position, bytes, 0, length);
+      Assert.fail("Excepted EOFException.");
+    } catch (IOException ioe) {
+      // excepted exception
+    }
+    in.close();
+  }
+
+  // compare the data from pos with length and data2 from 0 with length
+  private void compareByteArray(byte[] data1, int pos, byte[] data2, int 
length) {
+    byte[] expectedData = new byte[length];
+    byte[] realData = new byte[length];
+    // get the expected data with the position and length
+    System.arraycopy(data1, pos, expectedData, 0, length);
+    // get the real data
+    System.arraycopy(data2, 0, realData, 0, length);
+    Assert.assertArrayEquals(expectedData, realData);
+  }
+
+  private Cipher getCipher(String cipherClass) throws IOException {
+    try {
+      return (Cipher)ReflectionUtils.newInstance(
+          ReflectionUtils.getClassByName(cipherClass), props, transformation);
+    } catch (ClassNotFoundException cnfe) {
+      throw new IOException("Illegal crypto cipher!");
+    }
+  }
+
+  class PositionedInputForTest implements Input {
+
+    byte[] data;
+    long pos;
+    long count;
+
+    public PositionedInputForTest(byte[] data) {
+      this.data = data;
+      this.pos = 0;
+      this.count = data.length;
+    }
+
+    @Override
+    public int read(ByteBuffer dst) throws IOException {
+      int remaining = (int)(count - pos);
+      if(remaining <= 0) {
+        return -1;
+      }
+
+      int length = Math.min(dst.remaining(), remaining);
+      dst.put(data, (int)pos, length);
+      pos += length;
+      return length;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+      if (n <= 0) {
+        return 0;
+      }
+
+      long remaining = count - pos;
+      if(remaining < n) {
+        n = remaining;
+      }
+      pos += n;
+
+      return n;
+    }
+
+    @Override
+    public int read(long position, byte[] buffer, int offset, int length)
+            throws IOException {
+      if (buffer == null) {
+        throw new NullPointerException();
+      } else if (offset < 0 || length < 0 || length > buffer.length - offset) {
+        throw new IndexOutOfBoundsException();
+      }
+
+      if (position < 0 || position >= count) {
+        return -1;
+      }
+
+      long avail = count - position;
+      if (length > avail) {
+        length = (int)avail;
+      }
+      if (length <= 0) {
+        return 0;
+      }
+      System.arraycopy(data, (int)position, buffer, offset, length);
+      return length;
+    }
+
+    @Override
+    public void seek(long position) throws IOException {
+      if (pos < 0) {
+        throw new IOException("Negative seek offset");
+      } else if (position >= 0 && position < count) {
+        pos = position;
+      } else {
+        // to the end of file
+        pos = count;
+      }
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+
+    @Override
+    public int available() throws IOException {
+      return (int)(count - pos);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/test/java/org/apache/commons/crypto/utils/UtilsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/utils/UtilsTest.java 
b/src/test/java/org/apache/commons/crypto/utils/UtilsTest.java
new file mode 100644
index 0000000..a0475be
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/utils/UtilsTest.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.utils;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class UtilsTest {
+  @Test
+  public void testSplitOmitEmptyLine() {
+    List<String> clazzNames = Utils.splitClassNames("", ",");
+    Assert.assertEquals(Collections.<String>emptyList(), clazzNames);
+
+    clazzNames = Utils.splitClassNames("a,b", ",");
+    Assert.assertEquals(Arrays.asList("a", "b"), clazzNames);
+    clazzNames = Utils.splitClassNames("a,b,", ",");
+    Assert.assertEquals(Arrays.asList("a", "b"), clazzNames);
+    clazzNames = Utils.splitClassNames("a, b,", ",");
+    Assert.assertEquals(Arrays.asList("a", "b"), clazzNames);
+  }
+}

Reply via email to