You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2016/06/30 16:03:57 UTC

[1/2] commons-crypto git commit: [CRYPTO-94] Consistently camel-case type names: Ctr*.

Repository: commons-crypto
Updated Branches:
  refs/heads/master 294d73b24 -> ba14d8019


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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..376a56b
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CtrNoPaddingCipherStreamTest.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+
+public class CtrNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
+
+    @Override
+    public void setUp() throws IOException {
+        transformation = "AES/CTR/NoPadding";
+    }
+
+}


[2/2] commons-crypto git commit: [CRYPTO-94] Consistently camel-case type names: Ctr*.

Posted by gg...@apache.org.
[CRYPTO-94] Consistently camel-case type names: Ctr*.

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

Branch: refs/heads/master
Commit: ba14d801935decdb9399d6b2f038e0d1a71609e1
Parents: 294d73b
Author: Gary Gregory <gg...@apache.org>
Authored: Thu Jun 30 09:03:53 2016 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Thu Jun 30 09:03:53 2016 -0700

----------------------------------------------------------------------
 .../crypto/stream/CTRCryptoInputStream.java     | 672 -------------------
 .../crypto/stream/CTRCryptoOutputStream.java    | 384 -----------
 .../crypto/stream/CryptoInputStream.java        |   2 +-
 .../crypto/stream/CtrCryptoInputStream.java     | 672 +++++++++++++++++++
 .../crypto/stream/CtrCryptoOutputStream.java    | 384 +++++++++++
 .../stream/PositionedCryptoInputStream.java     |   4 +-
 .../crypto/stream/CTRCryptoStreamTest.java      |  55 --
 .../stream/CTRNoPaddingCipherStreamTest.java    |  29 -
 .../crypto/stream/CtrCryptoStreamTest.java      |  55 ++
 .../stream/CtrNoPaddingCipherStreamTest.java    |  29 +
 10 files changed, 1143 insertions(+), 1143 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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 6c6dacc..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
+++ /dev/null
@@ -1,672 +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.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.crypto.cipher.CryptoCipher;
-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;
-
-/**
- * <p>
- * 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>
- * <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
-     */
-    private long streamOffset = 0;
-
-    /**
-     * The initial IV.
-     */
-    private final byte[] initIV;
-
-    /**
-     * Initialization vector for the cipher.
-     */
-    private byte[] iv;
-
-    /**
-     * 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;
-
-    /**
-     * For AES, the algorithm block is fixed size of 128 bits.
-     *
-     * @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">
-     *      http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</a>
-     */
-    private static final int AES_BLOCK_SIZE = 16;
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param in the input stream.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoInputStream(Properties props, InputStream in, byte[] key,
-            byte[] iv) throws IOException {
-        this(props, in, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param in the ReadableByteChannel instance.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoInputStream(Properties props, ReadableByteChannel in,
-            byte[] key, byte[] iv) throws IOException {
-        this(props, in, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param in the input stream.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(InputStream in, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv) throws IOException {
-        this(in, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param in the ReadableByteChannel instance.
-     * @param cipher the cipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv) throws IOException {
-        this(in, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param input the input data.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(Input input, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv) throws IOException {
-        this(input, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param in the InputStream instance.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoInputStream(Properties props, InputStream in, byte[] key,
-            byte[] iv, long streamOffset) throws IOException {
-        this(in, Utils.getCipherInstance(
-                "AES/CTR/NoPadding", props),
-                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param in the ReadableByteChannel instance.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoInputStream(Properties props, ReadableByteChannel in,
-            byte[] key, byte[] iv, long streamOffset) throws IOException {
-        this(in, Utils.getCipherInstance(
-                "AES/CTR/NoPadding", props),
-                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param in the InputStream instance.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(InputStream in, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv, long streamOffset)
-            throws IOException {
-        this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv,
-                streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param in the ReadableByteChannel instance.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv, long streamOffset)
-            throws IOException {
-        this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoInputStream}.
-     *
-     * @param input the input data.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoInputStream(Input input, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv, long streamOffset)
-            throws IOException {
-        super(input, cipher, bufferSize, new SecretKeySpec(key, "AES"),
-                new IvParameterSpec(iv));
-
-        this.initIV = iv.clone();
-        this.iv = iv.clone();
-
-        CryptoInputStream.checkStreamCipher(cipher);
-
-        resetStreamOffset(streamOffset);
-    }
-
-    /**
-     * Overrides the {@link CryptoInputStream#skip(long)}. Skips over and
-     * discards <code>n</code> bytes of data from this input stream.
-     *
-     * @param n the number of bytes to be skipped.
-     * @return the actual number of bytes skipped.
-     * @throws IOException if an I/O error occurs.
-     */
-    @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;
-        }
-    }
-
-    /**
-     * Overrides the {@link CTRCryptoInputStream#read(ByteBuffer)}. Reads a
-     * sequence of bytes from this channel into the given buffer.
-     *
-     * @param buf The buffer into which bytes are to be transferred.
-     * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the
-     *         channel has reached end-of-stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    @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;
-            }
-            // 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;
-        }
-        buf.put(outBuffer);
-        return unread;
-    }
-
-    /**
-     * Seeks the stream to a specific position relative to start of the under
-     * layer stream.
-     *
-     * @param position the given position in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    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);
-        }
-    }
-
-    /**
-     * Gets the offset of the stream.
-     *
-     * @return the stream offset.
-     */
-    protected long getStreamOffset() {
-        return streamOffset;
-    }
-
-    /**
-     * Sets the offset of stream.
-     *
-     * @param streamOffset the stream offset.
-     */
-    protected void setStreamOffset(long streamOffset) {
-        this.streamOffset = streamOffset;
-    }
-
-    /**
-     * Gets the position of the stream.
-     *
-     * @return the position of the stream.
-     */
-    protected long getStreamPosition() {
-        return streamOffset - outBuffer.remaining();
-    }
-
-    /**
-     * Decrypts 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.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    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();
-    }
-
-    /**
-     * Does 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().
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    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);
-        }
-    }
-
-    /**
-     * Does 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.
-     *
-     * @param buf The buffer into which bytes are to be transferred.
-     * @throws IOException if an I/O error occurs.
-     */
-    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();
-    }
-
-    /**
-     * Decrypts 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.
-     *
-     * @param buf The buffer into which bytes are to be transferred.
-     * @param offset the start offset in the data.
-     * @param len the the maximum number of decrypted data bytes to read.
-     * @throws IOException if an I/O error occurs.
-     */
-    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. Checks whether
-     * cipher should be updated and recalculate padding if needed.
-     *
-     * @param position the given position in the data..
-     * @return the byte.
-     * @throws IOException if an I/O error occurs.
-     */
-    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;
-    }
-
-    /**
-     * Gets the initialization vector.
-     *
-     * @return the initIV.
-     */
-    protected byte[] getInitIV() {
-        return initIV;
-    }
-
-    /**
-     * Gets the counter for input stream position.
-     *
-     * @param position the given position in the data.
-     * @return the counter for input stream position.
-     */
-    protected long getCounter(long position) {
-        return position / cipher.getBlockSize();
-    }
-
-    /**
-     * Gets the padding for input stream position.
-     *
-     * @param position the given position in the data.
-     * @return the padding for input stream position.
-     */
-    protected byte getPadding(long position) {
-        return (byte) (position % cipher.getBlockSize());
-    }
-
-    /**
-     * Overrides the {@link CTRCryptoInputStream#initCipher()}. Initializes the
-     * cipher.
-     */
-    @Override
-    protected void initCipher() {
-        // Do nothing for initCipher
-        // Will reset the cipher when reset the stream offset
-    }
-
-    /**
-     * Calculates the counter and iv, resets the cipher.
-     *
-     * @param position the given position in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected void resetCipher(long position) throws IOException {
-        final long counter = getCounter(position);
-        CTRCryptoInputStream.calculateIV(initIV, counter, iv);
-        try {
-            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
-        } catch (InvalidKeyException e) {
-            throw new IOException(e);
-        } catch (InvalidAlgorithmParameterException e) {
-            throw new IOException(e);
-        }
-        cipherReset = false;
-    }
-
-    /**
-     * Resets the underlying stream offset; clear {@link #inBuffer} and
-     * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
-     *
-     * @param offset the offset of the stream.
-     * @throws IOException if an I/O error occurs.
-     */
-    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.
-    }
-
-    /**
-     * Does the decryption using out as output.
-     *
-     * @param out the output ByteBuffer.
-     * @throws IOException if an I/O error occurs.
-     */
-    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. CryptoCipher#update will
-                 * consume all input data and put result in outBuffer.
-                 * CryptoCipher#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);
-        }
-    }
-
-    /**
-     * <p>
-     * This method is only for Counter (CTR) mode. Generally the CryptoCipher
-     * calculates the IV and maintain encryption context internally.For example
-     * a Cipher will maintain its encryption context internally when we do
-     * encryption/decryption using the CryptoCipher#update interface.
-     * </p>
-     * <p>
-     * Encryption/Decryption is not always on the entire file. For example, in
-     * Hadoop, a node may only decrypt a portion of a file (i.e. a split). In
-     * these situations, the counter is derived from the file position.
-     * </p>
-     * The IV can be calculated by combining the initial IV and the counter with
-     * a lossless operation (concatenation, addition, or XOR).
-     *
-     * @see <a
-     *      href="http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29">
-     *      http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29</a>
-     *
-     * @param initIV initial IV
-     * @param counter counter for input stream position
-     * @param IV the IV for input stream position
-     */
-    static void calculateIV(byte[] initIV, long counter, byte[] IV) {
-        Utils.checkArgument(initIV.length == CTRCryptoInputStream.AES_BLOCK_SIZE);
-        Utils.checkArgument(IV.length == CTRCryptoInputStream.AES_BLOCK_SIZE);
-    
-        int i = IV.length; // IV length
-        int j = 0; // counter bytes index
-        int sum = 0;
-        while (i-- > 0) {
-            // (sum >>> Byte.SIZE) is the carry for addition
-            sum = (initIV[i] & 0xff) + (sum >>> Byte.SIZE); // NOPMD
-            if (j++ < 8) { // Big-endian, and long is 8 bytes length
-                sum += (byte) counter & 0xff;
-                counter >>>= 8;
-            }
-            IV[i] = (byte) sum;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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 6f9ea6b..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
+++ /dev/null
@@ -1,384 +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.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.crypto.cipher.CryptoCipher;
-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;
-
-/**
- * <p>
- * 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>
- * <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.
-     */
-    private long streamOffset = 0;
-
-    /**
-     * The initial IV.
-     */
-    private final byte[] initIV;
-
-    /**
-     * Initialization vector for the cipher.
-     */
-    private byte[] iv;
-
-    /**
-     * 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;
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param out the output stream.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoOutputStream(Properties props, OutputStream out,
-            byte[] key, byte[] iv) throws IOException {
-        this(props, out, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param out the WritableByteChannel instance.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
-            byte[] key, byte[] iv) throws IOException {
-        this(props, out, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param out the output stream.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(OutputStream out, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv) throws IOException {
-        this(out, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param channel the WritableByteChannel instance.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(WritableByteChannel channel,
-            CryptoCipher cipher, int bufferSize, byte[] key, byte[] iv)
-            throws IOException {
-        this(channel, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param output the Output instance.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(Output output, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv) throws IOException {
-        this(output, cipher, bufferSize, key, iv, 0);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param out the output stream.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoOutputStream(Properties props, OutputStream out,
-            byte[] key, byte[] iv, long streamOffset) throws IOException {
-        this(out, Utils.getCipherInstance(
-                "AES/CTR/NoPadding", props),
-                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param props The <code>Properties</code> class represents a set of
-     *        properties.
-     * @param out the WritableByteChannel instance.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
-            byte[] key, byte[] iv, long streamOffset) throws IOException {
-        this(out, Utils.getCipherInstance(
-                "AES/CTR/NoPadding", props),
-                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param out the output stream.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(OutputStream out, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv, long streamOffset)
-            throws IOException {
-        this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv,
-                streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param channel the WritableByteChannel instance.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(WritableByteChannel channel,
-            CryptoCipher cipher, int bufferSize, byte[] key, byte[] iv,
-            long streamOffset) throws IOException {
-        this(new ChannelOutput(channel), cipher, bufferSize, key, iv,
-                streamOffset);
-    }
-
-    /**
-     * Constructs a {@link CTRCryptoOutputStream}.
-     *
-     * @param output the output stream.
-     * @param cipher the CryptoCipher instance.
-     * @param bufferSize the bufferSize.
-     * @param key crypto key for the cipher.
-     * @param iv Initialization vector for the cipher.
-     * @param streamOffset the start offset in the data.
-     * @throws IOException if an I/O error occurs.
-     */
-    protected CTRCryptoOutputStream(Output output, CryptoCipher cipher,
-            int bufferSize, byte[] key, byte[] iv, long streamOffset)
-            throws IOException {
-        super(output, cipher, bufferSize, new SecretKeySpec(key, "AES"),
-                new IvParameterSpec(iv));
-
-        CryptoInputStream.checkStreamCipher(cipher);
-        this.streamOffset = streamOffset;
-        this.initIV = iv.clone();
-        this.iv = iv.clone();
-
-        resetCipher();
-    }
-
-    /**
-     * Does the encryption, input is {@link #inBuffer} and output is
-     * {@link #outBuffer}.
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @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();
-        }
-    }
-
-    /**
-     * Does final encryption of the last data.
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    protected void encryptFinal() throws IOException {
-        // The same as the normal encryption for Counter mode
-        encrypt();
-    }
-
-    /**
-     * Overrides the {@link CryptoOutputStream#initCipher()}. Initializes the
-     * cipher.
-     */
-    @Override
-    protected void initCipher() {
-        // Do nothing for initCipher
-        // Will reset the cipher considering the stream offset
-    }
-
-    /**
-     * Resets the {@link #cipher}: calculate counter and {@link #padding}.
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    private void resetCipher() throws IOException {
-        final long counter = streamOffset
-                / cipher.getBlockSize();
-        padding = (byte) (streamOffset % cipher.getBlockSize());
-        inBuffer.position(padding); // Set proper position for input data.
-
-        CTRCryptoInputStream.calculateIV(initIV, counter, iv);
-        try {
-            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
-        } catch (InvalidKeyException e) {
-            throw new IOException(e);
-        } catch (InvalidAlgorithmParameterException e) {
-            throw new IOException(e);
-        }
-        cipherReset = false;
-    }
-
-    /**
-     * Does the encryption if the ByteBuffer data.
-     *
-     * @param out the output ByteBuffer.
-     * @throws IOException if an I/O error occurs.
-     */
-    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. CryptoCipher#update will
-                 * consume all input data and put result in outBuffer.
-                 * CryptoCipher#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);
-        }
-    }
-
-    /**
-     * Get the underlying stream offset
-     *
-     * @return the underlying stream offset
-     */
-    protected long getStreamOffset() {
-        return streamOffset;
-    }
-
-    /**
-     * Set the underlying stream offset
-     *
-     * @param streamOffset the underlying stream offset
-     */
-    protected void setStreamOffset(long streamOffset) {
-        this.streamOffset = streamOffset;
-    }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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
index 655f3b5..334d391 100644
--- a/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
+++ b/src/main/java/org/apache/commons/crypto/stream/CryptoInputStream.java
@@ -356,7 +356,7 @@ public class CryptoInputStream extends InputStream implements
     /**
      * Overrides the {@link InputStream#markSupported()}.
      *
-     * @return false,the {@link CTRCryptoInputStream} don't support the mark
+     * @return false,the {@link CtrCryptoInputStream} don't support the mark
      *         method.
      */
     @Override

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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
new file mode 100644
index 0000000..56ad048
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CtrCryptoInputStream.java
@@ -0,0 +1,672 @@
+/**
+ * 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.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.crypto.cipher.CryptoCipher;
+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;
+
+/**
+ * <p>
+ * 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>
+ * <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
+     */
+    private long streamOffset = 0;
+
+    /**
+     * The initial IV.
+     */
+    private final byte[] initIV;
+
+    /**
+     * Initialization vector for the cipher.
+     */
+    private byte[] iv;
+
+    /**
+     * 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;
+
+    /**
+     * For AES, the algorithm block is fixed size of 128 bits.
+     *
+     * @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">
+     *      http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</a>
+     */
+    private static final int AES_BLOCK_SIZE = 16;
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param in the input stream.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoInputStream(Properties props, InputStream in, byte[] key,
+            byte[] iv) throws IOException {
+        this(props, in, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param in the ReadableByteChannel instance.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoInputStream(Properties props, ReadableByteChannel in,
+            byte[] key, byte[] iv) throws IOException {
+        this(props, in, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param in the input stream.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(InputStream in, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv) throws IOException {
+        this(in, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param in the ReadableByteChannel instance.
+     * @param cipher the cipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv) throws IOException {
+        this(in, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param input the input data.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(Input input, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv) throws IOException {
+        this(input, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param in the InputStream instance.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoInputStream(Properties props, InputStream in, byte[] key,
+            byte[] iv, long streamOffset) throws IOException {
+        this(in, Utils.getCipherInstance(
+                "AES/CTR/NoPadding", props),
+                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param in the ReadableByteChannel instance.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoInputStream(Properties props, ReadableByteChannel in,
+            byte[] key, byte[] iv, long streamOffset) throws IOException {
+        this(in, Utils.getCipherInstance(
+                "AES/CTR/NoPadding", props),
+                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param in the InputStream instance.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(InputStream in, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv, long streamOffset)
+            throws IOException {
+        this(new StreamInput(in, bufferSize), cipher, bufferSize, key, iv,
+                streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param in the ReadableByteChannel instance.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv, long streamOffset)
+            throws IOException {
+        this(new ChannelInput(in), cipher, bufferSize, key, iv, streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoInputStream}.
+     *
+     * @param input the input data.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoInputStream(Input input, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv, long streamOffset)
+            throws IOException {
+        super(input, cipher, bufferSize, new SecretKeySpec(key, "AES"),
+                new IvParameterSpec(iv));
+
+        this.initIV = iv.clone();
+        this.iv = iv.clone();
+
+        CryptoInputStream.checkStreamCipher(cipher);
+
+        resetStreamOffset(streamOffset);
+    }
+
+    /**
+     * Overrides the {@link CryptoInputStream#skip(long)}. Skips over and
+     * discards <code>n</code> bytes of data from this input stream.
+     *
+     * @param n the number of bytes to be skipped.
+     * @return the actual number of bytes skipped.
+     * @throws IOException if an I/O error occurs.
+     */
+    @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;
+        }
+    }
+
+    /**
+     * Overrides the {@link CtrCryptoInputStream#read(ByteBuffer)}. Reads a
+     * sequence of bytes from this channel into the given buffer.
+     *
+     * @param buf The buffer into which bytes are to be transferred.
+     * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the
+     *         channel has reached end-of-stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    @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;
+            }
+            // 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;
+        }
+        buf.put(outBuffer);
+        return unread;
+    }
+
+    /**
+     * Seeks the stream to a specific position relative to start of the under
+     * layer stream.
+     *
+     * @param position the given position in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    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);
+        }
+    }
+
+    /**
+     * Gets the offset of the stream.
+     *
+     * @return the stream offset.
+     */
+    protected long getStreamOffset() {
+        return streamOffset;
+    }
+
+    /**
+     * Sets the offset of stream.
+     *
+     * @param streamOffset the stream offset.
+     */
+    protected void setStreamOffset(long streamOffset) {
+        this.streamOffset = streamOffset;
+    }
+
+    /**
+     * Gets the position of the stream.
+     *
+     * @return the position of the stream.
+     */
+    protected long getStreamPosition() {
+        return streamOffset - outBuffer.remaining();
+    }
+
+    /**
+     * Decrypts 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.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    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();
+    }
+
+    /**
+     * Does 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().
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    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);
+        }
+    }
+
+    /**
+     * Does 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.
+     *
+     * @param buf The buffer into which bytes are to be transferred.
+     * @throws IOException if an I/O error occurs.
+     */
+    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();
+    }
+
+    /**
+     * Decrypts 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.
+     *
+     * @param buf The buffer into which bytes are to be transferred.
+     * @param offset the start offset in the data.
+     * @param len the the maximum number of decrypted data bytes to read.
+     * @throws IOException if an I/O error occurs.
+     */
+    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. Checks whether
+     * cipher should be updated and recalculate padding if needed.
+     *
+     * @param position the given position in the data..
+     * @return the byte.
+     * @throws IOException if an I/O error occurs.
+     */
+    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;
+    }
+
+    /**
+     * Gets the initialization vector.
+     *
+     * @return the initIV.
+     */
+    protected byte[] getInitIV() {
+        return initIV;
+    }
+
+    /**
+     * Gets the counter for input stream position.
+     *
+     * @param position the given position in the data.
+     * @return the counter for input stream position.
+     */
+    protected long getCounter(long position) {
+        return position / cipher.getBlockSize();
+    }
+
+    /**
+     * Gets the padding for input stream position.
+     *
+     * @param position the given position in the data.
+     * @return the padding for input stream position.
+     */
+    protected byte getPadding(long position) {
+        return (byte) (position % cipher.getBlockSize());
+    }
+
+    /**
+     * Overrides the {@link CtrCryptoInputStream#initCipher()}. Initializes the
+     * cipher.
+     */
+    @Override
+    protected void initCipher() {
+        // Do nothing for initCipher
+        // Will reset the cipher when reset the stream offset
+    }
+
+    /**
+     * Calculates the counter and iv, resets the cipher.
+     *
+     * @param position the given position in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected void resetCipher(long position) throws IOException {
+        final long counter = getCounter(position);
+        CtrCryptoInputStream.calculateIV(initIV, counter, iv);
+        try {
+            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+        } catch (InvalidKeyException e) {
+            throw new IOException(e);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new IOException(e);
+        }
+        cipherReset = false;
+    }
+
+    /**
+     * Resets the underlying stream offset; clear {@link #inBuffer} and
+     * {@link #outBuffer}. This Typically happens during {@link #skip(long)}.
+     *
+     * @param offset the offset of the stream.
+     * @throws IOException if an I/O error occurs.
+     */
+    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.
+    }
+
+    /**
+     * Does the decryption using out as output.
+     *
+     * @param out the output ByteBuffer.
+     * @throws IOException if an I/O error occurs.
+     */
+    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. CryptoCipher#update will
+                 * consume all input data and put result in outBuffer.
+                 * CryptoCipher#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);
+        }
+    }
+
+    /**
+     * <p>
+     * This method is only for Counter (CTR) mode. Generally the CryptoCipher
+     * calculates the IV and maintain encryption context internally.For example
+     * a Cipher will maintain its encryption context internally when we do
+     * encryption/decryption using the CryptoCipher#update interface.
+     * </p>
+     * <p>
+     * Encryption/Decryption is not always on the entire file. For example, in
+     * Hadoop, a node may only decrypt a portion of a file (i.e. a split). In
+     * these situations, the counter is derived from the file position.
+     * </p>
+     * The IV can be calculated by combining the initial IV and the counter with
+     * a lossless operation (concatenation, addition, or XOR).
+     *
+     * @see <a
+     *      href="http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29">
+     *      http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29</a>
+     *
+     * @param initIV initial IV
+     * @param counter counter for input stream position
+     * @param IV the IV for input stream position
+     */
+    static void calculateIV(byte[] initIV, long counter, byte[] IV) {
+        Utils.checkArgument(initIV.length == CtrCryptoInputStream.AES_BLOCK_SIZE);
+        Utils.checkArgument(IV.length == CtrCryptoInputStream.AES_BLOCK_SIZE);
+    
+        int i = IV.length; // IV length
+        int j = 0; // counter bytes index
+        int sum = 0;
+        while (i-- > 0) {
+            // (sum >>> Byte.SIZE) is the carry for addition
+            sum = (initIV[i] & 0xff) + (sum >>> Byte.SIZE); // NOPMD
+            if (j++ < 8) { // Big-endian, and long is 8 bytes length
+                sum += (byte) counter & 0xff;
+                counter >>>= 8;
+            }
+            IV[i] = (byte) sum;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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
new file mode 100644
index 0000000..c05b0c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CtrCryptoOutputStream.java
@@ -0,0 +1,384 @@
+/**
+ * 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.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.crypto.cipher.CryptoCipher;
+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;
+
+/**
+ * <p>
+ * 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>
+ * <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.
+     */
+    private long streamOffset = 0;
+
+    /**
+     * The initial IV.
+     */
+    private final byte[] initIV;
+
+    /**
+     * Initialization vector for the cipher.
+     */
+    private byte[] iv;
+
+    /**
+     * 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;
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param out the output stream.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoOutputStream(Properties props, OutputStream out,
+            byte[] key, byte[] iv) throws IOException {
+        this(props, out, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param out the WritableByteChannel instance.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoOutputStream(Properties props, WritableByteChannel out,
+            byte[] key, byte[] iv) throws IOException {
+        this(props, out, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param out the output stream.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(OutputStream out, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv) throws IOException {
+        this(out, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param channel the WritableByteChannel instance.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(WritableByteChannel channel,
+            CryptoCipher cipher, int bufferSize, byte[] key, byte[] iv)
+            throws IOException {
+        this(channel, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param output the Output instance.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(Output output, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv) throws IOException {
+        this(output, cipher, bufferSize, key, iv, 0);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param out the output stream.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoOutputStream(Properties props, OutputStream out,
+            byte[] key, byte[] iv, long streamOffset) throws IOException {
+        this(out, Utils.getCipherInstance(
+                "AES/CTR/NoPadding", props),
+                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param props The <code>Properties</code> class represents a set of
+     *        properties.
+     * @param out the WritableByteChannel instance.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    public CtrCryptoOutputStream(Properties props, WritableByteChannel out,
+            byte[] key, byte[] iv, long streamOffset) throws IOException {
+        this(out, Utils.getCipherInstance(
+                "AES/CTR/NoPadding", props),
+                CryptoInputStream.getBufferSize(props), key, iv, streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param out the output stream.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(OutputStream out, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv, long streamOffset)
+            throws IOException {
+        this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv,
+                streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param channel the WritableByteChannel instance.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(WritableByteChannel channel,
+            CryptoCipher cipher, int bufferSize, byte[] key, byte[] iv,
+            long streamOffset) throws IOException {
+        this(new ChannelOutput(channel), cipher, bufferSize, key, iv,
+                streamOffset);
+    }
+
+    /**
+     * Constructs a {@link CtrCryptoOutputStream}.
+     *
+     * @param output the output stream.
+     * @param cipher the CryptoCipher instance.
+     * @param bufferSize the bufferSize.
+     * @param key crypto key for the cipher.
+     * @param iv Initialization vector for the cipher.
+     * @param streamOffset the start offset in the data.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected CtrCryptoOutputStream(Output output, CryptoCipher cipher,
+            int bufferSize, byte[] key, byte[] iv, long streamOffset)
+            throws IOException {
+        super(output, cipher, bufferSize, new SecretKeySpec(key, "AES"),
+                new IvParameterSpec(iv));
+
+        CryptoInputStream.checkStreamCipher(cipher);
+        this.streamOffset = streamOffset;
+        this.initIV = iv.clone();
+        this.iv = iv.clone();
+
+        resetCipher();
+    }
+
+    /**
+     * Does the encryption, input is {@link #inBuffer} and output is
+     * {@link #outBuffer}.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @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();
+        }
+    }
+
+    /**
+     * Does final encryption of the last data.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void encryptFinal() throws IOException {
+        // The same as the normal encryption for Counter mode
+        encrypt();
+    }
+
+    /**
+     * Overrides the {@link CryptoOutputStream#initCipher()}. Initializes the
+     * cipher.
+     */
+    @Override
+    protected void initCipher() {
+        // Do nothing for initCipher
+        // Will reset the cipher considering the stream offset
+    }
+
+    /**
+     * Resets the {@link #cipher}: calculate counter and {@link #padding}.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    private void resetCipher() throws IOException {
+        final long counter = streamOffset
+                / cipher.getBlockSize();
+        padding = (byte) (streamOffset % cipher.getBlockSize());
+        inBuffer.position(padding); // Set proper position for input data.
+
+        CtrCryptoInputStream.calculateIV(initIV, counter, iv);
+        try {
+            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+        } catch (InvalidKeyException e) {
+            throw new IOException(e);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new IOException(e);
+        }
+        cipherReset = false;
+    }
+
+    /**
+     * Does the encryption if the ByteBuffer data.
+     *
+     * @param out the output ByteBuffer.
+     * @throws IOException if an I/O error occurs.
+     */
+    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. CryptoCipher#update will
+                 * consume all input data and put result in outBuffer.
+                 * CryptoCipher#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);
+        }
+    }
+
+    /**
+     * Get the underlying stream offset
+     *
+     * @return the underlying stream offset
+     */
+    protected long getStreamOffset() {
+        return streamOffset;
+    }
+
+    /**
+     * Set the underlying stream offset
+     *
+     * @param streamOffset the underlying stream offset
+     */
+    protected void setStreamOffset(long streamOffset) {
+        this.streamOffset = streamOffset;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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
index bd6c39d..b3d0509 100644
--- a/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
+++ b/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
@@ -42,7 +42,7 @@ import org.apache.commons.crypto.utils.Utils;
  * 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 {
+public class PositionedCryptoInputStream extends CtrCryptoInputStream {
 
     /**
      * DirectBuffer pool
@@ -296,7 +296,7 @@ public class PositionedCryptoInputStream extends CTRCryptoInputStream {
     private void resetCipher(CipherState state, long position, byte[] iv)
             throws IOException {
         final long counter = getCounter(position);
-        CTRCryptoInputStream.calculateIV(getInitIV(), counter, iv);
+        CtrCryptoInputStream.calculateIV(getInitIV(), counter, iv);
         try {
             state.getCipher().init(Cipher.DECRYPT_MODE, key,
                     new IvParameterSpec(iv));

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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 3ba55ec..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRCryptoStreamTest.java
+++ /dev/null
@@ -1,55 +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.CryptoCipher;
-
-public class CTRCryptoStreamTest extends AbstractCipherStreamTest {
-
-    @Override
-    public void setUp() throws IOException {
-        transformation = "AES/CTR/NoPadding";
-    }
-
-    @Override
-    protected CTRCryptoInputStream getCryptoInputStream(
-            ByteArrayInputStream bais, CryptoCipher cipher, int bufferSize,
-            byte[] iv, boolean withChannel) throws IOException {
-        if (withChannel) {
-            return new CTRCryptoInputStream(Channels.newChannel(bais), cipher,
-                    bufferSize, key, iv);
-        }
-        return new CTRCryptoInputStream(bais, cipher, bufferSize, key, iv);
-    }
-
-    @Override
-    protected CTRCryptoOutputStream getCryptoOutputStream(
-            ByteArrayOutputStream baos, CryptoCipher cipher, int bufferSize,
-            byte[] iv, boolean withChannel) throws IOException {
-        if (withChannel) {
-            return new CTRCryptoOutputStream(Channels.newChannel(baos), cipher,
-                    bufferSize, key, iv);
-        }
-        return new CTRCryptoOutputStream(baos, cipher, bufferSize, key, iv);
-    }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/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
deleted file mode 100644
index 76b8a7e..0000000
--- a/src/test/java/org/apache/commons/crypto/stream/CTRNoPaddingCipherStreamTest.java
+++ /dev/null
@@ -1,29 +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;
-
-public class CTRNoPaddingCipherStreamTest extends AbstractCipherStreamTest {
-
-    @Override
-    public void setUp() throws IOException {
-        transformation = "AES/CTR/NoPadding";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ba14d801/src/test/java/org/apache/commons/crypto/stream/CtrCryptoStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/CtrCryptoStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/CtrCryptoStreamTest.java
new file mode 100644
index 0000000..7b9c12b
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/stream/CtrCryptoStreamTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.CryptoCipher;
+
+public class CtrCryptoStreamTest extends AbstractCipherStreamTest {
+
+    @Override
+    public void setUp() throws IOException {
+        transformation = "AES/CTR/NoPadding";
+    }
+
+    @Override
+    protected CtrCryptoInputStream getCryptoInputStream(
+            ByteArrayInputStream bais, CryptoCipher cipher, int bufferSize,
+            byte[] iv, boolean withChannel) throws IOException {
+        if (withChannel) {
+            return new CtrCryptoInputStream(Channels.newChannel(bais), cipher,
+                    bufferSize, key, iv);
+        }
+        return new CtrCryptoInputStream(bais, cipher, bufferSize, key, iv);
+    }
+
+    @Override
+    protected CtrCryptoOutputStream getCryptoOutputStream(
+            ByteArrayOutputStream baos, CryptoCipher cipher, int bufferSize,
+            byte[] iv, boolean withChannel) throws IOException {
+        if (withChannel) {
+            return new CtrCryptoOutputStream(Channels.newChannel(baos), cipher,
+                    bufferSize, key, iv);
+        }
+        return new CtrCryptoOutputStream(baos, cipher, bufferSize, key, iv);
+    }
+}