commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s..@apache.org
Subject [4/5] commons-crypto git commit: CRYPTO-33: avoid shadowing JVM class renaming Cipher/Stream/Random classes
Date Fri, 29 Apr 2016 09:11:11 GMT
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/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
deleted file mode 100644
index b0cea74..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCipherInputStream.java
+++ /dev/null
@@ -1,625 +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 javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-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
-   */
-  long streamOffset = 0;
-
-  /**
-   * The initial IV.
-   */
-  protected final byte[] initIV;
-
-  /**
-   * Initialization vector for the cipher.
-   */
-  protected 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;
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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 CTRCipherInputStream(Properties props, InputStream in,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, in, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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 CTRCipherInputStream(Properties props, ReadableByteChannel in,
-      byte[] key, byte[] iv)
-      throws IOException {
-    this(props, in, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @param in the input stream.
-   * @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.
-   */
-  public CTRCipherInputStream(InputStream in, Cipher cipher, int bufferSize,
-      byte[] key, byte[] iv) throws IOException {
-    this(in, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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.
-   */
-  public CTRCipherInputStream(ReadableByteChannel in, Cipher cipher,
-      int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(in, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @param input the input data.
-   * @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.
-   */
-  public CTRCipherInputStream(
-      Input input,
-      Cipher cipher,
-      int bufferSize,
-      byte[] key,
-      byte[] iv) throws IOException {
-    this(input, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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 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);
-  }
-
-  /**
-   *Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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 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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @param in the InputStream instance.
-   * @param cipher the Cipher 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.
-   */
-  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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @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.
-   * @param streamOffset the start offset in the stream.
-   * @throws IOException if an I/O error occurs.
-   */
-  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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherInputStream}.
-   *
-   * @param input the input data.
-   * @param cipher the Cipher 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.
-   */
-  public CTRCipherInputStream(
-      Input input,
-      Cipher 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();
-
-    Utils.checkStreamCipher(cipher);
-
-    resetStreamOffset(streamOffset);
-  }
-
-  /**
-   * Overrides the {@link org.apache.commons.crypto.stream.CipherInputStream#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 org.apache.commons.crypto.stream.CTRCipherInputStream#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;
-      } 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;
-    }
-  }
-
-  /**
-   * 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;
-  }
-
-  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.
-   */
-  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.getTransformation().getAlgorithmBlockSize();
-  }
-
-  /**
-   * 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.getTransformation().getAlgorithmBlockSize());
-  }
-
-  /**
-   * Overrides the {@link CTRCipherInputStream#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);
-    Utils.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. 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/ea89d802/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
deleted file mode 100644
index 7889123..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CTRCipherOutputStream.java
+++ /dev/null
@@ -1,385 +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 javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-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.
-   */
-  long streamOffset = 0;
-
-  /**
-   * The initial IV.
-   */
-  protected final byte[] initIV;
-
-  /**
-   * Initialization vector for the cipher.
-   */
-  protected 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 org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @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 CTRCipherOutputStream(Properties props, OutputStream out, byte[] key,
-                               byte[] iv)
-      throws IOException {
-    this(props, out, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @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 CTRCipherOutputStream(Properties props, WritableByteChannel out,
-                               byte[] key, byte[] iv)
-      throws IOException {
-    this(props, out, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param out the output stream.
-   * @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.
-   */
-  public CTRCipherOutputStream(OutputStream out, Cipher cipher, int bufferSize,
-                               byte[] key, byte[] iv) throws IOException {
-    this(out, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param channel the WritableByteChannel 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.
-   */
-  public CTRCipherOutputStream(WritableByteChannel channel, Cipher cipher,
-                               int bufferSize, byte[] key, byte[] iv) throws IOException {
-    this(channel, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param output the Output 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.
-   */
-  public CTRCipherOutputStream(Output output, Cipher cipher, int bufferSize,
-                               byte[] key, byte[] iv)
-      throws IOException {
-    this(output, cipher, bufferSize, key, iv, 0);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @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 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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @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 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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param out the output stream.
-   * @param cipher the Cipher 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.
-   */
-  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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param channel the WritableByteChannel instance.
-   * @param cipher the Cipher 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.
-   */
-  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);
-  }
-
-  /**
-   * Constructs a {@link org.apache.commons.crypto.stream.CTRCipherOutputStream}.
-   *
-   * @param output the output stream.
-   * @param cipher the Cipher 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.
-   */
-  public CTRCipherOutputStream(Output output, Cipher cipher, int bufferSize,
-                               byte[] key, byte[] iv, long streamOffset)
-      throws IOException {
-    super(output, cipher, bufferSize, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
-
-    Utils.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 CipherOutputStream#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.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, 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. 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);
-    }
-  }
-
-  /**
-   * 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/ea89d802/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..dd9202d
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoInputStream.java
@@ -0,0 +1,625 @@
+/**
+ * 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 javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.crypto.cipher.CryptoCipher;
+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
+   */
+  long streamOffset = 0;
+
+  /**
+   * The initial IV.
+   */
+  protected final byte[] initIV;
+
+  /**
+   * Initialization vector for the cipher.
+   */
+  protected 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;
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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 org.apache.commons.crypto.stream.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 org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoInputStream(InputStream in, CryptoCipher cipher, int bufferSize,
+                              byte[] key, byte[] iv) throws IOException {
+    this(in, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoInputStream(ReadableByteChannel in, CryptoCipher cipher,
+                              int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(in, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoInputStream(
+      Input input,
+      CryptoCipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv) throws IOException {
+    this(input, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  /**
+   *Constructs a {@link org.apache.commons.crypto.stream.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(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public 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 org.apache.commons.crypto.stream.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.
+   */
+  public 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 org.apache.commons.crypto.stream.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.
+   */
+  public 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();
+
+    Utils.checkStreamCipher(cipher);
+
+    resetStreamOffset(streamOffset);
+  }
+
+  /**
+   * Overrides the {@link org.apache.commons.crypto.stream.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 org.apache.commons.crypto.stream.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;
+      } 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;
+    }
+  }
+
+  /**
+   * 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;
+  }
+
+  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.
+   */
+  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.getTransformation().getAlgorithmBlockSize();
+  }
+
+  /**
+   * 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.getTransformation().getAlgorithmBlockSize());
+  }
+
+  /**
+   * 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);
+    Utils.calculateIV(initIV, counter, iv);
+    try {
+      cipher.init(CryptoCipher.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);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/ea89d802/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..483f03c
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
@@ -0,0 +1,385 @@
+/**
+ * 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 javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.crypto.cipher.CryptoCipher;
+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.
+   */
+  long streamOffset = 0;
+
+  /**
+   * The initial IV.
+   */
+  protected final byte[] initIV;
+
+  /**
+   * Initialization vector for the cipher.
+   */
+  protected 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 org.apache.commons.crypto.stream.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 org.apache.commons.crypto.stream.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 org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoOutputStream(OutputStream out, CryptoCipher cipher, int bufferSize,
+                               byte[] key, byte[] iv) throws IOException {
+    this(out, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoOutputStream(WritableByteChannel channel, CryptoCipher cipher,
+                               int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(channel, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public CTRCryptoOutputStream(Output output, CryptoCipher cipher, int bufferSize,
+                               byte[] key, byte[] iv)
+      throws IOException {
+    this(output, cipher, bufferSize, key, iv, 0);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.stream.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.
+   */
+  public 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 org.apache.commons.crypto.stream.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.
+   */
+  public 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 org.apache.commons.crypto.stream.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.
+   */
+  public 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));
+
+    Utils.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.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(CryptoCipher.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/ea89d802/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
deleted file mode 100644
index 83df143..0000000
--- a/src/main/java/org/apache/commons/crypto/stream/CipherInputStream.java
+++ /dev/null
@@ -1,559 +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.Channel;
-import java.nio.channels.ReadableByteChannel;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.Properties;
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-
-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 CipherInputStream extends InputStream implements
-    ReadableByteChannel {
-  private final byte[] oneByteBuf = new byte[1];
-
-  /**The Cipher instance.*/
-  final Cipher cipher;
-
-  /**The buffer size.*/
-  final int bufferSize;
-
-  /**Crypto key for the cipher.*/
-  final Key key;
-
-  /** the algorithm parameters */
-  final AlgorithmParameterSpec params;
-
-  /** Flag to mark whether the input stream is closed.*/
-  private boolean closed;
-
-  /** Flag to mark whether do final of the cipher to end the decrypting stream.*/
-  private boolean finalDone = false;
-
-  /**The input data.*/
-  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;
-
-  /**
-   * Constructs a {@link CipherInputStream}.
-   *
-   * @param transformation the CipherTransformation instance.
-   * @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 params the algorithm parameters.
-   * @throws IOException if an I/O error occurs.
-   */
-  public CipherInputStream(CipherTransformation transformation,
-                           Properties props, InputStream in, Key key, AlgorithmParameterSpec params)
-      throws IOException {
-    this(in, Utils.getCipherInstance(transformation, props), Utils.getBufferSize(props), key,
-      params);
-  }
-
-  /**
-   * Constructs a {@link CipherInputStream}.
-   *
-   * @param transformation the CipherTransformation instance.
-   * @param props The <code>Properties</code> class represents a set of
-   *              properties.
-   * @param in the ReadableByteChannel object.
-   * @param key crypto key for the cipher.
-   * @param params the algorithm parameters.
-   * @throws IOException if an I/O error occurs.
-   */
-  public CipherInputStream(CipherTransformation transformation,
-                           Properties props, ReadableByteChannel in, Key key, AlgorithmParameterSpec params)
-      throws IOException {
-    this(in, Utils.getCipherInstance(transformation, props),
-        Utils.getBufferSize(props), key, params);
-  }
-
-  /**
-   * Constructs a {@link CipherInputStream}.
-   *
-   * @param cipher the cipher instance.
-   * @param in the input stream.
-   * @param bufferSize the bufferSize.
-   * @param key crypto key for the cipher.
-   * @param params the algorithm parameters.
-   * @throws IOException if an I/O error occurs.
-   */
-  public CipherInputStream(InputStream in, Cipher cipher, int bufferSize,
-                           Key key, AlgorithmParameterSpec params) throws IOException {
-    this(new StreamInput(in, bufferSize), cipher, bufferSize, key, params);
-  }
-
-  /**
-   * Constructs a {@link CipherInputStream}.
-   *
-   * @param in the ReadableByteChannel instance.
-   * @param cipher the cipher instance.
-   * @param bufferSize the bufferSize.
-   * @param key crypto key for the cipher.
-   * @param params the algorithm parameters.
-   * @throws IOException if an I/O error occurs.
-   */
-  public CipherInputStream(ReadableByteChannel in, Cipher cipher,
-                           int bufferSize, Key key, AlgorithmParameterSpec params) throws IOException {
-    this(new ChannelInput(in), cipher, bufferSize, key, params);
-  }
-
-  /**
-   * Constructs a {@link CipherInputStream}.
-   *
-   * @param input the input data.
-   * @param cipher the cipher instance.
-   * @param bufferSize the bufferSize.
-   * @param key crypto key for the cipher.
-   * @param params the algorithm parameters.
-   * @throws IOException if an I/O error occurs.
-   */
-  public CipherInputStream(Input input, Cipher cipher, int bufferSize,
-                           Key key, AlgorithmParameterSpec params) throws IOException {
-    this.input = input;
-    this.cipher = cipher;
-    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
-
-    this.key = key;
-    this.params = params;
-    if (!(params instanceof IvParameterSpec)) {
-      //other AlgorithmParameterSpec such as GCMParameterSpec is not supported now.
-      throw new IOException("Illegal parameters");
-    }
-
-    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
-    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
-        cipher.getTransformation().getAlgorithmBlockSize());
-    outBuffer.limit(0);
-
-    initCipher();
-  }
-
-  /**
-   * Overrides the {@link java.io.InputStream#read()}.
-   * Reads the next byte of data from the input stream.
-   *
-   * @return the next byte of data, or <code>-1</code> if the end of the
-   *         stream is reached.
-   * @throws IOException if an I/O error occurs.
-   */
-  @Override
-  public int read() throws IOException {
-    int n;
-    while ((n = read(oneByteBuf, 0, 1)) == 0) ;
-    return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
-  }
-
-  /**
-   * Overrides the {@link java.io.InputStream#read(byte[], int, int)}.
-   * 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 if an I/O error occurs.
-   */
-  @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;
-    }
-  }
-
-  /**
-   * Overrides the {@link java.io.InputStream#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;
-    }
-
-    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;
-  }
-
-  /**
-   * Overrides the {@link InputStream#available()}.
-   * Returns an estimate of the number of bytes that can be read (or
-   * skipped over) from this input stream without blocking by the next
-   * invocation of a method for this input stream.
-   *
-   * @return an estimate of the number of bytes that can be read (or skipped
-   *         over) from this input stream without blocking or {@code 0} when
-   *          it reaches the end of the input stream.
-   * @throws IOException if an I/O error occurs.
-   */
-  @Override
-  public int available() throws IOException {
-    checkStream();
-
-    return input.available() + outBuffer.remaining();
-  }
-
-  /**
-   * Overrides the {@link InputStream#close()}.
-   * Closes this input stream and releases any system resources associated
-   * with the stream.
-   *
-   * @throws IOException if an I/O error occurs.
-   */
-  @Override
-  public void close() throws IOException {
-    if (closed) {
-      return;
-    }
-
-    input.close();
-    freeBuffers();
-    cipher.close();
-    super.close();
-    closed = true;
-  }
-
-  /**
-   * Overrides the {@link java.io.InputStream#mark(int)}.
-   * For {@link CipherInputStream},we don't support the mark method.
-   *
-   * @param readlimit the maximum limit of bytes that can be read before
-   *                  the mark position becomes invalid.
-   */
-  @Override
-  public void mark(int readlimit) {
-  }
-
-  /**
-   * Overrides the {@link InputStream#reset()}.
-   * For {@link CipherInputStream},we don't support the reset method.
-   *
-   * @throws IOException if an I/O error occurs.
-   */
-  @Override
-  public void reset() throws IOException {
-    throw new IOException("Mark/reset not supported");
-  }
-
-  /**
-   * Overrides the {@link InputStream#markSupported()}.
-   *
-   * @return false,the {@link CTRCipherInputStream} don't support the mark method.
-   */
-  @Override
-  public boolean markSupported() {
-    return false;
-  }
-
-  /**
-   * Overrides the {@link Channel#isOpen()}.
-   *
-   * @return <tt>true</tt> if, and only if, this channel is open.
-   */
-  @Override
-  public boolean isOpen() {
-    return !closed;
-  }
-
-  /**
-   * Overrides the {@link java.nio.channels.ReadableByteChannel#read(ByteBuffer)}.
-   * Reads a sequence of bytes from this channel into the given buffer.
-   *
-   * @param dst 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 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;
-    }
-  }
-
-  /**
-   * Gets the buffer size.
-   *
-   * @return the bufferSize.
-   */
-  protected int getBufferSize() {
-    return bufferSize;
-  }
-
-  /**
-   * Gets the key.
-   *
-   * @return the key.
-   */
-  protected Key getKey() {
-    return key;
-  }
-
-
-  /**
-   * Gets the internal Cipher.
-   *
-   * @return the cipher instance.
-   */
-  protected Cipher getCipher() {
-    return cipher;
-  }
-
-  /**
-   * Gets the specification of cryptographic parameters.
-   *
-   * @return the params.
-   */
-  protected AlgorithmParameterSpec getParams() {
-    return params;
-  }
-
-  /**
-   * Gets the input.
-   *
-   * @return the input.
-   */
-  protected Input getInput() {
-    return input;
-  }
-
-  /**
-   * Initializes the cipher.
-   *
-   * @throws IOException if an I/O error occurs.
-   */
-  protected void initCipher()
-      throws IOException {
-    try {
-      cipher.init(Cipher.DECRYPT_MODE, key, params);
-    } catch (InvalidKeyException e) {
-      throw new IOException(e);
-    } catch(InvalidAlgorithmParameterException e) {
-      throw new IOException(e);
-    }
-  }
-
-  /**
-   * Decrypts 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();
-    }
-  }
-
-  /**
-   * 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.
-   */
-  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();
-  }
-
-  /**
-   * Does final of the cipher to end the decrypting stream.
-   *
-   *@throws IOException if an I/O error occurs.
-   */
-  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();
-  }
-
-  /**
-   * Checks whether the stream is closed.
-   *
-   * @throws IOException if an I/O error occurs.
-   */
-  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);
-  }
-}


Mime
View raw message