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:49 UTC

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

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