You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by di...@apache.org on 2016/04/26 05:38:47 UTC

[1/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke via Dian Fu)

Repository: commons-crypto
Updated Branches:
  refs/heads/master f819dd404 -> ad81d236a


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.java
new file mode 100644
index 0000000..617f1e3
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/PositionedCipherInputStreamTest.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 PositionedCipherInputStreamTest {
+
+  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 CipherOutputStream(baos, cipher, bufferSize, key, iv);
+    out.write(testData);
+    out.flush();
+    out.close();
+    encData = baos.toByteArray();
+  }
+
+  public void setUp() throws IOException {}
+
+  private PositionedCipherInputStream getCipherInputStream(Cipher cipher,
+                                                           int bufferSize) throws IOException {
+    return new PositionedCipherInputStream(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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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 {
+    PositionedCipherInputStream in = getCipherInputStream(
+            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/ad81d236/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
deleted file mode 100644
index 5907ba8..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/**
- * 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);
-    }
-  }
-}


[2/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke via Dian Fu)

Posted by di...@apache.org.
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
deleted file mode 100644
index 3d3ac8d..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/**
- * 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 java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.WritableByteChannel;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.stream.output.ChannelOutput;
-import org.apache.commons.crypto.stream.output.Output;
-import org.apache.commons.crypto.stream.output.StreamOutput;
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * CryptoOutputStream encrypts data and writes to the under layer output. It supports
- * any mode of operations such as AES CBC/CTR/GCM mode in concept. It is not thread-safe.
- */
-
-public class CryptoOutputStream extends OutputStream implements
-    WritableByteChannel {
-  private final byte[] oneByteBuf = new byte[1];
-
-  protected Output output;
-  protected final Cipher cipher;
-  protected final int bufferSize;
-
-  protected final byte[] key;
-  protected final byte[] initIV;
-  protected byte[] iv;
-
-  protected boolean closed;
-
-  /**
-   * Input data buffer. The data starts at inBuffer.position() and ends at
-   * inBuffer.limit().
-   */
-  protected ByteBuffer inBuffer;
-
-  /**
-   * Encrypted data buffer. The data starts at outBuffer.position() and ends at
-   * outBuffer.limit();
-   */
-  protected ByteBuffer outBuffer;
-
-  public CryptoOutputStream(CipherTransformation transformation,
-      Properties props, OutputStream out, byte[] key, byte[] iv)
-      throws IOException {
-    this(out, Utils.getCipherInstance(transformation, props),
-        Utils.getBufferSize(props), key, iv);
-  }
-
-  public CryptoOutputStream(CipherTransformation transformation,
-      Properties props, WritableByteChannel out, byte[] key, byte[] iv)
-      throws IOException {
-    this(out, Utils.getCipherInstance(transformation, props),
-        Utils.getBufferSize(props), key, iv);
-  }
-
-  public CryptoOutputStream(OutputStream out, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv);
-  }
-
-  public CryptoOutputStream(WritableByteChannel channel, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(new ChannelOutput(channel), cipher, bufferSize, key, iv);
-  }
-
-  protected CryptoOutputStream(Output output, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv)
-      throws IOException {
-
-    this.output = output;
-    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
-    this.cipher = cipher;
-    this.key = key.clone();
-    this.initIV = iv.clone();
-    this.iv = iv.clone();
-    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
-    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
-        cipher.getTransformation().getAlgorithmBlockSize());
-
-    initCipher();
-  }
-
-  @Override
-  public void write(int b) throws IOException {
-    oneByteBuf[0] = (byte)(b & 0xff);
-    write(oneByteBuf, 0, oneByteBuf.length);
-  }
-
-  /**
-   * Encryption is buffer based.
-   * If there is enough room in {@link #inBuffer}, then write to this buffer.
-   * If {@link #inBuffer} is full, then do encryption and write data to the
-   * underlying stream.
-   * @param b the data.
-   * @param off the start offset in the data.
-   * @param len the number of bytes to write.
-   * @throws IOException
-   */
-  public void write(byte[] b, int off, int len) throws IOException {
-    checkStream();
-    if (b == null) {
-      throw new NullPointerException();
-    } else if (off < 0 || len < 0 || off > b.length ||
-        len > b.length - off) {
-      throw new IndexOutOfBoundsException();
-    }
-
-    while (len > 0) {
-      final int remaining = inBuffer.remaining();
-      if (len < remaining) {
-        inBuffer.put(b, off, len);
-        len = 0;
-      } else {
-        inBuffer.put(b, off, remaining);
-        off += remaining;
-        len -= remaining;
-        encrypt();
-      }
-    }
-  }
-
-  /**
-   * To flush, we need to encrypt the data in the buffer and write to the
-   * underlying stream, then do the flush.
-   */
-  @Override
-  public void flush() throws IOException {
-    checkStream();
-    encrypt();
-    output.flush();
-    super.flush();
-  }
-
-  @Override
-  public void close() throws IOException {
-    if (closed) {
-      return;
-    }
-
-    try {
-      encryptFinal();
-      output.close();
-      freeBuffers();
-      cipher.close();
-      super.close();
-    } finally {
-      closed = true;
-    }
-  }
-
-  @Override
-  public boolean isOpen() {
-    return !closed;
-  }
-
-  @Override
-  public int write(ByteBuffer src) throws IOException {
-    checkStream();
-    final int len = src.remaining();
-    int remaining = len;
-    while (remaining > 0) {
-      final int space = inBuffer.remaining();
-      if (remaining < space) {
-        inBuffer.put(src);
-        remaining = 0;
-      } else {
-        // to void copy twice, we set the limit to copy directly
-        final int oldLimit = src.limit();
-        final int newLimit = src.position() + space;
-        src.limit(newLimit);
-
-        inBuffer.put(src);
-
-        // restore the old limit
-        src.limit(oldLimit);
-
-        remaining -= space;
-        encrypt();
-      }
-    }
-
-    return len;
-  }
-
-  /** Initialize the cipher. */
-  protected void initCipher()
-      throws IOException {
-    try {
-      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    } catch(InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-  }
-
-  /**
-   * Do the encryption, input is {@link #inBuffer} and output is
-   * {@link #outBuffer}.
-   */
-  protected void encrypt() throws IOException {
-
-    inBuffer.flip();
-    outBuffer.clear();
-
-    try {
-      cipher.update(inBuffer, outBuffer);
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    }
-
-    inBuffer.clear();
-    outBuffer.flip();
-
-    // write to output
-    output.write(outBuffer);
-  }
-
-  /**
-   * Do final encryption of the last data
-   */
-  protected void encryptFinal() throws IOException {
-    inBuffer.flip();
-    outBuffer.clear();
-
-    try {
-      cipher.doFinal(inBuffer, outBuffer);
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    } catch (IllegalBlockSizeException e) {
-      throw new IOException(e);
-    } catch( BadPaddingException e) {
-      throw new IOException(e);
-    }
-
-    inBuffer.clear();
-    outBuffer.flip();
-
-    // write to output
-    output.write(outBuffer);
-  }
-
-  protected void checkStream() throws IOException {
-    if (closed) {
-      throw new IOException("Stream closed");
-    }
-  }
-
-  /** Forcibly free the direct buffers. */
-  protected void freeBuffers() {
-    Utils.freeDirectBuffer(inBuffer);
-    Utils.freeDirectBuffer(outBuffer);
-  }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
new file mode 100644
index 0000000..c6a4169
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/PositionedCipherInputStream.java
@@ -0,0 +1,311 @@
+/**
+ * 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 java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherFactory;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.utils.IOUtils;
+import org.apache.commons.crypto.utils.Utils;
+
+import static org.apache.commons.crypto.cipher.CipherTransformation.AES_CTR_NOPADDING;
+
+/**
+ * PositionedCipherInputStream provides the capability to decrypt the stream starting
+ * at random position as well as provides the foundation for positioned read for
+ * decrypting. This needs a stream cipher mode such as AES CTR mode.
+ */
+public class PositionedCipherInputStream extends CTRCipherInputStream {
+
+  /**
+   * DirectBuffer pool
+   */
+  private final Queue<ByteBuffer> bufferPool = new
+      ConcurrentLinkedQueue<ByteBuffer>();
+
+  /**
+   * Cipher pool
+   */
+  private final Queue<CipherState> cipherPool = new
+      ConcurrentLinkedQueue<CipherState>();
+
+  public PositionedCipherInputStream(Properties props, Input in,
+                                     byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(in, Utils.getCipherInstance(AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public PositionedCipherInputStream(
+      Input input,
+      Cipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv,
+      long streamOffset) throws IOException {
+    super(input, cipher, bufferSize, key, iv, streamOffset);
+  }
+
+  /**
+   * Read upto the specified number of bytes from a given position
+   * within a stream and return the number of bytes read. This does not
+   * change the current offset of the stream, and is thread-safe.
+   */
+  public int read(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    checkStream();
+    final int n = input.read(position, buffer, offset, length);
+    if (n > 0) {
+      // This operation does not change the current offset of the file
+      decrypt(position, buffer, offset, n);
+    }
+    return n;
+  }
+
+  /**
+   * Read the specified number of bytes from a given position within a stream.
+   * This does not change the current offset of the stream and is thread-safe.
+   */
+  public void readFully(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    checkStream();
+    IOUtils.readFully(input, position, buffer, offset, length);
+    if (length > 0) {
+      // This operation does not change the current offset of the file
+      decrypt(position, buffer, offset, length);
+    }
+  }
+
+  public void readFully(long position, byte[] buffer) throws IOException {
+    readFully(position, buffer, 0, buffer.length);
+  }
+
+  /**
+   * Decrypt length bytes in buffer starting at offset. Output is also put
+   * into buffer starting at offset. It is thread-safe.
+   */
+  protected void decrypt(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    ByteBuffer inBuffer = getBuffer();
+    ByteBuffer outBuffer = getBuffer();
+    CipherState state = null;
+    try {
+      state = getCipherState();
+      byte[] iv = getInitIV().clone();
+      resetCipher(state, position, iv);
+      byte padding = getPadding(position);
+      inBuffer.position(padding); // Set proper position for input data.
+
+      int n = 0;
+      while (n < length) {
+        int toDecrypt = Math.min(length - n, inBuffer.remaining());
+        inBuffer.put(buffer, offset + n, toDecrypt);
+
+        // Do decryption
+        decrypt(state, inBuffer, outBuffer, padding);
+
+        outBuffer.get(buffer, offset + n, toDecrypt);
+        n += toDecrypt;
+        padding = postDecryption(state, inBuffer, position + n, iv);
+      }
+    } finally {
+      returnBuffer(inBuffer);
+      returnBuffer(outBuffer);
+      returnCipherState(state);
+    }
+  }
+
+  /**
+   * Do the decryption using inBuffer as input and outBuffer as output.
+   * Upon return, inBuffer is cleared; the decrypted data starts at
+   * outBuffer.position() and ends at outBuffer.limit();
+   */
+  private void decrypt(CipherState state, ByteBuffer inBuffer,
+      ByteBuffer outBuffer, byte padding) throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    if(inBuffer.position() == padding) {
+      // There is no real data in inBuffer.
+      return;
+    }
+    inBuffer.flip();
+    outBuffer.clear();
+    decryptBuffer(state, inBuffer, outBuffer);
+    inBuffer.clear();
+    outBuffer.flip();
+    if (padding > 0) {
+      /*
+       * The plain text and cipher text have a 1:1 mapping, they start at the
+       * same position.
+       */
+      outBuffer.position(padding);
+    }
+  }
+
+  private void decryptBuffer(CipherState state, ByteBuffer inBuffer, ByteBuffer outBuffer)
+      throws IOException {
+    int inputSize = inBuffer.remaining();
+    try {
+      int n = state.getCipher().update(inBuffer, outBuffer);
+      if (n < inputSize) {
+        /**
+         * Typically code will not get here. Cipher#update will consume all
+         * input data and put result in outBuffer.
+         * Cipher#doFinal will reset the cipher context.
+         */
+        state.getCipher().doFinal(inBuffer, outBuffer);
+        state.reset(true);
+      }
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch (BadPaddingException e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * This method is executed immediately after decryption. Check whether
+   * cipher should be updated and recalculate padding if needed.
+   */
+  private byte postDecryption(CipherState state, ByteBuffer inBuffer,
+      long position, byte[] iv) throws IOException {
+    byte padding = 0;
+    if (state.isReset()) {
+      /*
+       * This code is generally not executed since the cipher usually
+       * maintains cipher context (e.g. the counter) internally. However,
+       * some implementations can't maintain context so a re-init is necessary
+       * after each decryption call.
+       */
+      resetCipher(state, position, iv);
+      padding = getPadding(position);
+      inBuffer.position(padding);
+    }
+    return padding;
+  }
+
+  /** Calculate the counter and iv, reset the cipher. */
+  private void resetCipher(CipherState state, long position, byte[] iv)
+      throws IOException {
+    final long counter = getCounter(position);
+    Utils.calculateIV(getInitIV(), counter, iv);
+    try {
+      state.getCipher().init(Cipher.DECRYPT_MODE, getKey(), iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch (InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+    state.reset(false);
+  }
+
+  /** Get Cipher from pool */
+  private CipherState getCipherState() throws IOException {
+    CipherState state = cipherPool.poll();
+    if (state == null) {
+      Cipher cipher;
+      try {
+        cipher = CipherFactory.getInstance(getCipher().getTransformation(),
+            getCipher().getProperties());
+      } catch (GeneralSecurityException e) {
+        throw new IOException(e);
+      }
+      state = new CipherState(cipher);
+    }
+
+    return state;
+  }
+
+  /** Return Cipher to pool */
+  private void returnCipherState(CipherState state) {
+    if (state != null) {
+      cipherPool.add(state);
+    }
+  }
+
+  /** Get direct buffer from pool */
+  private ByteBuffer getBuffer() {
+    ByteBuffer buffer = bufferPool.poll();
+    if (buffer == null) {
+      buffer = ByteBuffer.allocateDirect(getBufferSize());
+    }
+
+    return buffer;
+  }
+
+  /** Return direct buffer to pool */
+  private void returnBuffer(ByteBuffer buf) {
+    if (buf != null) {
+      buf.clear();
+      bufferPool.add(buf);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (!isOpen()) {
+      return;
+    }
+
+    cleanBufferPool();
+    super.close();
+  }
+
+  /** Clean direct buffer pool */
+  private void cleanBufferPool() {
+    ByteBuffer buf;
+    while ((buf = bufferPool.poll()) != null) {
+      Utils.freeDirectBuffer(buf);
+    }
+  }
+
+  private class CipherState {
+    private Cipher cipher;
+    private boolean reset;
+
+    public CipherState(Cipher cipher) {
+      this.cipher = cipher;
+      this.reset = false;
+    }
+
+    public Cipher getCipher() {
+      return cipher;
+    }
+
+    public boolean isReset() {
+      return reset;
+    }
+
+    public void reset(boolean reset) {
+      this.reset = reset;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
deleted file mode 100644
index 160f6ee..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/**
- * 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 java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherFactory;
-import org.apache.commons.crypto.stream.input.Input;
-import org.apache.commons.crypto.utils.IOUtils;
-import org.apache.commons.crypto.utils.Utils;
-
-import static org.apache.commons.crypto.cipher.CipherTransformation.AES_CTR_NOPADDING;
-
-/**
- * PositionedCryptoInputStream provides the capability to decrypt the stream starting
- * at random position as well as provides the foundation for positioned read for
- * decrypting. This needs a stream cipher mode such as AES CTR mode.
- */
-public class PositionedCryptoInputStream extends CTRCryptoInputStream {
-
-  /**
-   * DirectBuffer pool
-   */
-  private final Queue<ByteBuffer> bufferPool = new
-      ConcurrentLinkedQueue<ByteBuffer>();
-
-  /**
-   * Cipher pool
-   */
-  private final Queue<CipherState> cipherPool = new
-      ConcurrentLinkedQueue<CipherState>();
-
-  public PositionedCryptoInputStream(Properties props, Input in,
-      byte[] key, byte[] iv, long streamOffset) throws IOException {
-    this(in, Utils.getCipherInstance(AES_CTR_NOPADDING, props),
-        Utils.getBufferSize(props), key, iv, streamOffset);
-  }
-
-  public PositionedCryptoInputStream(
-      Input input,
-      Cipher cipher,
-      int bufferSize,
-      byte[] key,
-      byte[] iv,
-      long streamOffset) throws IOException {
-    super(input, cipher, bufferSize, key, iv, streamOffset);
-  }
-
-  /**
-   * Read upto the specified number of bytes from a given position
-   * within a stream and return the number of bytes read. This does not
-   * change the current offset of the stream, and is thread-safe.
-   */
-  public int read(long position, byte[] buffer, int offset, int length)
-      throws IOException {
-    checkStream();
-    final int n = input.read(position, buffer, offset, length);
-    if (n > 0) {
-      // This operation does not change the current offset of the file
-      decrypt(position, buffer, offset, n);
-    }
-    return n;
-  }
-
-  /**
-   * Read the specified number of bytes from a given position within a stream.
-   * This does not change the current offset of the stream and is thread-safe.
-   */
-  public void readFully(long position, byte[] buffer, int offset, int length)
-      throws IOException {
-    checkStream();
-    IOUtils.readFully(input, position, buffer, offset, length);
-    if (length > 0) {
-      // This operation does not change the current offset of the file
-      decrypt(position, buffer, offset, length);
-    }
-  }
-
-  public void readFully(long position, byte[] buffer) throws IOException {
-    readFully(position, buffer, 0, buffer.length);
-  }
-
-  /**
-   * Decrypt length bytes in buffer starting at offset. Output is also put
-   * into buffer starting at offset. It is thread-safe.
-   */
-  protected void decrypt(long position, byte[] buffer, int offset, int length)
-      throws IOException {
-    ByteBuffer inBuffer = getBuffer();
-    ByteBuffer outBuffer = getBuffer();
-    CipherState state = null;
-    try {
-      state = getCipherState();
-      byte[] iv = getInitIV().clone();
-      resetCipher(state, position, iv);
-      byte padding = getPadding(position);
-      inBuffer.position(padding); // Set proper position for input data.
-
-      int n = 0;
-      while (n < length) {
-        int toDecrypt = Math.min(length - n, inBuffer.remaining());
-        inBuffer.put(buffer, offset + n, toDecrypt);
-
-        // Do decryption
-        decrypt(state, inBuffer, outBuffer, padding);
-
-        outBuffer.get(buffer, offset + n, toDecrypt);
-        n += toDecrypt;
-        padding = postDecryption(state, inBuffer, position + n, iv);
-      }
-    } finally {
-      returnBuffer(inBuffer);
-      returnBuffer(outBuffer);
-      returnCipherState(state);
-    }
-  }
-
-  /**
-   * Do the decryption using inBuffer as input and outBuffer as output.
-   * Upon return, inBuffer is cleared; the decrypted data starts at
-   * outBuffer.position() and ends at outBuffer.limit();
-   */
-  private void decrypt(CipherState state, ByteBuffer inBuffer,
-      ByteBuffer outBuffer, byte padding) throws IOException {
-    Utils.checkState(inBuffer.position() >= padding);
-    if(inBuffer.position() == padding) {
-      // There is no real data in inBuffer.
-      return;
-    }
-    inBuffer.flip();
-    outBuffer.clear();
-    decryptBuffer(state, inBuffer, outBuffer);
-    inBuffer.clear();
-    outBuffer.flip();
-    if (padding > 0) {
-      /*
-       * The plain text and cipher text have a 1:1 mapping, they start at the
-       * same position.
-       */
-      outBuffer.position(padding);
-    }
-  }
-
-  private void decryptBuffer(CipherState state, ByteBuffer inBuffer, ByteBuffer outBuffer)
-      throws IOException {
-    int inputSize = inBuffer.remaining();
-    try {
-      int n = state.getCipher().update(inBuffer, outBuffer);
-      if (n < inputSize) {
-        /**
-         * Typically code will not get here. Cipher#update will consume all
-         * input data and put result in outBuffer.
-         * Cipher#doFinal will reset the cipher context.
-         */
-        state.getCipher().doFinal(inBuffer, outBuffer);
-        state.reset(true);
-      }
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    } catch (IllegalBlockSizeException e) {
-      throw new IOException(e);
-    } catch (BadPaddingException e) {
-      throw new IOException(e);
-    }
-  }
-
-  /**
-   * This method is executed immediately after decryption. Check whether
-   * cipher should be updated and recalculate padding if needed.
-   */
-  private byte postDecryption(CipherState state, ByteBuffer inBuffer,
-      long position, byte[] iv) throws IOException {
-    byte padding = 0;
-    if (state.isReset()) {
-      /*
-       * This code is generally not executed since the cipher usually
-       * maintains cipher context (e.g. the counter) internally. However,
-       * some implementations can't maintain context so a re-init is necessary
-       * after each decryption call.
-       */
-      resetCipher(state, position, iv);
-      padding = getPadding(position);
-      inBuffer.position(padding);
-    }
-    return padding;
-  }
-
-  /** Calculate the counter and iv, reset the cipher. */
-  private void resetCipher(CipherState state, long position, byte[] iv)
-      throws IOException {
-    final long counter = getCounter(position);
-    Utils.calculateIV(getInitIV(), counter, iv);
-    try {
-      state.getCipher().init(Cipher.DECRYPT_MODE, getKey(), iv);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    } catch (InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-    state.reset(false);
-  }
-
-  /** Get Cipher from pool */
-  private CipherState getCipherState() throws IOException {
-    CipherState state = cipherPool.poll();
-    if (state == null) {
-      Cipher cipher;
-      try {
-        cipher = CipherFactory.getInstance(getCipher().getTransformation(),
-            getCipher().getProperties());
-      } catch (GeneralSecurityException e) {
-        throw new IOException(e);
-      }
-      state = new CipherState(cipher);
-    }
-
-    return state;
-  }
-
-  /** Return Cipher to pool */
-  private void returnCipherState(CipherState state) {
-    if (state != null) {
-      cipherPool.add(state);
-    }
-  }
-
-  /** Get direct buffer from pool */
-  private ByteBuffer getBuffer() {
-    ByteBuffer buffer = bufferPool.poll();
-    if (buffer == null) {
-      buffer = ByteBuffer.allocateDirect(getBufferSize());
-    }
-
-    return buffer;
-  }
-
-  /** Return direct buffer to pool */
-  private void returnBuffer(ByteBuffer buf) {
-    if (buf != null) {
-      buf.clear();
-      bufferPool.add(buf);
-    }
-  }
-
-  @Override
-  public void close() throws IOException {
-    if (!isOpen()) {
-      return;
-    }
-
-    cleanBufferPool();
-    super.close();
-  }
-
-  /** Clean direct buffer pool */
-  private void cleanBufferPool() {
-    ByteBuffer buf;
-    while ((buf = bufferPool.poll()) != null) {
-      Utils.freeDirectBuffer(buf);
-    }
-  }
-
-  private class CipherState {
-    private Cipher cipher;
-    private boolean reset;
-
-    public CipherState(Cipher cipher) {
-      this.cipher = cipher;
-      this.reset = false;
-    }
-
-    public Cipher getCipher() {
-      return cipher;
-    }
-
-    public boolean isReset() {
-      return reset;
-    }
-
-    public void reset(boolean reset) {
-      this.reset = reset;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
index ee24a0c..9aff624 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java
@@ -23,7 +23,7 @@ import java.nio.channels.ReadableByteChannel;
 
 /**
  * The ChannelInput class takes a <code>ReadableByteChannel</code> object and
- * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>.
+ * wraps it as <code>Input</code> object acceptable by <code>CipherInputStream</code>.
  */
 public class ChannelInput implements Input {
   private static final int SKIP_BUFFER_SIZE = 2048;

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/Input.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/Input.java b/src/main/java/org/apache/commons/crypto/stream/input/Input.java
index a63c6ca..2971edb 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/Input.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/Input.java
@@ -21,7 +21,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 
 /**
- * The Input interface abstract the input source of <code>CryptoInputStream</code> so that
+ * The Input interface abstract the input source of <code>CipherInputStream</code> so that
  * different implementation of input can be used. The implementation Input interface will usually
  * wraps an input mechanism such as <code>InputStream</code> or <code>ReadableByteChannel</code>.
  */

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
index ac3739b..c7e6771 100644
--- a/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
 
 /**
  * The StreamInput class takes a <code>InputStream</code> object and
- * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>.
+ * wraps it as <code>Input</code> object acceptable by <code>CipherInputStream</code>.
  */
 public class StreamInput implements Input {
   private byte[] buf;

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
index bae82a8..0a60217 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java
@@ -23,7 +23,7 @@ import java.nio.channels.WritableByteChannel;
 
 /**
  * The ChannelOutput class takes a <code>WritableByteChannel</code> object and wraps it as 
- * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target.
+ * <code>Output</code> object acceptable by <code>CipherOutputStream</code> as the output target.
  */
 public class ChannelOutput implements Output {
 

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/Output.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/Output.java b/src/main/java/org/apache/commons/crypto/stream/output/Output.java
index 903fcea..c57a89e 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/Output.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/Output.java
@@ -21,7 +21,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 
 /**
- * The Output interface abstract the output target of <code>CryptoOutputStream</code> so that
+ * The Output interface abstract the output target of <code>CipherOutputStream</code> so that
  * different implementation of output can be used. The implementation Output interface will usually
  * wraps an output mechanism such as <code>OutputStream</code> or <code>WritableByteChannel</code>.
  */

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
index beead7f..ad067be 100644
--- a/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
+++ b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
 
 /**
  * The StreamOutput class takes a <code>OutputStream</code> object and wraps it as 
- * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target.
+ * <code>Output</code> object acceptable by <code>CipherOutputStream</code> as the output target.
  */
 public class StreamOutput implements Output {
   private byte[] buf;

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
new file mode 100644
index 0000000..6e2d864
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.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 AbstractCipherStreamTest {
+  private static final Log LOG= LogFactory.getLog(AbstractCipherStreamTest.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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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 = getCipherInputStream(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();
+    CipherOutputStream out =
+        getCipherOutputStream(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 = getCipherInputStream(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 CipherOutputStream(baos, cipher, defaultBufferSize, key, iv);
+    out.write(data);
+    out.flush();
+    out.close();
+    encData = baos.toByteArray();
+  }
+
+  protected CipherInputStream getCipherInputStream(ByteArrayInputStream bais,
+                                                   Cipher cipher,
+                                                   int bufferSize, byte[] iv,
+                                                   boolean withChannel) throws
+      IOException {
+    if (withChannel) {
+      return new CipherInputStream(Channels.newChannel(bais), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CipherInputStream(bais, cipher, bufferSize, key, iv);
+    }
+  }
+
+  protected CipherOutputStream getCipherOutputStream(ByteArrayOutputStream baos,
+                                                     Cipher cipher,
+                                                     int bufferSize, byte[] iv,
+                                                     boolean withChannel) throws
+      IOException {
+    if (withChannel) {
+      return new CipherOutputStream(Channels.newChannel(baos), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CipherOutputStream(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();
+    CipherOutputStream out =
+        getCipherOutputStream(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
+    CipherInputStream in = getCipherInputStream(
+        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 = getCipherInputStream(
+        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();
+    CipherOutputStream out =
+        getCipherOutputStream(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
+    CipherInputStream in = getCipherInputStream(
+        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 = getCipherInputStream(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/ad81d236/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
deleted file mode 100644
index fe73f00..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/AbstractCryptoStreamTest.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/**
- * 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/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.java
new file mode 100644
index 0000000..c8717ff
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCipherStreamTest.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 CBCNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CBC_NOPADDING;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
deleted file mode 100644
index 743a3ac..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CBCNoPaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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/ad81d236/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.java
new file mode 100644
index 0000000..ff34bc9
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCipherStreamTest.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 CBCPKCS5PaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CBC_PKCS5PADDING;
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
deleted file mode 100644
index cb65dac..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CBCPKCS5PaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.java
new file mode 100644
index 0000000..f5e85ff
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CTRCipherStreamTest.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 CTRCipherStreamTest extends AbstractCipherStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CTR_NOPADDING;
+  }
+
+  @Override
+  protected CTRCipherInputStream getCipherInputStream
+      (ByteArrayInputStream bais, Cipher cipher, int
+      bufferSize,byte[] iv, boolean withChannel)
+      throws IOException {
+    if (withChannel) {
+      return new CTRCipherInputStream(Channels.newChannel(bais), cipher,
+          bufferSize, key, iv);
+    } else {
+      return new CTRCipherInputStream(bais, cipher, bufferSize, key, iv);
+    }
+  }
+
+  @Override
+  protected CTRCipherOutputStream getCipherOutputStream(ByteArrayOutputStream baos, Cipher cipher, int
+      bufferSize, byte[] iv, boolean withChannel)
+      throws IOException {
+    if (withChannel) {
+      return new CTRCipherOutputStream(Channels.newChannel(baos), cipher, bufferSize, key, iv);
+    } else {
+      return new CTRCipherOutputStream(baos, cipher, bufferSize, key, iv);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
deleted file mode 100644
index 1a50382..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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/ad81d236/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
new file mode 100644
index 0000000..b59761d
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.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 CTRNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+  public void setUp() throws IOException {
+    transformation = CipherTransformation
+        .AES_CTR_NOPADDING;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/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
deleted file mode 100644
index 1bbf600..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCryptoStreamTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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;
-  }
-
-}


[3/3] commons-crypto git commit: CRYPTO-12: Rename CryptoInputStream to CipherInputStream and CryptoOutputStream to CipherOutputStream (Xianda Ke via Dian Fu)

Posted by di...@apache.org.
CRYPTO-12: Rename CryptoInputStream to CipherInputStream and CryptoOutputStream to CipherOutputStream
(Xianda Ke via Dian Fu)


Project: http://git-wip-us.apache.org/repos/asf/commons-crypto/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-crypto/commit/ad81d236
Tree: http://git-wip-us.apache.org/repos/asf/commons-crypto/tree/ad81d236
Diff: http://git-wip-us.apache.org/repos/asf/commons-crypto/diff/ad81d236

Branch: refs/heads/master
Commit: ad81d236a9e132f59486b5a73c347faabe329e9e
Parents: f819dd4
Author: dianfu <di...@apache.org>
Authored: Tue Apr 26 11:27:08 2016 +0800
Committer: dianfu <di...@apache.org>
Committed: Tue Apr 26 11:27:08 2016 +0800

----------------------------------------------------------------------
 README.md                                       |   6 +-
 .../crypto/stream/CTRCipherInputStream.java     | 423 +++++++++++++++++
 .../crypto/stream/CTRCipherOutputStream.java    | 230 ++++++++++
 .../crypto/stream/CTRCryptoInputStream.java     | 423 -----------------
 .../crypto/stream/CTRCryptoOutputStream.java    | 230 ----------
 .../crypto/stream/CipherInputStream.java        | 394 ++++++++++++++++
 .../crypto/stream/CipherOutputStream.java       | 283 ++++++++++++
 .../crypto/stream/CryptoInputStream.java        | 394 ----------------
 .../crypto/stream/CryptoOutputStream.java       | 283 ------------
 .../stream/PositionedCipherInputStream.java     | 311 +++++++++++++
 .../stream/PositionedCryptoInputStream.java     | 311 -------------
 .../crypto/stream/input/ChannelInput.java       |   2 +-
 .../commons/crypto/stream/input/Input.java      |   2 +-
 .../crypto/stream/input/StreamInput.java        |   2 +-
 .../crypto/stream/output/ChannelOutput.java     |   2 +-
 .../commons/crypto/stream/output/Output.java    |   2 +-
 .../crypto/stream/output/StreamOutput.java      |   2 +-
 .../crypto/stream/AbstractCipherStreamTest.java | 456 +++++++++++++++++++
 .../crypto/stream/AbstractCryptoStreamTest.java | 456 -------------------
 .../stream/CBCNoPaddingCipherStreamTest.java    |  31 ++
 .../stream/CBCNoPaddingCryptoStreamTest.java    |  31 --
 .../stream/CBCPKCS5PaddingCipherStreamTest.java |  30 ++
 .../stream/CBCPKCS5PaddingCryptoStreamTest.java |  30 --
 .../crypto/stream/CTRCipherStreamTest.java      |  58 +++
 .../crypto/stream/CTRCryptoStreamTest.java      |  58 ---
 .../stream/CTRNoPaddingCipherStreamTest.java    |  31 ++
 .../stream/CTRNoPaddingCryptoStreamTest.java    |  31 --
 .../stream/PositionedCipherInputStreamTest.java | 381 ++++++++++++++++
 .../stream/PositionedCryptoInputStreamTest.java | 381 ----------------
 29 files changed, 2637 insertions(+), 2637 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 34845c0..adc9d63 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ Chimera [![Build Status](https://travis-ci.org/intel-hadoop/chimera.svg?branch=m
 
 ## Features
   * Cipher API for low level cryptographic operations.
-  * Java stream API (CryptoInputStream/CryptoOutputStream) for high level stream encyrption/decryption.
+  * Java stream API (CipherInputStream/CipherOutputStream) for high level stream encyrption/decryption.
   * Both optimized with high performance AES encryption/decryption. (1400 MB/s - 1700 MB/s throughput in modern Xeon processors).
   * JNI-based implementation to achieve comparable performance to the native C++ version based on Openssl.
   * Portable across various operating systems (currently only Linux); Chimera loads the library according to your machine environment (It looks system properties, `os.name` and `os.arch`). 
@@ -47,13 +47,13 @@ String input = "hello world!";
 byte[] decryptedData = new byte[1024];
 // Encrypt
 ByteArrayOutputStream os = new ByteArrayOutputStream();
-CryptoOutputStream cos = new CryptoOutputStream(os, cipher, bufferSize, key, iv);
+CipherOutputStream cos = new CipherOutputStream(os, cipher, bufferSize, key, iv);
 cos.write(input.getBytes("UTF-8"));
 cos.flush();
 cos.close();
 
 // Decrypt
-CryptoInputStream cis = new CryptoInputStream(new ByteArrayInputStream(os.toByteArray()), cipher, bufferSize, key, iv);
+CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(os.toByteArray()), cipher, bufferSize, key, iv);
 int decryptedLen = cis.read(decryptedData, 0, 1024);
 
 ```

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java
new file mode 100644
index 0000000..6c3c657
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java
@@ -0,0 +1,423 @@
+/**
+ * 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 java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.input.ChannelInput;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.stream.input.StreamInput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CTRCipherInputStream decrypts data. AES CTR mode is required in order to
+ * ensure that the plain text and cipher text have a 1:1 mapping. CTR crypto
+ * stream has stream characteristic which is useful for implement features
+ * like random seek. The decryption is buffer based. The key points of the
+ * decryption are (1) calculating the counter and (2) padding through stream
+ * position:
+ * <p/>
+ * counter = base + pos/(algorithm blocksize);
+ * padding = pos%(algorithm blocksize);
+ * <p/>
+ * The underlying stream offset is maintained as state. It is not thread-safe.
+ */
+public class CTRCipherInputStream extends CipherInputStream {
+  /**
+   * Underlying stream offset
+   */
+  protected long streamOffset = 0;
+
+  /**
+   * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer}
+   * before any other data goes in. The purpose of padding is to put the input
+   * data at proper position.
+   */
+  private byte padding;
+
+  /**
+   * Flag to mark whether the cipher has been reset
+   */
+  private boolean cipherReset = false;
+
+  public CTRCipherInputStream(Properties props, InputStream in,
+                              byte[] key, byte[] iv)
+      throws IOException {
+    this(props, in, key, iv, 0);
+  }
+
+  public CTRCipherInputStream(Properties props, ReadableByteChannel in,
+                              byte[] key, byte[] iv)
+      throws IOException {
+    this(props, in, key, iv, 0);
+  }
+
+  public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize,
+                              byte[] key, byte[] iv) throws IOException {
+    this(in, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher,
+                              int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(in, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherInputStream(
+      Input input,
+      Cipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv) throws IOException {
+    this(input, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherInputStream(Properties props, InputStream in,
+                              byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCipherInputStream(Properties props, ReadableByteChannel in,
+                              byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize,
+                              byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher,
+                              int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCipherInputStream(
+      Input input,
+      Cipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv,
+      long streamOffset) throws IOException {
+    super(input, cipher, bufferSize, key, iv);
+
+    Utils.checkStreamCipher(cipher);
+
+    resetStreamOffset(streamOffset);
+  }
+
+  /** Skip n bytes */
+  @Override
+  public long skip(long n) throws IOException {
+    Utils.checkArgument(n >= 0, "Negative skip length.");
+    checkStream();
+
+    if (n == 0) {
+      return 0;
+    } else if (n <= outBuffer.remaining()) {
+      int pos = outBuffer.position() + (int) n;
+      outBuffer.position(pos);
+      return n;
+    } else {
+      /*
+       * Subtract outBuffer.remaining() to see how many bytes we need to
+       * skip in the underlying stream. Add outBuffer.remaining() to the
+       * actual number of skipped bytes in the underlying stream to get the
+       * number of skipped bytes from the user's point of view.
+       */
+      n -= outBuffer.remaining();
+      long skipped = input.skip(n);
+      if (skipped < 0) {
+        skipped = 0;
+      }
+      long pos = streamOffset + skipped;
+      skipped += outBuffer.remaining();
+      resetStreamOffset(pos);
+      return skipped;
+    }
+  }
+
+  /** ByteBuffer read. */
+  @Override
+  public int read(ByteBuffer buf) throws IOException {
+    checkStream();
+    int unread = outBuffer.remaining();
+    if (unread <= 0) { // Fill the unread decrypted data buffer firstly
+      final int n = input.read(inBuffer);
+      if (n <= 0) {
+        return n;
+      }
+
+      streamOffset += n; // Read n bytes
+      if (buf.isDirect() && buf.remaining() >= inBuffer.position() && padding == 0) {
+        // Use buf as the output buffer directly
+        decryptInPlace(buf);
+        padding = postDecryption(streamOffset);
+        return n;
+      } else {
+        // Use outBuffer as the output buffer
+        decrypt();
+        padding = postDecryption(streamOffset);
+      }
+    }
+
+    // Copy decrypted data from outBuffer to buf
+    unread = outBuffer.remaining();
+    final int toRead = buf.remaining();
+    if (toRead <= unread) {
+      final int limit = outBuffer.limit();
+      outBuffer.limit(outBuffer.position() + toRead);
+      buf.put(outBuffer);
+      outBuffer.limit(limit);
+      return toRead;
+    } else {
+      buf.put(outBuffer);
+      return unread;
+    }
+  }
+
+  /**
+   * Seek the stream to a specific position relative to start of the under layer stream.
+   * 
+   * @param position The position to seek to
+   * @throws IOException if seek failed
+   */
+  public void seek(long position) throws IOException {
+    Utils.checkArgument(position >= 0, "Cannot seek to negative offset.");
+    checkStream();
+    /*
+     * If data of target pos in the underlying stream has already been read
+     * and decrypted in outBuffer, we just need to re-position outBuffer.
+     */
+    if (position >= getStreamPosition() && position <= getStreamOffset()) {
+      int forward = (int) (position - getStreamPosition());
+      if (forward > 0) {
+        outBuffer.position(outBuffer.position() + forward);
+      }
+    } else {
+      input.seek(position);
+      resetStreamOffset(position);
+    }
+  }
+
+  protected long getStreamOffset() {
+    return streamOffset;
+  }
+
+  protected long getStreamPosition() {
+    return streamOffset - outBuffer.remaining();
+  }
+
+  /**
+   * Decrypt more data by reading the under layer stream. The decrypted data will
+   * be put in the output buffer.
+   *
+   * @return The number of decrypted data. -1 if end of the decrypted stream
+   */
+  protected int decryptMore() throws IOException {
+    int n = input.read(inBuffer);
+    if (n <= 0) {
+      return n;
+    }
+
+    streamOffset += n; // Read n bytes
+    decrypt();
+    padding = postDecryption(streamOffset);
+    return outBuffer.remaining();
+  }
+
+  /**
+   * Do the decryption using inBuffer as input and outBuffer as output.
+   * Upon return, inBuffer is cleared; the decrypted data starts at
+   * outBuffer.position() and ends at outBuffer.limit();
+   */
+  protected void decrypt() throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    if(inBuffer.position() == padding) {
+      // There is no real data in inBuffer.
+      return;
+    }
+
+    inBuffer.flip();
+    outBuffer.clear();
+    decryptBuffer(outBuffer);
+    inBuffer.clear();
+    outBuffer.flip();
+
+    if (padding > 0) {
+      /*
+       * The plain text and cipher text have a 1:1 mapping, they start at the
+       * same position.
+       */
+      outBuffer.position(padding);
+    }
+  }
+
+  /**
+   * Do the decryption using inBuffer as input and buf as output.
+   * Upon return, inBuffer is cleared; the buf's position will be equal to
+   * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i> where <i>p</i> is the position before
+   * decryption, <i>n</i> is the number of bytes decrypted.
+   * The buf's limit will not have changed.
+   */
+  protected void decryptInPlace(ByteBuffer buf) throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    Utils.checkState(buf.isDirect());
+    Utils.checkState(buf.remaining() >= inBuffer.position());
+    Utils.checkState(padding == 0);
+
+    if(inBuffer.position() == padding) {
+      // There is no real data in inBuffer.
+      return;
+    }
+    inBuffer.flip();
+    decryptBuffer(buf);
+    inBuffer.clear();
+  }
+
+  /**
+   * Decrypt all data in buf: total n bytes from given start position.
+   * Output is also buf and same start position.
+   * buf.position() and buf.limit() should be unchanged after decryption.
+   */
+  protected void decrypt(ByteBuffer buf, int offset, int len)
+      throws IOException {
+    final int pos = buf.position();
+    final int limit = buf.limit();
+    int n = 0;
+    while (n < len) {
+      buf.position(offset + n);
+      buf.limit(offset + n + Math.min(len - n, inBuffer.remaining()));
+      inBuffer.put(buf);
+      // Do decryption
+      try {
+        decrypt();
+        buf.position(offset + n);
+        buf.limit(limit);
+        n += outBuffer.remaining();
+        buf.put(outBuffer);
+      } finally {
+        padding = postDecryption(streamOffset - (len - n));
+      }
+    }
+    buf.position(pos);
+  }
+
+  /**
+   * This method is executed immediately after decryption. Check whether
+   * cipher should be updated and recalculate padding if needed.
+   */
+  protected byte postDecryption(long position) throws IOException {
+    byte padding = 0;
+    if (cipherReset) {
+      /*
+       * This code is generally not executed since the cipher usually
+       * maintains cipher context (e.g. the counter) internally. However,
+       * some implementations can't maintain context so a re-init is necessary
+       * after each decryption call.
+       */
+      resetCipher(position);
+      padding = getPadding(position);
+      inBuffer.position(padding);
+    }
+    return padding;
+  }
+
+  protected long getCounter(long position) {
+    return position / cipher.getTransformation().getAlgorithmBlockSize();
+  }
+
+  protected byte getPadding(long position) {
+    return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize());
+  }
+
+  /** Initialize the cipher. */
+  @Override
+  protected void initCipher() {
+    // Do nothing for initCipher
+    // Will reset the cipher when reset the stream offset
+  }
+
+  /** Calculate the counter and iv, reset the cipher. */
+  protected void resetCipher(long position)
+      throws IOException {
+    final long counter = getCounter(position);
+    Utils.calculateIV(initIV, counter, iv);
+    try {
+      cipher.init(Cipher.DECRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch (InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+    cipherReset = false;
+  }
+
+  /**
+   * Reset the underlying stream offset; clear {@link #inBuffer} and
+   * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
+   */
+  protected void resetStreamOffset(long offset) throws IOException {
+    streamOffset = offset;
+    inBuffer.clear();
+    outBuffer.clear();
+    outBuffer.limit(0);
+    resetCipher(offset);
+    padding = getPadding(offset);
+    inBuffer.position(padding); // Set proper position for input data.
+  }
+
+  protected void decryptBuffer(ByteBuffer out)
+      throws IOException {
+    int inputSize = inBuffer.remaining();
+    try {
+      int n = cipher.update(inBuffer, out);
+      if (n < inputSize) {
+        /**
+         * Typically code will not get here. Cipher#update will consume all
+         * input data and put result in outBuffer.
+         * Cipher#doFinal will reset the cipher context.
+         */
+        cipher.doFinal(inBuffer, out);
+        cipherReset = true;
+      }
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch (BadPaddingException e) {
+      throw new IOException(e);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java
new file mode 100644
index 0000000..13ac256
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java
@@ -0,0 +1,230 @@
+/**
+ * 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 java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.output.ChannelOutput;
+import org.apache.commons.crypto.stream.output.Output;
+import org.apache.commons.crypto.stream.output.StreamOutput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CTRCipherOutputStream encrypts data. It is not thread-safe. AES CTR mode is
+ * required in order to ensure that the plain text and cipher text have a 1:1
+ * mapping. The encryption is buffer based. The key points of the encryption are
+ * (1) calculating counter and (2) padding through stream position.
+ * <p/>
+ * counter = base + pos/(algorithm blocksize);
+ * padding = pos%(algorithm blocksize);
+ * <p/>
+ * The underlying stream offset is maintained as state.
+ */
+public class CTRCipherOutputStream extends CipherOutputStream {
+  /**
+   * Underlying stream offset.
+   */
+  protected long streamOffset = 0;
+
+  /**
+   * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer}
+   * before any other data goes in. The purpose of padding is to put input data
+   * at proper position.
+   */
+  private byte padding;
+
+  /**
+   * Flag to mark whether the cipher has been reset
+   */
+  private boolean cipherReset = false;
+
+  public CTRCipherOutputStream(Properties props, OutputStream out,
+                               byte[] key, byte[] iv)
+      throws IOException {
+    this(props, out, key, iv, 0);
+  }
+
+  public CTRCipherOutputStream(Properties props, WritableByteChannel out,
+                               byte[] key, byte[] iv)
+      throws IOException {
+    this(props, out, key, iv, 0);
+  }
+
+  public CTRCipherOutputStream(OutputStream out, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(out, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(channel, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherOutputStream(Output output, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv)
+      throws IOException {
+    this(output, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCipherOutputStream(Properties props, OutputStream out,
+                               byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCipherOutputStream(Properties props, WritableByteChannel out,
+                               byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCipherOutputStream(OutputStream out, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new StreamOutput(out, bufferSize), cipher,
+        bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new ChannelOutput(channel), cipher,
+        bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCipherOutputStream(Output output, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    super(output, cipher, bufferSize, key, iv);
+
+    Utils.checkStreamCipher(cipher);
+    this.streamOffset = streamOffset;
+
+    resetCipher();
+  }
+
+  /**
+   * Do the encryption, input is {@link #inBuffer} and output is
+   * {@link #outBuffer}.
+   */
+  @Override
+  protected void encrypt() throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    if (inBuffer.position() == padding) {
+      // There is no real data in the inBuffer.
+      return;
+    }
+
+    inBuffer.flip();
+    outBuffer.clear();
+    encryptBuffer(outBuffer);
+    inBuffer.clear();
+    outBuffer.flip();
+
+    if (padding > 0) {
+      /*
+       * The plain text and cipher text have a 1:1 mapping, they start at the
+       * same position.
+       */
+      outBuffer.position(padding);
+      padding = 0;
+    }
+
+    final int len = output.write(outBuffer);
+    streamOffset += len;
+    if (cipherReset) {
+      /*
+       * This code is generally not executed since the encryptor usually
+       * maintains encryption context (e.g. the counter) internally. However,
+       * some implementations can't maintain context so a re-init is necessary
+       * after each encryption call.
+       */
+      resetCipher();
+    }
+  }
+
+  /**
+   * Do final encryption of the last data
+   */
+  @Override
+  protected void encryptFinal() throws IOException {
+    // The same as the normal encryption for Counter mode
+    encrypt();
+  }
+
+  /** Initialize the cipher. */
+  @Override
+  protected void initCipher() {
+    // Do nothing for initCipher
+    // Will reset the cipher considering the stream offset
+  }
+
+  /** Reset the {@link #cipher}: calculate counter and {@link #padding}. */
+  private void resetCipher() throws IOException {
+    final long counter =
+        streamOffset / cipher.getTransformation().getAlgorithmBlockSize();
+    padding =
+        (byte)(streamOffset % cipher.getTransformation().getAlgorithmBlockSize());
+    inBuffer.position(padding); // Set proper position for input data.
+
+    Utils.calculateIV(initIV, counter, iv);
+    try {
+      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    }catch (InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+    cipherReset = false;
+  }
+
+  private void encryptBuffer(ByteBuffer out)
+      throws IOException {
+    int inputSize = inBuffer.remaining();
+    try {
+      int n = cipher.update(inBuffer, out);
+      if (n < inputSize) {
+        /**
+         * Typically code will not get here. Cipher#update will consume all
+         * input data and put result in outBuffer.
+         * Cipher#doFinal will reset the cipher context.
+         */
+        cipher.doFinal(inBuffer, out);
+        cipherReset = true;
+      }
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (BadPaddingException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
deleted file mode 100644
index fc76f16..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/**
- * 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 java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.stream.input.ChannelInput;
-import org.apache.commons.crypto.stream.input.Input;
-import org.apache.commons.crypto.stream.input.StreamInput;
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * CTRCryptoInputStream decrypts data. AES CTR mode is required in order to
- * ensure that the plain text and cipher text have a 1:1 mapping. CTR crypto
- * stream has stream characteristic which is useful for implement features
- * like random seek. The decryption is buffer based. The key points of the
- * decryption are (1) calculating the counter and (2) padding through stream
- * position:
- * <p/>
- * counter = base + pos/(algorithm blocksize);
- * padding = pos%(algorithm blocksize);
- * <p/>
- * The underlying stream offset is maintained as state. It is not thread-safe.
- */
-public class CTRCryptoInputStream extends CryptoInputStream {
-  /**
-   * Underlying stream offset
-   */
-  protected long streamOffset = 0;
-
-  /**
-   * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer}
-   * before any other data goes in. The purpose of padding is to put the input
-   * data at proper position.
-   */
-  private byte padding;
-
-  /**
-   * Flag to mark whether the cipher has been reset
-   */
-  private boolean cipherReset = false;
-
-  public CTRCryptoInputStream(Properties props, InputStream in,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, in, key, iv, 0);
-  }
-
-  public CTRCryptoInputStream(Properties props, ReadableByteChannel in,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, in, key, iv, 0);
-  }
-
-  public CTRCryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
-      byte[] key, byte[] iv) throws IOException {
-    this(in, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoInputStream(ReadableByteChannel in, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(in, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoInputStream(
-      Input input,
-      Cipher cipher,
-      int bufferSize,
-      byte[] key,
-      byte[] iv) throws IOException {
-    this(input, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoInputStream(Properties props, InputStream in,
-      byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
-        Utils.getBufferSize(props), key, iv, streamOffset);
-  }
-
-  public CTRCryptoInputStream(Properties props, ReadableByteChannel in,
-      byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    this(in, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
-        Utils.getBufferSize(props), key, iv, streamOffset);
-  }
-
-  public CTRCryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
-      byte[] key, byte[] iv, long streamOffset) throws IOException {
-    this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv, streamOffset);
-  }
-
-  public CTRCryptoInputStream(ReadableByteChannel in, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
-    this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset);
-  }
-
-  public CTRCryptoInputStream(
-      Input input,
-      Cipher cipher,
-      int bufferSize,
-      byte[] key,
-      byte[] iv,
-      long streamOffset) throws IOException {
-    super(input, cipher, bufferSize, key, iv);
-
-    Utils.checkStreamCipher(cipher);
-
-    resetStreamOffset(streamOffset);
-  }
-
-  /** Skip n bytes */
-  @Override
-  public long skip(long n) throws IOException {
-    Utils.checkArgument(n >= 0, "Negative skip length.");
-    checkStream();
-
-    if (n == 0) {
-      return 0;
-    } else if (n <= outBuffer.remaining()) {
-      int pos = outBuffer.position() + (int) n;
-      outBuffer.position(pos);
-      return n;
-    } else {
-      /*
-       * Subtract outBuffer.remaining() to see how many bytes we need to
-       * skip in the underlying stream. Add outBuffer.remaining() to the
-       * actual number of skipped bytes in the underlying stream to get the
-       * number of skipped bytes from the user's point of view.
-       */
-      n -= outBuffer.remaining();
-      long skipped = input.skip(n);
-      if (skipped < 0) {
-        skipped = 0;
-      }
-      long pos = streamOffset + skipped;
-      skipped += outBuffer.remaining();
-      resetStreamOffset(pos);
-      return skipped;
-    }
-  }
-
-  /** ByteBuffer read. */
-  @Override
-  public int read(ByteBuffer buf) throws IOException {
-    checkStream();
-    int unread = outBuffer.remaining();
-    if (unread <= 0) { // Fill the unread decrypted data buffer firstly
-      final int n = input.read(inBuffer);
-      if (n <= 0) {
-        return n;
-      }
-
-      streamOffset += n; // Read n bytes
-      if (buf.isDirect() && buf.remaining() >= inBuffer.position() && padding == 0) {
-        // Use buf as the output buffer directly
-        decryptInPlace(buf);
-        padding = postDecryption(streamOffset);
-        return n;
-      } else {
-        // Use outBuffer as the output buffer
-        decrypt();
-        padding = postDecryption(streamOffset);
-      }
-    }
-
-    // Copy decrypted data from outBuffer to buf
-    unread = outBuffer.remaining();
-    final int toRead = buf.remaining();
-    if (toRead <= unread) {
-      final int limit = outBuffer.limit();
-      outBuffer.limit(outBuffer.position() + toRead);
-      buf.put(outBuffer);
-      outBuffer.limit(limit);
-      return toRead;
-    } else {
-      buf.put(outBuffer);
-      return unread;
-    }
-  }
-
-  /**
-   * Seek the stream to a specific position relative to start of the under layer stream.
-   * 
-   * @param position The position to seek to
-   * @throws IOException if seek failed
-   */
-  public void seek(long position) throws IOException {
-    Utils.checkArgument(position >= 0, "Cannot seek to negative offset.");
-    checkStream();
-    /*
-     * If data of target pos in the underlying stream has already been read
-     * and decrypted in outBuffer, we just need to re-position outBuffer.
-     */
-    if (position >= getStreamPosition() && position <= getStreamOffset()) {
-      int forward = (int) (position - getStreamPosition());
-      if (forward > 0) {
-        outBuffer.position(outBuffer.position() + forward);
-      }
-    } else {
-      input.seek(position);
-      resetStreamOffset(position);
-    }
-  }
-
-  protected long getStreamOffset() {
-    return streamOffset;
-  }
-
-  protected long getStreamPosition() {
-    return streamOffset - outBuffer.remaining();
-  }
-
-  /**
-   * Decrypt more data by reading the under layer stream. The decrypted data will
-   * be put in the output buffer.
-   *
-   * @return The number of decrypted data. -1 if end of the decrypted stream
-   */
-  protected int decryptMore() throws IOException {
-    int n = input.read(inBuffer);
-    if (n <= 0) {
-      return n;
-    }
-
-    streamOffset += n; // Read n bytes
-    decrypt();
-    padding = postDecryption(streamOffset);
-    return outBuffer.remaining();
-  }
-
-  /**
-   * Do the decryption using inBuffer as input and outBuffer as output.
-   * Upon return, inBuffer is cleared; the decrypted data starts at
-   * outBuffer.position() and ends at outBuffer.limit();
-   */
-  protected void decrypt() throws IOException {
-    Utils.checkState(inBuffer.position() >= padding);
-    if(inBuffer.position() == padding) {
-      // There is no real data in inBuffer.
-      return;
-    }
-
-    inBuffer.flip();
-    outBuffer.clear();
-    decryptBuffer(outBuffer);
-    inBuffer.clear();
-    outBuffer.flip();
-
-    if (padding > 0) {
-      /*
-       * The plain text and cipher text have a 1:1 mapping, they start at the
-       * same position.
-       */
-      outBuffer.position(padding);
-    }
-  }
-
-  /**
-   * Do the decryption using inBuffer as input and buf as output.
-   * Upon return, inBuffer is cleared; the buf's position will be equal to
-   * <i>p</i>&nbsp;<tt>+</tt>&nbsp;<i>n</i> where <i>p</i> is the position before
-   * decryption, <i>n</i> is the number of bytes decrypted.
-   * The buf's limit will not have changed.
-   */
-  protected void decryptInPlace(ByteBuffer buf) throws IOException {
-    Utils.checkState(inBuffer.position() >= padding);
-    Utils.checkState(buf.isDirect());
-    Utils.checkState(buf.remaining() >= inBuffer.position());
-    Utils.checkState(padding == 0);
-
-    if(inBuffer.position() == padding) {
-      // There is no real data in inBuffer.
-      return;
-    }
-    inBuffer.flip();
-    decryptBuffer(buf);
-    inBuffer.clear();
-  }
-
-  /**
-   * Decrypt all data in buf: total n bytes from given start position.
-   * Output is also buf and same start position.
-   * buf.position() and buf.limit() should be unchanged after decryption.
-   */
-  protected void decrypt(ByteBuffer buf, int offset, int len)
-      throws IOException {
-    final int pos = buf.position();
-    final int limit = buf.limit();
-    int n = 0;
-    while (n < len) {
-      buf.position(offset + n);
-      buf.limit(offset + n + Math.min(len - n, inBuffer.remaining()));
-      inBuffer.put(buf);
-      // Do decryption
-      try {
-        decrypt();
-        buf.position(offset + n);
-        buf.limit(limit);
-        n += outBuffer.remaining();
-        buf.put(outBuffer);
-      } finally {
-        padding = postDecryption(streamOffset - (len - n));
-      }
-    }
-    buf.position(pos);
-  }
-
-  /**
-   * This method is executed immediately after decryption. Check whether
-   * cipher should be updated and recalculate padding if needed.
-   */
-  protected byte postDecryption(long position) throws IOException {
-    byte padding = 0;
-    if (cipherReset) {
-      /*
-       * This code is generally not executed since the cipher usually
-       * maintains cipher context (e.g. the counter) internally. However,
-       * some implementations can't maintain context so a re-init is necessary
-       * after each decryption call.
-       */
-      resetCipher(position);
-      padding = getPadding(position);
-      inBuffer.position(padding);
-    }
-    return padding;
-  }
-
-  protected long getCounter(long position) {
-    return position / cipher.getTransformation().getAlgorithmBlockSize();
-  }
-
-  protected byte getPadding(long position) {
-    return (byte)(position % cipher.getTransformation().getAlgorithmBlockSize());
-  }
-
-  /** Initialize the cipher. */
-  @Override
-  protected void initCipher() {
-    // Do nothing for initCipher
-    // Will reset the cipher when reset the stream offset
-  }
-
-  /** Calculate the counter and iv, reset the cipher. */
-  protected void resetCipher(long position)
-      throws IOException {
-    final long counter = getCounter(position);
-    Utils.calculateIV(initIV, counter, iv);
-    try {
-      cipher.init(Cipher.DECRYPT_MODE, key, iv);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    } catch (InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-    cipherReset = false;
-  }
-
-  /**
-   * Reset the underlying stream offset; clear {@link #inBuffer} and
-   * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
-   */
-  protected void resetStreamOffset(long offset) throws IOException {
-    streamOffset = offset;
-    inBuffer.clear();
-    outBuffer.clear();
-    outBuffer.limit(0);
-    resetCipher(offset);
-    padding = getPadding(offset);
-    inBuffer.position(padding); // Set proper position for input data.
-  }
-
-  protected void decryptBuffer(ByteBuffer out)
-      throws IOException {
-    int inputSize = inBuffer.remaining();
-    try {
-      int n = cipher.update(inBuffer, out);
-      if (n < inputSize) {
-        /**
-         * Typically code will not get here. Cipher#update will consume all
-         * input data and put result in outBuffer.
-         * Cipher#doFinal will reset the cipher context.
-         */
-        cipher.doFinal(inBuffer, out);
-        cipherReset = true;
-      }
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    } catch (IllegalBlockSizeException e) {
-      throw new IOException(e);
-    } catch (BadPaddingException e) {
-      throw new IOException(e);
-    }
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
deleted file mode 100644
index cb282e3..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * 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 java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.WritableByteChannel;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.stream.output.ChannelOutput;
-import org.apache.commons.crypto.stream.output.Output;
-import org.apache.commons.crypto.stream.output.StreamOutput;
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * CTRCryptoOutputStream encrypts data. It is not thread-safe. AES CTR mode is
- * required in order to ensure that the plain text and cipher text have a 1:1
- * mapping. The encryption is buffer based. The key points of the encryption are
- * (1) calculating counter and (2) padding through stream position.
- * <p/>
- * counter = base + pos/(algorithm blocksize);
- * padding = pos%(algorithm blocksize);
- * <p/>
- * The underlying stream offset is maintained as state.
- */
-public class CTRCryptoOutputStream extends CryptoOutputStream {
-  /**
-   * Underlying stream offset.
-   */
-  protected long streamOffset = 0;
-
-  /**
-   * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer}
-   * before any other data goes in. The purpose of padding is to put input data
-   * at proper position.
-   */
-  private byte padding;
-
-  /**
-   * Flag to mark whether the cipher has been reset
-   */
-  private boolean cipherReset = false;
-
-  public CTRCryptoOutputStream(Properties props, OutputStream out,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, out, key, iv, 0);
-  }
-
-  public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, out, key, iv, 0);
-  }
-
-  public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(out, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(channel, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoOutputStream(Output output, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv)
-      throws IOException {
-    this(output, cipher, bufferSize, key, iv, 0);
-  }
-
-  public CTRCryptoOutputStream(Properties props, OutputStream out,
-      byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
-        Utils.getBufferSize(props), key, iv, streamOffset);
-  }
-
-  public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
-      byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
-        Utils.getBufferSize(props), key, iv, streamOffset);
-  }
-
-  public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
-    this(new StreamOutput(out, bufferSize), cipher,
-        bufferSize, key, iv, streamOffset);
-  }
-
-  public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
-    this(new ChannelOutput(channel), cipher,
-        bufferSize, key, iv, streamOffset);
-  }
-
-  public CTRCryptoOutputStream(Output output, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    super(output, cipher, bufferSize, key, iv);
-
-    Utils.checkStreamCipher(cipher);
-    this.streamOffset = streamOffset;
-
-    resetCipher();
-  }
-
-  /**
-   * Do the encryption, input is {@link #inBuffer} and output is
-   * {@link #outBuffer}.
-   */
-  @Override
-  protected void encrypt() throws IOException {
-    Utils.checkState(inBuffer.position() >= padding);
-    if (inBuffer.position() == padding) {
-      // There is no real data in the inBuffer.
-      return;
-    }
-
-    inBuffer.flip();
-    outBuffer.clear();
-    encryptBuffer(outBuffer);
-    inBuffer.clear();
-    outBuffer.flip();
-
-    if (padding > 0) {
-      /*
-       * The plain text and cipher text have a 1:1 mapping, they start at the
-       * same position.
-       */
-      outBuffer.position(padding);
-      padding = 0;
-    }
-
-    final int len = output.write(outBuffer);
-    streamOffset += len;
-    if (cipherReset) {
-      /*
-       * This code is generally not executed since the encryptor usually
-       * maintains encryption context (e.g. the counter) internally. However,
-       * some implementations can't maintain context so a re-init is necessary
-       * after each encryption call.
-       */
-      resetCipher();
-    }
-  }
-
-  /**
-   * Do final encryption of the last data
-   */
-  @Override
-  protected void encryptFinal() throws IOException {
-    // The same as the normal encryption for Counter mode
-    encrypt();
-  }
-
-  /** Initialize the cipher. */
-  @Override
-  protected void initCipher() {
-    // Do nothing for initCipher
-    // Will reset the cipher considering the stream offset
-  }
-
-  /** Reset the {@link #cipher}: calculate counter and {@link #padding}. */
-  private void resetCipher() throws IOException {
-    final long counter =
-        streamOffset / cipher.getTransformation().getAlgorithmBlockSize();
-    padding =
-        (byte)(streamOffset % cipher.getTransformation().getAlgorithmBlockSize());
-    inBuffer.position(padding); // Set proper position for input data.
-
-    Utils.calculateIV(initIV, counter, iv);
-    try {
-      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    }catch (InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-    cipherReset = false;
-  }
-
-  private void encryptBuffer(ByteBuffer out)
-      throws IOException {
-    int inputSize = inBuffer.remaining();
-    try {
-      int n = cipher.update(inBuffer, out);
-      if (n < inputSize) {
-        /**
-         * Typically code will not get here. Cipher#update will consume all
-         * input data and put result in outBuffer.
-         * Cipher#doFinal will reset the cipher context.
-         */
-        cipher.doFinal(inBuffer, out);
-        cipherReset = true;
-      }
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    } catch (BadPaddingException e) {
-      throw new IOException(e);
-    } catch (IllegalBlockSizeException e) {
-      throw new IOException(e);
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java
new file mode 100644
index 0000000..47a1c4b
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java
@@ -0,0 +1,394 @@
+/**
+ * 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 java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.input.ChannelInput;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.stream.input.StreamInput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CipherInputStream reads input data and decrypts data in stream manner. It supports
+ * any mode of operations such as AES CBC/CTR/GCM mode in concept.It is not thread-safe.
+ *
+ */
+
+public class CipherInputStream extends InputStream implements
+    ReadableByteChannel {
+  private final byte[] oneByteBuf = new byte[1];
+
+  protected final Cipher cipher;
+  protected final int bufferSize;
+
+  protected final byte[] key;
+  protected final byte[] initIV;
+  protected byte[] iv;
+
+  protected boolean closed;
+  protected boolean finalDone = false;
+
+  protected Input input;
+
+  /**
+   * Input data buffer. The data starts at inBuffer.position() and ends at
+   * to inBuffer.limit().
+   */
+  protected ByteBuffer inBuffer;
+
+  /**
+   * The decrypted data buffer. The data starts at outBuffer.position() and
+   * ends at outBuffer.limit();
+   */
+  protected ByteBuffer outBuffer;
+
+  public CipherInputStream(CipherTransformation transformation,
+                           Properties props, InputStream in, byte[] key, byte[] iv)
+      throws IOException {
+    this(in, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CipherInputStream(CipherTransformation transformation,
+                           Properties props, ReadableByteChannel in, byte[] key, byte[] iv)
+      throws IOException {
+    this(in, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CipherInputStream(InputStream in, Cipher cipher, int bufferSize,
+                           byte[] key, byte[] iv) throws IOException {
+    this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv);
+  }
+
+  public CipherInputStream(ReadableByteChannel in, Cipher cipher,
+                           int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(new ChannelInput(in), cipher, bufferSize, key, iv);
+  }
+
+  public CipherInputStream(
+      Input input,
+      Cipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv) throws IOException {
+    this.input = input;
+    this.cipher = cipher;
+    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
+    this.key = key.clone();
+    this.initIV = iv.clone();
+    this.iv = iv.clone();
+
+    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
+    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
+        cipher.getTransformation().getAlgorithmBlockSize());
+    outBuffer.limit(0);
+
+    initCipher();
+  }
+
+  @Override
+  public int read() throws IOException {
+    int n;
+    while ((n = read(oneByteBuf, 0, 1)) == 0) ;
+    return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
+  }
+
+  /**
+   * Decryption is buffer based.
+   * If there is data in {@link #outBuffer}, then read it out of this buffer.
+   * If there is no data in {@link #outBuffer}, then read more from the
+   * underlying stream and do the decryption.
+   * @param b the buffer into which the decrypted data is read.
+   * @param off the buffer offset.
+   * @param len the maximum number of decrypted data bytes to read.
+   * @return int the total number of decrypted data bytes read into the buffer.
+   * @throws IOException
+   */
+  @Override
+  public int read(byte[] b, int off, int len) throws IOException {
+    checkStream();
+    if (b == null) {
+      throw new NullPointerException();
+    } else if (off < 0 || len < 0 || len > b.length - off) {
+      throw new IndexOutOfBoundsException();
+    } else if (len == 0) {
+      return 0;
+    }
+
+    int remaining = outBuffer.remaining();
+    if (remaining > 0) {
+      // Satisfy the read with the existing data
+      int n = Math.min(len, remaining);
+      outBuffer.get(b, off, n);
+      return n;
+    } else {
+      // No data in the out buffer, try read new data and decrypt it
+      int nd = decryptMore();
+      if(nd <= 0)
+        return nd;
+
+      int n = Math.min(len, outBuffer.remaining());
+      outBuffer.get(b, off, n);
+      return n;
+    }
+  }
+
+  @Override
+  public long skip(long n) throws IOException {
+    Utils.checkArgument(n >= 0, "Negative skip length.");
+    checkStream();
+
+    if (n == 0) {
+      return 0;
+    }
+
+    long remaining = n;
+    int nd;
+
+    while (remaining > 0) {
+      if(remaining <= outBuffer.remaining()) {
+        // Skip in the remaining buffer
+        int pos = outBuffer.position() + (int) remaining;
+        outBuffer.position(pos);
+
+        remaining = 0;
+        break;
+      } else {
+        remaining -= outBuffer.remaining();
+        outBuffer.clear();
+      }
+
+      nd = decryptMore();
+      if (nd < 0) {
+        break;
+      }
+    }
+
+    return n - remaining;
+  }
+
+  @Override
+  public int available() throws IOException {
+    checkStream();
+
+    return input.available() + outBuffer.remaining();
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (closed) {
+      return;
+    }
+
+    input.close();
+    freeBuffers();
+    cipher.close();
+    super.close();
+    closed = true;
+  }
+
+  @Override
+  public void mark(int readlimit) {
+  }
+
+  @Override
+  public void reset() throws IOException {
+    throw new IOException("Mark/reset not supported");
+  }
+
+  @Override
+  public boolean markSupported() {
+    return false;
+  }
+
+  @Override
+  public boolean isOpen() {
+    return !closed;
+  }
+
+  @Override
+  public int read(ByteBuffer dst) throws IOException {
+    checkStream();
+    int remaining = outBuffer.remaining();
+    if (remaining <= 0) {
+      // Decrypt more data
+      int nd = decryptMore();
+      if(nd < 0) {
+        return -1;
+      }
+    }
+
+    // Copy decrypted data from outBuffer to dst
+    remaining = outBuffer.remaining();
+    final int toRead = dst.remaining();
+    if (toRead <= remaining) {
+      final int limit = outBuffer.limit();
+      outBuffer.limit(outBuffer.position() + toRead);
+      dst.put(outBuffer);
+      outBuffer.limit(limit);
+      return toRead;
+    } else {
+      dst.put(outBuffer);
+      return remaining;
+    }
+  }
+
+  /**
+   * Get the buffer size
+   */
+  protected int getBufferSize() {
+    return bufferSize;
+  }
+
+  /**
+   * Get the key
+   */
+  protected byte[] getKey() {
+    return key;
+  }
+
+  /**
+   * Get the initialization vector
+   */
+  protected byte[] getInitIV() {
+    return initIV;
+  }
+
+  /**
+   * Get the internal Cipher
+   */
+  protected Cipher getCipher() {
+    return cipher;
+  }
+
+  /** Initialize the cipher. */
+  protected void initCipher()
+      throws IOException {
+    try {
+      cipher.init(Cipher.DECRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch(InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * Decrypt more data by reading the under layer stream. The decrypted data will
+   * be put in the output buffer. If the end of the under stream reached, we will
+   * do final of the cipher to finish all the decrypting of data.
+   *
+   * @return The number of decrypted data. -1 if end of the decrypted stream
+   */
+  protected int decryptMore() throws IOException {
+    if(finalDone) {
+      return -1;
+    }
+
+    int n = input.read(inBuffer);
+    if (n < 0) {
+      // The stream is end, finalize the cipher stream
+      decryptFinal();
+
+      // Satisfy the read with the remaining
+      int remaining = outBuffer.remaining();
+      if (remaining > 0) {
+        return remaining;
+      }
+
+      // End of the stream
+      return -1;
+    } else if(n == 0) {
+      // No data is read, but the stream is not end yet
+      return 0;
+    } else {
+      decrypt();
+      return outBuffer.remaining();
+    }
+  }
+
+  /**
+   * Do the decryption using inBuffer as input and outBuffer as output.
+   * Upon return, inBuffer is cleared; the decrypted data starts at
+   * outBuffer.position() and ends at outBuffer.limit();
+   */
+  protected void decrypt() throws IOException {
+    // Prepare the input buffer and clear the out buffer
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.update(inBuffer, outBuffer);
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    }
+
+    // Clear the input buffer and prepare out buffer
+    inBuffer.clear();
+    outBuffer.flip();
+  }
+
+  /**
+   * Do final of the cipher to end the decrypting stream
+   */
+  protected void decryptFinal() throws IOException {
+    // Prepare the input buffer and clear the out buffer
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.doFinal(inBuffer, outBuffer);
+      finalDone = true;
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch( BadPaddingException e) {
+      throw new IOException(e);
+    }
+
+    // Clear the input buffer and prepare out buffer
+    inBuffer.clear();
+    outBuffer.flip();
+  }
+
+  protected void checkStream() throws IOException {
+    if (closed) {
+      throw new IOException("Stream closed");
+    }
+  }
+
+  /** Forcibly free the direct buffers. */
+  protected void freeBuffers() {
+    Utils.freeDirectBuffer(inBuffer);
+    Utils.freeDirectBuffer(outBuffer);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
new file mode 100644
index 0000000..b0a0f96
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CipherOutputStream.java
@@ -0,0 +1,283 @@
+/**
+ * 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 java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.output.ChannelOutput;
+import org.apache.commons.crypto.stream.output.Output;
+import org.apache.commons.crypto.stream.output.StreamOutput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CipherOutputStream encrypts data and writes to the under layer output. It supports
+ * any mode of operations such as AES CBC/CTR/GCM mode in concept. It is not thread-safe.
+ */
+
+public class CipherOutputStream extends OutputStream implements
+    WritableByteChannel {
+  private final byte[] oneByteBuf = new byte[1];
+
+  protected Output output;
+  protected final Cipher cipher;
+  protected final int bufferSize;
+
+  protected final byte[] key;
+  protected final byte[] initIV;
+  protected byte[] iv;
+
+  protected boolean closed;
+
+  /**
+   * Input data buffer. The data starts at inBuffer.position() and ends at
+   * inBuffer.limit().
+   */
+  protected ByteBuffer inBuffer;
+
+  /**
+   * Encrypted data buffer. The data starts at outBuffer.position() and ends at
+   * outBuffer.limit();
+   */
+  protected ByteBuffer outBuffer;
+
+  public CipherOutputStream(CipherTransformation transformation,
+                            Properties props, OutputStream out, byte[] key, byte[] iv)
+      throws IOException {
+    this(out, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CipherOutputStream(CipherTransformation transformation,
+                            Properties props, WritableByteChannel out, byte[] key, byte[] iv)
+      throws IOException {
+    this(out, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CipherOutputStream(OutputStream out, Cipher cipher,
+                            int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv);
+  }
+
+  public CipherOutputStream(WritableByteChannel channel, Cipher cipher,
+                            int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(new ChannelOutput(channel), cipher, bufferSize, key, iv);
+  }
+
+  protected CipherOutputStream(Output output, Cipher cipher,
+                               int bufferSize, byte[] key, byte[] iv)
+      throws IOException {
+
+    this.output = output;
+    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
+    this.cipher = cipher;
+    this.key = key.clone();
+    this.initIV = iv.clone();
+    this.iv = iv.clone();
+    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
+    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
+        cipher.getTransformation().getAlgorithmBlockSize());
+
+    initCipher();
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    oneByteBuf[0] = (byte)(b & 0xff);
+    write(oneByteBuf, 0, oneByteBuf.length);
+  }
+
+  /**
+   * Encryption is buffer based.
+   * If there is enough room in {@link #inBuffer}, then write to this buffer.
+   * If {@link #inBuffer} is full, then do encryption and write data to the
+   * underlying stream.
+   * @param b the data.
+   * @param off the start offset in the data.
+   * @param len the number of bytes to write.
+   * @throws IOException
+   */
+  public void write(byte[] b, int off, int len) throws IOException {
+    checkStream();
+    if (b == null) {
+      throw new NullPointerException();
+    } else if (off < 0 || len < 0 || off > b.length ||
+        len > b.length - off) {
+      throw new IndexOutOfBoundsException();
+    }
+
+    while (len > 0) {
+      final int remaining = inBuffer.remaining();
+      if (len < remaining) {
+        inBuffer.put(b, off, len);
+        len = 0;
+      } else {
+        inBuffer.put(b, off, remaining);
+        off += remaining;
+        len -= remaining;
+        encrypt();
+      }
+    }
+  }
+
+  /**
+   * To flush, we need to encrypt the data in the buffer and write to the
+   * underlying stream, then do the flush.
+   */
+  @Override
+  public void flush() throws IOException {
+    checkStream();
+    encrypt();
+    output.flush();
+    super.flush();
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (closed) {
+      return;
+    }
+
+    try {
+      encryptFinal();
+      output.close();
+      freeBuffers();
+      cipher.close();
+      super.close();
+    } finally {
+      closed = true;
+    }
+  }
+
+  @Override
+  public boolean isOpen() {
+    return !closed;
+  }
+
+  @Override
+  public int write(ByteBuffer src) throws IOException {
+    checkStream();
+    final int len = src.remaining();
+    int remaining = len;
+    while (remaining > 0) {
+      final int space = inBuffer.remaining();
+      if (remaining < space) {
+        inBuffer.put(src);
+        remaining = 0;
+      } else {
+        // to void copy twice, we set the limit to copy directly
+        final int oldLimit = src.limit();
+        final int newLimit = src.position() + space;
+        src.limit(newLimit);
+
+        inBuffer.put(src);
+
+        // restore the old limit
+        src.limit(oldLimit);
+
+        remaining -= space;
+        encrypt();
+      }
+    }
+
+    return len;
+  }
+
+  /** Initialize the cipher. */
+  protected void initCipher()
+      throws IOException {
+    try {
+      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch(InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * Do the encryption, input is {@link #inBuffer} and output is
+   * {@link #outBuffer}.
+   */
+  protected void encrypt() throws IOException {
+
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.update(inBuffer, outBuffer);
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    }
+
+    inBuffer.clear();
+    outBuffer.flip();
+
+    // write to output
+    output.write(outBuffer);
+  }
+
+  /**
+   * Do final encryption of the last data
+   */
+  protected void encryptFinal() throws IOException {
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.doFinal(inBuffer, outBuffer);
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch( BadPaddingException e) {
+      throw new IOException(e);
+    }
+
+    inBuffer.clear();
+    outBuffer.flip();
+
+    // write to output
+    output.write(outBuffer);
+  }
+
+  protected void checkStream() throws IOException {
+    if (closed) {
+      throw new IOException("Stream closed");
+    }
+  }
+
+  /** Forcibly free the direct buffers. */
+  protected void freeBuffers() {
+    Utils.freeDirectBuffer(inBuffer);
+    Utils.freeDirectBuffer(outBuffer);
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ad81d236/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java b/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
deleted file mode 100644
index 6498273..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/**
- * 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 java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.util.Properties;
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.cipher.Cipher;
-import org.apache.commons.crypto.cipher.CipherTransformation;
-import org.apache.commons.crypto.stream.input.ChannelInput;
-import org.apache.commons.crypto.stream.input.Input;
-import org.apache.commons.crypto.stream.input.StreamInput;
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * CryptoInputStream reads input data and decrypts data in stream manner. It supports
- * any mode of operations such as AES CBC/CTR/GCM mode in concept.It is not thread-safe.
- *
- */
-
-public class CryptoInputStream extends InputStream implements
-    ReadableByteChannel {
-  private final byte[] oneByteBuf = new byte[1];
-
-  protected final Cipher cipher;
-  protected final int bufferSize;
-
-  protected final byte[] key;
-  protected final byte[] initIV;
-  protected byte[] iv;
-
-  protected boolean closed;
-  protected boolean finalDone = false;
-
-  protected Input input;
-
-  /**
-   * Input data buffer. The data starts at inBuffer.position() and ends at
-   * to inBuffer.limit().
-   */
-  protected ByteBuffer inBuffer;
-
-  /**
-   * The decrypted data buffer. The data starts at outBuffer.position() and
-   * ends at outBuffer.limit();
-   */
-  protected ByteBuffer outBuffer;
-
-  public CryptoInputStream(CipherTransformation transformation,
-      Properties props, InputStream in, byte[] key, byte[] iv)
-      throws IOException {
-    this(in, Utils.getCipherInstance(transformation, props),
-        Utils.getBufferSize(props), key, iv);
-  }
-
-  public CryptoInputStream(CipherTransformation transformation,
-      Properties props, ReadableByteChannel in, byte[] key, byte[] iv)
-      throws IOException {
-    this(in, Utils.getCipherInstance(transformation, props),
-        Utils.getBufferSize(props), key, iv);
-  }
-
-  public CryptoInputStream(InputStream in, Cipher cipher, int bufferSize,
-      byte[] key, byte[] iv) throws IOException {
-    this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv);
-  }
-
-  public CryptoInputStream(ReadableByteChannel in, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(new ChannelInput(in), cipher, bufferSize, key, iv);
-  }
-
-  public CryptoInputStream(
-      Input input,
-      Cipher cipher,
-      int bufferSize,
-      byte[] key,
-      byte[] iv) throws IOException {
-    this.input = input;
-    this.cipher = cipher;
-    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
-    this.key = key.clone();
-    this.initIV = iv.clone();
-    this.iv = iv.clone();
-
-    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
-    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
-        cipher.getTransformation().getAlgorithmBlockSize());
-    outBuffer.limit(0);
-
-    initCipher();
-  }
-
-  @Override
-  public int read() throws IOException {
-    int n;
-    while ((n = read(oneByteBuf, 0, 1)) == 0) ;
-    return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
-  }
-
-  /**
-   * Decryption is buffer based.
-   * If there is data in {@link #outBuffer}, then read it out of this buffer.
-   * If there is no data in {@link #outBuffer}, then read more from the
-   * underlying stream and do the decryption.
-   * @param b the buffer into which the decrypted data is read.
-   * @param off the buffer offset.
-   * @param len the maximum number of decrypted data bytes to read.
-   * @return int the total number of decrypted data bytes read into the buffer.
-   * @throws IOException
-   */
-  @Override
-  public int read(byte[] b, int off, int len) throws IOException {
-    checkStream();
-    if (b == null) {
-      throw new NullPointerException();
-    } else if (off < 0 || len < 0 || len > b.length - off) {
-      throw new IndexOutOfBoundsException();
-    } else if (len == 0) {
-      return 0;
-    }
-
-    int remaining = outBuffer.remaining();
-    if (remaining > 0) {
-      // Satisfy the read with the existing data
-      int n = Math.min(len, remaining);
-      outBuffer.get(b, off, n);
-      return n;
-    } else {
-      // No data in the out buffer, try read new data and decrypt it
-      int nd = decryptMore();
-      if(nd <= 0)
-        return nd;
-
-      int n = Math.min(len, outBuffer.remaining());
-      outBuffer.get(b, off, n);
-      return n;
-    }
-  }
-
-  @Override
-  public long skip(long n) throws IOException {
-    Utils.checkArgument(n >= 0, "Negative skip length.");
-    checkStream();
-
-    if (n == 0) {
-      return 0;
-    }
-
-    long remaining = n;
-    int nd;
-
-    while (remaining > 0) {
-      if(remaining <= outBuffer.remaining()) {
-        // Skip in the remaining buffer
-        int pos = outBuffer.position() + (int) remaining;
-        outBuffer.position(pos);
-
-        remaining = 0;
-        break;
-      } else {
-        remaining -= outBuffer.remaining();
-        outBuffer.clear();
-      }
-
-      nd = decryptMore();
-      if (nd < 0) {
-        break;
-      }
-    }
-
-    return n - remaining;
-  }
-
-  @Override
-  public int available() throws IOException {
-    checkStream();
-
-    return input.available() + outBuffer.remaining();
-  }
-
-  @Override
-  public void close() throws IOException {
-    if (closed) {
-      return;
-    }
-
-    input.close();
-    freeBuffers();
-    cipher.close();
-    super.close();
-    closed = true;
-  }
-
-  @Override
-  public void mark(int readlimit) {
-  }
-
-  @Override
-  public void reset() throws IOException {
-    throw new IOException("Mark/reset not supported");
-  }
-
-  @Override
-  public boolean markSupported() {
-    return false;
-  }
-
-  @Override
-  public boolean isOpen() {
-    return !closed;
-  }
-
-  @Override
-  public int read(ByteBuffer dst) throws IOException {
-    checkStream();
-    int remaining = outBuffer.remaining();
-    if (remaining <= 0) {
-      // Decrypt more data
-      int nd = decryptMore();
-      if(nd < 0) {
-        return -1;
-      }
-    }
-
-    // Copy decrypted data from outBuffer to dst
-    remaining = outBuffer.remaining();
-    final int toRead = dst.remaining();
-    if (toRead <= remaining) {
-      final int limit = outBuffer.limit();
-      outBuffer.limit(outBuffer.position() + toRead);
-      dst.put(outBuffer);
-      outBuffer.limit(limit);
-      return toRead;
-    } else {
-      dst.put(outBuffer);
-      return remaining;
-    }
-  }
-
-  /**
-   * Get the buffer size
-   */
-  protected int getBufferSize() {
-    return bufferSize;
-  }
-
-  /**
-   * Get the key
-   */
-  protected byte[] getKey() {
-    return key;
-  }
-
-  /**
-   * Get the initialization vector
-   */
-  protected byte[] getInitIV() {
-    return initIV;
-  }
-
-  /**
-   * Get the internal Cipher
-   */
-  protected Cipher getCipher() {
-    return cipher;
-  }
-
-  /** Initialize the cipher. */
-  protected void initCipher()
-      throws IOException {
-    try {
-      cipher.init(Cipher.DECRYPT_MODE, key, iv);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    } catch(InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-  }
-
-  /**
-   * Decrypt more data by reading the under layer stream. The decrypted data will
-   * be put in the output buffer. If the end of the under stream reached, we will
-   * do final of the cipher to finish all the decrypting of data.
-   *
-   * @return The number of decrypted data. -1 if end of the decrypted stream
-   */
-  protected int decryptMore() throws IOException {
-    if(finalDone) {
-      return -1;
-    }
-
-    int n = input.read(inBuffer);
-    if (n < 0) {
-      // The stream is end, finalize the cipher stream
-      decryptFinal();
-
-      // Satisfy the read with the remaining
-      int remaining = outBuffer.remaining();
-      if (remaining > 0) {
-        return remaining;
-      }
-
-      // End of the stream
-      return -1;
-    } else if(n == 0) {
-      // No data is read, but the stream is not end yet
-      return 0;
-    } else {
-      decrypt();
-      return outBuffer.remaining();
-    }
-  }
-
-  /**
-   * Do the decryption using inBuffer as input and outBuffer as output.
-   * Upon return, inBuffer is cleared; the decrypted data starts at
-   * outBuffer.position() and ends at outBuffer.limit();
-   */
-  protected void decrypt() throws IOException {
-    // Prepare the input buffer and clear the out buffer
-    inBuffer.flip();
-    outBuffer.clear();
-
-    try {
-      cipher.update(inBuffer, outBuffer);
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    }
-
-    // Clear the input buffer and prepare out buffer
-    inBuffer.clear();
-    outBuffer.flip();
-  }
-
-  /**
-   * Do final of the cipher to end the decrypting stream
-   */
-  protected void decryptFinal() throws IOException {
-    // Prepare the input buffer and clear the out buffer
-    inBuffer.flip();
-    outBuffer.clear();
-
-    try {
-      cipher.doFinal(inBuffer, outBuffer);
-      finalDone = true;
-    } catch (ShortBufferException e) {
-      throw new IOException(e);
-    } catch (IllegalBlockSizeException e) {
-      throw new IOException(e);
-    } catch( BadPaddingException e) {
-      throw new IOException(e);
-    }
-
-    // Clear the input buffer and prepare out buffer
-    inBuffer.clear();
-    outBuffer.flip();
-  }
-
-  protected void checkStream() throws IOException {
-    if (closed) {
-      throw new IOException("Stream closed");
-    }
-  }
-
-  /** Forcibly free the direct buffers. */
-  protected void freeBuffers() {
-    Utils.freeDirectBuffer(inBuffer);
-    Utils.freeDirectBuffer(outBuffer);
-  }
-}
\ No newline at end of file