You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by xu...@apache.org on 2016/04/22 07:18:33 UTC

[5/8] commons-crypto git commit: CRYPTO-7: Rename source code in Chimera to Apache name space

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java b/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java
new file mode 100644
index 0000000..57a8cc7
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.cipher;
+
+import java.nio.ByteBuffer;
+
+/**
+ * JNI interface of {@link Openssl} implementation. The native method in this class is
+ * defined in OpensslNative.h(genereted by javah).
+ */
+public class OpensslNative {
+
+  /**
+   * Declares a native method to initialize JNI field and method IDs.
+   */
+  public native static void initIDs();
+
+  /**
+   * Declares a native method to initialize the cipher context.
+   *
+   * @param algorithm The algorithm name of cipher
+   * @param padding The padding name of cipher
+   * @return the context address of cipher
+   */
+  public native static long initContext(int algorithm, int padding);
+
+  /**
+   * Declares a native method to initialize the cipher context.
+   *
+   * @return the context address of cipher
+   */
+  public native static long init(long context, int mode, int alg, int padding,
+      byte[] key, byte[] iv);
+
+  /**
+   * Continues a multiple-part encryption/decryption operation. The data
+   * is encrypted or decrypted, depending on how this cipher was initialized.
+   *
+   * @param context The cipher context address
+   * @param input The input byte buffer
+   * @param inputOffset The offset in input where the input starts
+   * @param inputLength The input length
+   * @param output The byte buffer for the result
+   * @param outputOffset The offset in output where the result is stored
+   * @param maxOutputLength The maximum length for output
+   * @return The number of bytes stored in output
+   */
+  public native static int update(long context, ByteBuffer input,
+      int inputOffset, int inputLength, ByteBuffer output, int outputOffset,
+      int maxOutputLength);
+
+  /**
+   * Continues a multiple-part encryption/decryption operation. The data
+   * is encrypted or decrypted, depending on how this cipher was initialized.
+   *
+   * @param context The cipher context address
+   * @param input The input byte array
+   * @param inputOffset  The offset in input where the input starts
+   * @param inputLength The input length
+   * @param output The byte array for the result
+   * @param outputOffset The offset in output where the result is stored
+   * @param maxOutputLength The maximum length for output
+   * @return The number of bytes stored in output
+   */
+  public native static int updateByteArray(long context, byte[] input,
+      int inputOffset, int inputLength, byte[] output, int outputOffset,
+      int maxOutputLength);
+
+  /**
+   * Finishes a multiple-part operation. The data is encrypted or decrypted,
+   * depending on how this cipher was initialized.
+   *
+   * @param context The cipher context address
+   * @param output The byte buffer for the result
+   * @param offset The offset in output where the result is stored
+   * @param maxOutputLength The maximum length for output
+   * @return The number of bytes stored in output
+   */
+  public native static int doFinal(long context, ByteBuffer output, int offset,
+      int maxOutputLength);
+
+  /**
+   * Finishes a multiple-part operation. The data is encrypted or decrypted,
+   * depending on how this cipher was initialized.
+   *
+   * @param context The cipher context address
+   * @param output The byte array for the result
+   * @param offset The offset in output where the result is stored
+   * @param maxOutputLength The maximum length for output
+   * @return The number of bytes stored in output
+   */
+  public native static int doFinalByteArray(long context, byte[] output, int offset,
+      int maxOutputLength);
+
+  /**
+   * Cleans the context at native.
+   *
+   * @param context The cipher context address
+   */
+  public native static void clean(long context);
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/cipher/package-info.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/package-info.java b/src/main/java/org/apache/commons/crypto/cipher/package-info.java
new file mode 100644
index 0000000..c650b57
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/cipher/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+/**
+ * Cipher classes
+ */
+package org.apache.commons.crypto.cipher;

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java b/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java
new file mode 100644
index 0000000..d9c193f
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java
@@ -0,0 +1,139 @@
+/**
+ * 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.conf;
+
+import org.apache.commons.crypto.cipher.JceCipher;
+
+/**
+ * The ConfigurationKeys contains Configuration keys and default values.
+ */
+public class ConfigurationKeys {
+
+  /**
+   * The prefix named with project name.
+   */
+  public static final String CHIMERA_PREFIX = "chimera.";
+
+  /**
+   * The filename of configuration file.
+   */
+  public static final String CHIMERA_SYSTEM_PROPERTIES_FILE =
+      CHIMERA_PREFIX + "properties";
+
+  /**
+   * The prefix of crypto configuration.
+   */
+  public static final String CONF_PREFIX =
+      CHIMERA_PREFIX + "crypto.";
+
+  /**
+   * The configuration key of implementation class for crypto cipher.
+   * The values of CHIMERA_CRYPTO_CIPHER_CLASSES_KEY can be
+   * "org.apache.commons.crypto.cipher.JceCipher" and "org.apache.commons.crypto.cipher.OpensslCipher".
+   * And it takes a common separated list.
+   * The "org.apache.commons.crypto.cipher.JceCipher" use jce provider to
+   * implement {@link org.apache.commons.crypto.cipher.Cipher} and
+   * the "org.apache.commons.crypto.cipher.OpensslCipher" use jni into openssl to implement.
+   * Note that for each value,the first value which can be created without exception
+   * will be used (priority by order).
+    */
+  public static final String CHIMERA_CRYPTO_CIPHER_CLASSES_KEY =
+      CONF_PREFIX + "cipher.classes";
+
+  /**
+   * The default value for crypto cipher.
+   */
+  public static final String CHIMERA_CRYPTO_CIPHER_CLASSES_DEFAULT =
+      JceCipher.class.getName();
+
+  /**
+   * The configuration key of the provider class for JCE cipher.
+   */
+  public static final String CHIMERA_CRYPTO_CIPHER_JCE_PROVIDER_KEY =
+      CONF_PREFIX + "cipher.jce.provider";
+
+  // security random related configuration keys
+  /**
+   * The configuration key of the file path for secure random device.
+   */
+  public static final String CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_KEY =
+      CONF_PREFIX + "secure.random.device.file.path";
+
+  /**
+   * The default value of the file path for secure random device.
+   */
+  public static final String CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_DEFAULT =
+      "/dev/urandom";
+
+  /**
+   * The configuration key of the algorithm of secure random.
+   */
+  public static final String CHIMERA_CRYPTO_SECURE_RANDOM_JAVA_ALGORITHM_KEY =
+      CONF_PREFIX + "secure.random.java.algorithm";
+
+  /**
+   * The default value of the algorithm of secure random.
+   */
+  public static final String
+      CHIMERA_CRYPTO_SECURE_RANDOM_JAVA_ALGORITHM_DEFAULT = "SHA1PRNG";
+
+  /**
+   * The configuration key of the implementation class for secure random.
+   * The values of CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY can be
+   * "org.apache.commons.crypto.random.JavaSecureRandom" and "org.apache.commons.crypto.random.OpensslSecureRandom".
+   * And it takes a common separated list.
+   * The "org.apache.commons.crypto.random.JavaSecureRandom" use java to
+   * implement {@link org.apache.commons.crypto.random.SecureRandom} and
+   * the "org.apache.commons.crypto.random.OpensslSecureRandom" use jni into openssl to implement.
+   * Note that for each value,the first value which can be created without exception
+   * will be used (priority by order).
+   */
+  public static final String CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY =
+      CONF_PREFIX + "secure.random.classes";
+
+  /**
+   * The configuration key of the buffer size for stream.
+   */
+  public static final String CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_KEY =
+      CONF_PREFIX + "stream.buffer.size";
+
+  // stream related configuration keys
+  /**
+   * The default value of the buffer size for stream.
+   */
+  public static final int CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_DEFAULT = 8192;
+
+  // native lib related configuration keys
+  /**
+   * The configuration key of the path for loading crypto library.
+   */
+  public static final String CHIMERA_CRYPTO_LIB_PATH_KEY =
+      CONF_PREFIX + "lib.path";
+
+  /**
+   * The configuration key of the file name for loading crypto library.
+   */
+  public static final String CHIMERA_CRYPTO_LIB_NAME_KEY =
+      CONF_PREFIX + "lib.name";
+
+  /**
+   * The configuration key of temp directory for extracting crypto library.
+   */
+  public static final String CHIMERA_CRYPTO_LIB_TEMPDIR_KEY =
+      CONF_PREFIX + "lib.tempdir";
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/conf/package-info.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/conf/package-info.java b/src/main/java/org/apache/commons/crypto/conf/package-info.java
new file mode 100644
index 0000000..837b195
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/conf/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+/**
+ * Configuration classes
+ */
+package org.apache.commons.crypto.conf;

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/JavaSecureRandom.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/JavaSecureRandom.java b/src/main/java/org/apache/commons/crypto/random/JavaSecureRandom.java
new file mode 100644
index 0000000..6c079d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/JavaSecureRandom.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.crypto.conf.ConfigurationKeys;
+
+/**
+ * A SecureRandom of Java implementation.
+ */
+public class JavaSecureRandom implements SecureRandom {
+  private static final Log LOG =
+      LogFactory.getLog(JavaSecureRandom.class.getName());
+
+  private java.security.SecureRandom instance;
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.random.JavaSecureRandom}.
+   *
+   * @param properties the configuration properties.
+   */
+  public JavaSecureRandom(Properties properties) {
+    try {
+      instance = java.security.SecureRandom
+          .getInstance(properties.getProperty(
+              ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_JAVA_ALGORITHM_KEY,
+              ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_JAVA_ALGORITHM_DEFAULT));
+    } catch (NoSuchAlgorithmException e) {
+      LOG.error("Failed to create java secure random due to error: " + e);
+    }
+  }
+
+  /**
+   * Overrides {@link java.lang.AutoCloseable#close()}.
+   * For{@link JavaSecureRandom}, we don't need to recycle resource.
+   */
+  @Override
+  public void close() {
+    // do nothing
+  }
+
+  /**
+   * Overrides {@link org.apache.commons.crypto.random.SecureRandom#nextBytes(byte[])}.
+   * Generates random bytes and places them into a user-supplied byte array.
+   * The number of random bytes produced is equal to the length of the byte array.
+   *
+   * @param bytes the array to be filled in with random bytes.
+   */
+  @Override
+  public void nextBytes(byte[] bytes) {
+    instance.nextBytes(bytes);
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandom.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandom.java b/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandom.java
new file mode 100644
index 0000000..3794fde
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandom.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.crypto.utils.NativeCodeLoader;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * OpenSSL secure random using JNI.
+ * This implementation is thread-safe.
+ * <p/>
+ *
+ * If using an Intel chipset with RDRAND, the high-performance hardware
+ * random number generator will be used and it's much faster than
+ * {@link java.security.SecureRandom}. If RDRAND is unavailable, default
+ * OpenSSL secure random generator will be used. It's still faster
+ * and can generate strong random bytes.
+ * <p/>
+ * @see https://wiki.openssl.org/index.php/Random_Numbers
+ * @see http://en.wikipedia.org/wiki/RdRand
+ */
+public class OpensslSecureRandom extends Random implements SecureRandom {
+  private static final long serialVersionUID = -7828193502768789584L;
+  private static final Log LOG =
+      LogFactory.getLog(OpensslSecureRandom.class.getName());
+
+  /** If native SecureRandom unavailable, use java SecureRandom */
+  private JavaSecureRandom fallback = null;
+  private static boolean nativeEnabled = false;
+  static {
+    if (NativeCodeLoader.isNativeCodeLoaded()) {
+      try {
+        OpensslSecureRandomNative.initSR();
+        nativeEnabled = true;
+      } catch (Throwable t) {
+        LOG.error("Failed to load Openssl SecureRandom", t);
+      }
+    }
+  }
+
+  /**
+   * Judges whether loading native library successfully.
+   *
+   * @return true if loading library successfully.
+   */
+  public static boolean isNativeCodeLoaded() {
+    return nativeEnabled;
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.random.OpensslSecureRandom}.
+   *
+   * @param props the configuration properties.
+   */
+  public OpensslSecureRandom(Properties props) {
+    if (!nativeEnabled) {
+      fallback = new JavaSecureRandom(props);
+    }
+  }
+
+  /**
+   * Generates a user-specified number of random bytes.
+   * It's thread-safe.
+   *
+   * @param bytes the array to be filled in with random bytes.
+   */
+  @Override
+  public void nextBytes(byte[] bytes) {
+    if (!nativeEnabled || !OpensslSecureRandomNative.nextRandBytes(bytes)) {
+      fallback.nextBytes(bytes);
+    }
+  }
+
+  /**
+   * Overrides {@link OpensslSecureRandom}.
+   * For {@link OpensslSecureRandom}, we don't need to set seed.
+   *
+   * @param seed the initial seed.
+   */
+  @Override
+  public void setSeed(long seed) {
+    // Self-seeding.
+  }
+
+  /**
+   * Overrides {@link java.util.Random# next()}. Generates an integer
+   * containing the user-specified number of random
+   * bits(right justified, with leading zeros).
+   *
+   * @param numBits number of random bits to be generated, where
+   * 0 <= <code>numBits</code> <= 32.
+   * @return int an <code>int</code> containing the user-specified number
+   * of random bits (right justified, with leading zeros).
+   */
+  @Override
+  final protected int next(int numBits) {
+    Utils.checkArgument(numBits >= 0 && numBits <= 32);
+    int numBytes = (numBits + 7) / 8;
+    byte b[] = new byte[numBytes];
+    int next = 0;
+
+    nextBytes(b);
+    for (int i = 0; i < numBytes; i++) {
+      next = (next << 8) + (b[i] & 0xFF);
+    }
+
+    return next >>> (numBytes * 8 - numBits);
+  }
+
+  /**
+   * Overrides {@link java.lang.AutoCloseable#close()}. Closes openssl context if native enabled.
+   */
+  @Override
+  public void close() {
+    if (!nativeEnabled && fallback !=null) {
+      fallback.close();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandomNative.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandomNative.java b/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandomNative.java
new file mode 100644
index 0000000..d80cf89
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/OpensslSecureRandomNative.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+/**
+ * JNI interface of {@link SecureRandom} implementation.
+ * The native method in this class is defined in
+ * OpensslSecureRandomNative.h(genereted by javah).
+ */
+public class OpensslSecureRandomNative {
+  /**
+   * Declares a native method to initialize SR.
+   */
+  public native static void initSR();
+
+  /**
+   * Judges whether use {@link OpensslSecureRandomNative} to
+   * generate the user-specified number of random bits.
+   *
+   * @param bytes the array to be filled in with random bytes.
+   * @return true if use {@link OpensslSecureRandomNative} to
+   * generate the user-specified number of random bits.
+   */
+  public native static boolean nextRandBytes(byte[] bytes); 
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/OsSecureRandom.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/OsSecureRandom.java b/src/main/java/org/apache/commons/crypto/random/OsSecureRandom.java
new file mode 100644
index 0000000..916ad10
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/OsSecureRandom.java
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.commons.crypto.utils.IOUtils;
+import org.apache.commons.crypto.utils.Utils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A Random implementation that uses random bytes sourced from the
+ * operating system.
+ */
+public class OsSecureRandom extends Random implements SecureRandom {
+  public static final Log LOG = LogFactory.getLog(OsSecureRandom.class);
+  
+  private static final long serialVersionUID = 6391500337172057900L;
+
+  private final int RESERVOIR_LENGTH = 8192;
+
+  private String randomDevPath;
+
+  private transient FileInputStream stream;
+
+  private final byte[] reservoir = new byte[RESERVOIR_LENGTH];
+
+  private int pos = reservoir.length;
+
+  private void fillReservoir(int min) {
+    if (pos >= reservoir.length - min) {
+      try {
+        IOUtils.readFully(stream, reservoir, 0, reservoir.length);
+      } catch (IOException e) {
+        throw new RuntimeException("failed to fill reservoir", e);
+      }
+      pos = 0;
+    }
+  }
+
+  /**
+   * Constructs a {@link org.apache.commons.crypto.random.OsSecureRandom}.
+   *
+   * @param props the configuration properties.
+   */
+  public OsSecureRandom(Properties props) {
+    randomDevPath = Utils.getRandomDevPath(props);
+    File randomDevFile = new File(randomDevPath);
+
+    try {
+      close();
+      this.stream = new FileInputStream(randomDevFile);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+
+    try {
+      fillReservoir(0);
+    } catch (RuntimeException e) {
+      close();
+      throw e;
+    }
+  }
+
+  /**
+   * Overrides {@link org.apache.commons.crypto.random.SecureRandom#nextBytes(byte[])}.
+   * Generates random bytes and places them into a user-supplied byte array.
+   * The number of random bytes produced is equal to the length of the byte array.
+   *
+   * @param bytes the array to be filled in with random bytes.
+   */
+  @Override
+  synchronized public void nextBytes(byte[] bytes) {
+    int off = 0;
+    int n = 0;
+    while (off < bytes.length) {
+      fillReservoir(0);
+      n = Math.min(bytes.length - off, reservoir.length - pos);
+      System.arraycopy(reservoir, pos, bytes, off, n);
+      off += n;
+      pos += n;
+    }
+  }
+
+  /**
+   * Overrides {@link java.util.Random# next()}. Generates the next pseudorandom number.
+   * Subclasses should override this, as this is used by all other methods.
+   *
+   * @param  nbits random bits.
+   * @return the next pseudorandom value from this random number
+   *         generator's sequence.
+   */
+  @Override
+  synchronized protected int next(int nbits) {
+    fillReservoir(4);
+    int n = 0;
+    for (int i = 0; i < 4; i++) {
+      n = ((n << 8) | (reservoir[pos++] & 0xff));
+    }
+    return n & (0xffffffff >> (32 - nbits));
+  }
+
+  /**
+   * Overrides {@link java.lang.AutoCloseable#close()}. Closes the OS stream.
+   */
+  @Override
+  synchronized public void close() {
+    if (stream != null) {
+      IOUtils.cleanup(LOG, stream);
+      stream = null;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/SecureRandom.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/SecureRandom.java b/src/main/java/org/apache/commons/crypto/random/SecureRandom.java
new file mode 100644
index 0000000..69c5559
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/SecureRandom.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.io.Closeable;
+
+/**
+ * The interface for SecureRandom.
+ */
+public interface SecureRandom extends Closeable {
+
+  /**
+   * Generates random bytes and places them into a user-supplied
+   * byte array.  The number of random bytes produced is equal to
+   * the length of the byte array.
+   *
+   * @param bytes the byte array to fill with random bytes
+   */
+  void nextBytes(byte[] bytes);
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/SecureRandomFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/SecureRandomFactory.java b/src/main/java/org/apache/commons/crypto/random/SecureRandomFactory.java
new file mode 100644
index 0000000..104e5d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/SecureRandomFactory.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.random;
+
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.commons.crypto.utils.Utils;
+import org.apache.commons.crypto.utils.ReflectionUtils;
+
+import static org.apache.commons.crypto.conf.ConfigurationKeys
+    .CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY;
+
+/**
+ * This is the factory class used for {@link SecureRandom}.
+ */
+public class SecureRandomFactory {
+  public final static Logger LOG = LoggerFactory
+      .getLogger(SecureRandomFactory.class);
+
+  /**
+   * Gets a SecureRandom instance for specified props.
+   *
+   * @param props the configuration properties.
+   * @return SecureRandom the secureRandom object.Null value will be returned if no
+   *         SecureRandom classes with props.
+   */
+  public static SecureRandom getSecureRandom(Properties props) {
+    String secureRandomClasses = props.getProperty(
+        CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY);
+    if (secureRandomClasses == null) {
+      secureRandomClasses = System.getProperty(
+          CHIMERA_CRYPTO_SECURE_RANDOM_CLASSES_KEY);
+    }
+
+    SecureRandom random = null;
+    if (secureRandomClasses != null) {
+      for (String klassName : Utils.splitClassNames(secureRandomClasses, ",")) {
+        try {
+          final Class<?> klass = ReflectionUtils.getClassByName(klassName);
+          random = (SecureRandom) ReflectionUtils.newInstance(klass, props);
+          if (random != null) {
+            break;
+          }
+        } catch (ClassCastException e) {
+          LOG.error("Class {} is not a Cipher.", klassName);
+        } catch (ClassNotFoundException e) {
+          LOG.error("Cipher {} not found.", klassName);
+        }
+      }
+    }
+
+    return (random == null) ? new JavaSecureRandom(props) : random;
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/random/package-info.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/random/package-info.java b/src/main/java/org/apache/commons/crypto/random/package-info.java
new file mode 100644
index 0000000..f8fd4de
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/random/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+/**
+ * Random classes
+ */
+package org.apache.commons.crypto.random;

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

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/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..cb282e3
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CTRCryptoOutputStream.java
@@ -0,0 +1,230 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.output.ChannelOutput;
+import org.apache.commons.crypto.stream.output.Output;
+import org.apache.commons.crypto.stream.output.StreamOutput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CTRCryptoOutputStream encrypts data. It is not thread-safe. AES CTR mode is
+ * required in order to ensure that the plain text and cipher text have a 1:1
+ * mapping. The encryption is buffer based. The key points of the encryption are
+ * (1) calculating counter and (2) padding through stream position.
+ * <p/>
+ * counter = base + pos/(algorithm blocksize);
+ * padding = pos%(algorithm blocksize);
+ * <p/>
+ * The underlying stream offset is maintained as state.
+ */
+public class CTRCryptoOutputStream extends CryptoOutputStream {
+  /**
+   * Underlying stream offset.
+   */
+  protected long streamOffset = 0;
+
+  /**
+   * Padding = pos%(algorithm blocksize); Padding is put into {@link #inBuffer}
+   * before any other data goes in. The purpose of padding is to put input data
+   * at proper position.
+   */
+  private byte padding;
+
+  /**
+   * Flag to mark whether the cipher has been reset
+   */
+  private boolean cipherReset = false;
+
+  public CTRCryptoOutputStream(Properties props, OutputStream out,
+      byte[] key, byte[] iv)
+      throws IOException {
+    this(props, out, key, iv, 0);
+  }
+
+  public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
+      byte[] key, byte[] iv)
+      throws IOException {
+    this(props, out, key, iv, 0);
+  }
+
+  public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(out, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(channel, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCryptoOutputStream(Output output, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv)
+      throws IOException {
+    this(output, cipher, bufferSize, key, iv, 0);
+  }
+
+  public CTRCryptoOutputStream(Properties props, OutputStream out,
+      byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCryptoOutputStream(Properties props, WritableByteChannel out,
+      byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    this(out, Utils.getCipherInstance(CipherTransformation.AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public CTRCryptoOutputStream(OutputStream out, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new StreamOutput(out, bufferSize), cipher,
+        bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCryptoOutputStream(WritableByteChannel channel, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(new ChannelOutput(channel), cipher,
+        bufferSize, key, iv, streamOffset);
+  }
+
+  public CTRCryptoOutputStream(Output output, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv, long streamOffset)
+      throws IOException {
+    super(output, cipher, bufferSize, key, iv);
+
+    Utils.checkStreamCipher(cipher);
+    this.streamOffset = streamOffset;
+
+    resetCipher();
+  }
+
+  /**
+   * Do the encryption, input is {@link #inBuffer} and output is
+   * {@link #outBuffer}.
+   */
+  @Override
+  protected void encrypt() throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    if (inBuffer.position() == padding) {
+      // There is no real data in the inBuffer.
+      return;
+    }
+
+    inBuffer.flip();
+    outBuffer.clear();
+    encryptBuffer(outBuffer);
+    inBuffer.clear();
+    outBuffer.flip();
+
+    if (padding > 0) {
+      /*
+       * The plain text and cipher text have a 1:1 mapping, they start at the
+       * same position.
+       */
+      outBuffer.position(padding);
+      padding = 0;
+    }
+
+    final int len = output.write(outBuffer);
+    streamOffset += len;
+    if (cipherReset) {
+      /*
+       * This code is generally not executed since the encryptor usually
+       * maintains encryption context (e.g. the counter) internally. However,
+       * some implementations can't maintain context so a re-init is necessary
+       * after each encryption call.
+       */
+      resetCipher();
+    }
+  }
+
+  /**
+   * Do final encryption of the last data
+   */
+  @Override
+  protected void encryptFinal() throws IOException {
+    // The same as the normal encryption for Counter mode
+    encrypt();
+  }
+
+  /** Initialize the cipher. */
+  @Override
+  protected void initCipher() {
+    // Do nothing for initCipher
+    // Will reset the cipher considering the stream offset
+  }
+
+  /** Reset the {@link #cipher}: calculate counter and {@link #padding}. */
+  private void resetCipher() throws IOException {
+    final long counter =
+        streamOffset / cipher.getTransformation().getAlgorithmBlockSize();
+    padding =
+        (byte)(streamOffset % cipher.getTransformation().getAlgorithmBlockSize());
+    inBuffer.position(padding); // Set proper position for input data.
+
+    Utils.calculateIV(initIV, counter, iv);
+    try {
+      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    }catch (InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+    cipherReset = false;
+  }
+
+  private void encryptBuffer(ByteBuffer out)
+      throws IOException {
+    int inputSize = inBuffer.remaining();
+    try {
+      int n = cipher.update(inBuffer, out);
+      if (n < inputSize) {
+        /**
+         * Typically code will not get here. Cipher#update will consume all
+         * input data and put result in outBuffer.
+         * Cipher#doFinal will reset the cipher context.
+         */
+        cipher.doFinal(inBuffer, out);
+        cipherReset = true;
+      }
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (BadPaddingException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    }
+  }
+}

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

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java b/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
new file mode 100644
index 0000000..3d3ac8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/CryptoOutputStream.java
@@ -0,0 +1,283 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherTransformation;
+import org.apache.commons.crypto.stream.output.ChannelOutput;
+import org.apache.commons.crypto.stream.output.Output;
+import org.apache.commons.crypto.stream.output.StreamOutput;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * CryptoOutputStream encrypts data and writes to the under layer output. It supports
+ * any mode of operations such as AES CBC/CTR/GCM mode in concept. It is not thread-safe.
+ */
+
+public class CryptoOutputStream extends OutputStream implements
+    WritableByteChannel {
+  private final byte[] oneByteBuf = new byte[1];
+
+  protected Output output;
+  protected final Cipher cipher;
+  protected final int bufferSize;
+
+  protected final byte[] key;
+  protected final byte[] initIV;
+  protected byte[] iv;
+
+  protected boolean closed;
+
+  /**
+   * Input data buffer. The data starts at inBuffer.position() and ends at
+   * inBuffer.limit().
+   */
+  protected ByteBuffer inBuffer;
+
+  /**
+   * Encrypted data buffer. The data starts at outBuffer.position() and ends at
+   * outBuffer.limit();
+   */
+  protected ByteBuffer outBuffer;
+
+  public CryptoOutputStream(CipherTransformation transformation,
+      Properties props, OutputStream out, byte[] key, byte[] iv)
+      throws IOException {
+    this(out, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CryptoOutputStream(CipherTransformation transformation,
+      Properties props, WritableByteChannel out, byte[] key, byte[] iv)
+      throws IOException {
+    this(out, Utils.getCipherInstance(transformation, props),
+        Utils.getBufferSize(props), key, iv);
+  }
+
+  public CryptoOutputStream(OutputStream out, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(new StreamOutput(out, bufferSize), cipher, bufferSize, key, iv);
+  }
+
+  public CryptoOutputStream(WritableByteChannel channel, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv) throws IOException {
+    this(new ChannelOutput(channel), cipher, bufferSize, key, iv);
+  }
+
+  protected CryptoOutputStream(Output output, Cipher cipher,
+      int bufferSize, byte[] key, byte[] iv)
+      throws IOException {
+
+    this.output = output;
+    this.bufferSize = Utils.checkBufferSize(cipher, bufferSize);
+    this.cipher = cipher;
+    this.key = key.clone();
+    this.initIV = iv.clone();
+    this.iv = iv.clone();
+    inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
+    outBuffer = ByteBuffer.allocateDirect(this.bufferSize +
+        cipher.getTransformation().getAlgorithmBlockSize());
+
+    initCipher();
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    oneByteBuf[0] = (byte)(b & 0xff);
+    write(oneByteBuf, 0, oneByteBuf.length);
+  }
+
+  /**
+   * Encryption is buffer based.
+   * If there is enough room in {@link #inBuffer}, then write to this buffer.
+   * If {@link #inBuffer} is full, then do encryption and write data to the
+   * underlying stream.
+   * @param b the data.
+   * @param off the start offset in the data.
+   * @param len the number of bytes to write.
+   * @throws IOException
+   */
+  public void write(byte[] b, int off, int len) throws IOException {
+    checkStream();
+    if (b == null) {
+      throw new NullPointerException();
+    } else if (off < 0 || len < 0 || off > b.length ||
+        len > b.length - off) {
+      throw new IndexOutOfBoundsException();
+    }
+
+    while (len > 0) {
+      final int remaining = inBuffer.remaining();
+      if (len < remaining) {
+        inBuffer.put(b, off, len);
+        len = 0;
+      } else {
+        inBuffer.put(b, off, remaining);
+        off += remaining;
+        len -= remaining;
+        encrypt();
+      }
+    }
+  }
+
+  /**
+   * To flush, we need to encrypt the data in the buffer and write to the
+   * underlying stream, then do the flush.
+   */
+  @Override
+  public void flush() throws IOException {
+    checkStream();
+    encrypt();
+    output.flush();
+    super.flush();
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (closed) {
+      return;
+    }
+
+    try {
+      encryptFinal();
+      output.close();
+      freeBuffers();
+      cipher.close();
+      super.close();
+    } finally {
+      closed = true;
+    }
+  }
+
+  @Override
+  public boolean isOpen() {
+    return !closed;
+  }
+
+  @Override
+  public int write(ByteBuffer src) throws IOException {
+    checkStream();
+    final int len = src.remaining();
+    int remaining = len;
+    while (remaining > 0) {
+      final int space = inBuffer.remaining();
+      if (remaining < space) {
+        inBuffer.put(src);
+        remaining = 0;
+      } else {
+        // to void copy twice, we set the limit to copy directly
+        final int oldLimit = src.limit();
+        final int newLimit = src.position() + space;
+        src.limit(newLimit);
+
+        inBuffer.put(src);
+
+        // restore the old limit
+        src.limit(oldLimit);
+
+        remaining -= space;
+        encrypt();
+      }
+    }
+
+    return len;
+  }
+
+  /** Initialize the cipher. */
+  protected void initCipher()
+      throws IOException {
+    try {
+      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch(InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * Do the encryption, input is {@link #inBuffer} and output is
+   * {@link #outBuffer}.
+   */
+  protected void encrypt() throws IOException {
+
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.update(inBuffer, outBuffer);
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    }
+
+    inBuffer.clear();
+    outBuffer.flip();
+
+    // write to output
+    output.write(outBuffer);
+  }
+
+  /**
+   * Do final encryption of the last data
+   */
+  protected void encryptFinal() throws IOException {
+    inBuffer.flip();
+    outBuffer.clear();
+
+    try {
+      cipher.doFinal(inBuffer, outBuffer);
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch( BadPaddingException e) {
+      throw new IOException(e);
+    }
+
+    inBuffer.clear();
+    outBuffer.flip();
+
+    // write to output
+    output.write(outBuffer);
+  }
+
+  protected void checkStream() throws IOException {
+    if (closed) {
+      throw new IOException("Stream closed");
+    }
+  }
+
+  /** Forcibly free the direct buffers. */
+  protected void freeBuffers() {
+    Utils.freeDirectBuffer(inBuffer);
+    Utils.freeDirectBuffer(outBuffer);
+  }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/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
new file mode 100644
index 0000000..160f6ee
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/stream/PositionedCryptoInputStream.java
@@ -0,0 +1,311 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.crypto.stream;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.util.Properties;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.cipher.Cipher;
+import org.apache.commons.crypto.cipher.CipherFactory;
+import org.apache.commons.crypto.stream.input.Input;
+import org.apache.commons.crypto.utils.IOUtils;
+import org.apache.commons.crypto.utils.Utils;
+
+import static org.apache.commons.crypto.cipher.CipherTransformation.AES_CTR_NOPADDING;
+
+/**
+ * PositionedCryptoInputStream provides the capability to decrypt the stream starting
+ * at random position as well as provides the foundation for positioned read for
+ * decrypting. This needs a stream cipher mode such as AES CTR mode.
+ */
+public class PositionedCryptoInputStream extends CTRCryptoInputStream {
+
+  /**
+   * DirectBuffer pool
+   */
+  private final Queue<ByteBuffer> bufferPool = new
+      ConcurrentLinkedQueue<ByteBuffer>();
+
+  /**
+   * Cipher pool
+   */
+  private final Queue<CipherState> cipherPool = new
+      ConcurrentLinkedQueue<CipherState>();
+
+  public PositionedCryptoInputStream(Properties props, Input in,
+      byte[] key, byte[] iv, long streamOffset) throws IOException {
+    this(in, Utils.getCipherInstance(AES_CTR_NOPADDING, props),
+        Utils.getBufferSize(props), key, iv, streamOffset);
+  }
+
+  public PositionedCryptoInputStream(
+      Input input,
+      Cipher cipher,
+      int bufferSize,
+      byte[] key,
+      byte[] iv,
+      long streamOffset) throws IOException {
+    super(input, cipher, bufferSize, key, iv, streamOffset);
+  }
+
+  /**
+   * Read upto the specified number of bytes from a given position
+   * within a stream and return the number of bytes read. This does not
+   * change the current offset of the stream, and is thread-safe.
+   */
+  public int read(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    checkStream();
+    final int n = input.read(position, buffer, offset, length);
+    if (n > 0) {
+      // This operation does not change the current offset of the file
+      decrypt(position, buffer, offset, n);
+    }
+    return n;
+  }
+
+  /**
+   * Read the specified number of bytes from a given position within a stream.
+   * This does not change the current offset of the stream and is thread-safe.
+   */
+  public void readFully(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    checkStream();
+    IOUtils.readFully(input, position, buffer, offset, length);
+    if (length > 0) {
+      // This operation does not change the current offset of the file
+      decrypt(position, buffer, offset, length);
+    }
+  }
+
+  public void readFully(long position, byte[] buffer) throws IOException {
+    readFully(position, buffer, 0, buffer.length);
+  }
+
+  /**
+   * Decrypt length bytes in buffer starting at offset. Output is also put
+   * into buffer starting at offset. It is thread-safe.
+   */
+  protected void decrypt(long position, byte[] buffer, int offset, int length)
+      throws IOException {
+    ByteBuffer inBuffer = getBuffer();
+    ByteBuffer outBuffer = getBuffer();
+    CipherState state = null;
+    try {
+      state = getCipherState();
+      byte[] iv = getInitIV().clone();
+      resetCipher(state, position, iv);
+      byte padding = getPadding(position);
+      inBuffer.position(padding); // Set proper position for input data.
+
+      int n = 0;
+      while (n < length) {
+        int toDecrypt = Math.min(length - n, inBuffer.remaining());
+        inBuffer.put(buffer, offset + n, toDecrypt);
+
+        // Do decryption
+        decrypt(state, inBuffer, outBuffer, padding);
+
+        outBuffer.get(buffer, offset + n, toDecrypt);
+        n += toDecrypt;
+        padding = postDecryption(state, inBuffer, position + n, iv);
+      }
+    } finally {
+      returnBuffer(inBuffer);
+      returnBuffer(outBuffer);
+      returnCipherState(state);
+    }
+  }
+
+  /**
+   * Do the decryption using inBuffer as input and outBuffer as output.
+   * Upon return, inBuffer is cleared; the decrypted data starts at
+   * outBuffer.position() and ends at outBuffer.limit();
+   */
+  private void decrypt(CipherState state, ByteBuffer inBuffer,
+      ByteBuffer outBuffer, byte padding) throws IOException {
+    Utils.checkState(inBuffer.position() >= padding);
+    if(inBuffer.position() == padding) {
+      // There is no real data in inBuffer.
+      return;
+    }
+    inBuffer.flip();
+    outBuffer.clear();
+    decryptBuffer(state, inBuffer, outBuffer);
+    inBuffer.clear();
+    outBuffer.flip();
+    if (padding > 0) {
+      /*
+       * The plain text and cipher text have a 1:1 mapping, they start at the
+       * same position.
+       */
+      outBuffer.position(padding);
+    }
+  }
+
+  private void decryptBuffer(CipherState state, ByteBuffer inBuffer, ByteBuffer outBuffer)
+      throws IOException {
+    int inputSize = inBuffer.remaining();
+    try {
+      int n = state.getCipher().update(inBuffer, outBuffer);
+      if (n < inputSize) {
+        /**
+         * Typically code will not get here. Cipher#update will consume all
+         * input data and put result in outBuffer.
+         * Cipher#doFinal will reset the cipher context.
+         */
+        state.getCipher().doFinal(inBuffer, outBuffer);
+        state.reset(true);
+      }
+    } catch (ShortBufferException e) {
+      throw new IOException(e);
+    } catch (IllegalBlockSizeException e) {
+      throw new IOException(e);
+    } catch (BadPaddingException e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * This method is executed immediately after decryption. Check whether
+   * cipher should be updated and recalculate padding if needed.
+   */
+  private byte postDecryption(CipherState state, ByteBuffer inBuffer,
+      long position, byte[] iv) throws IOException {
+    byte padding = 0;
+    if (state.isReset()) {
+      /*
+       * This code is generally not executed since the cipher usually
+       * maintains cipher context (e.g. the counter) internally. However,
+       * some implementations can't maintain context so a re-init is necessary
+       * after each decryption call.
+       */
+      resetCipher(state, position, iv);
+      padding = getPadding(position);
+      inBuffer.position(padding);
+    }
+    return padding;
+  }
+
+  /** Calculate the counter and iv, reset the cipher. */
+  private void resetCipher(CipherState state, long position, byte[] iv)
+      throws IOException {
+    final long counter = getCounter(position);
+    Utils.calculateIV(getInitIV(), counter, iv);
+    try {
+      state.getCipher().init(Cipher.DECRYPT_MODE, getKey(), iv);
+    } catch (InvalidKeyException e) {
+      throw new IOException(e);
+    } catch (InvalidAlgorithmParameterException e) {
+      throw new IOException(e);
+    }
+    state.reset(false);
+  }
+
+  /** Get Cipher from pool */
+  private CipherState getCipherState() throws IOException {
+    CipherState state = cipherPool.poll();
+    if (state == null) {
+      Cipher cipher;
+      try {
+        cipher = CipherFactory.getInstance(getCipher().getTransformation(),
+            getCipher().getProperties());
+      } catch (GeneralSecurityException e) {
+        throw new IOException(e);
+      }
+      state = new CipherState(cipher);
+    }
+
+    return state;
+  }
+
+  /** Return Cipher to pool */
+  private void returnCipherState(CipherState state) {
+    if (state != null) {
+      cipherPool.add(state);
+    }
+  }
+
+  /** Get direct buffer from pool */
+  private ByteBuffer getBuffer() {
+    ByteBuffer buffer = bufferPool.poll();
+    if (buffer == null) {
+      buffer = ByteBuffer.allocateDirect(getBufferSize());
+    }
+
+    return buffer;
+  }
+
+  /** Return direct buffer to pool */
+  private void returnBuffer(ByteBuffer buf) {
+    if (buf != null) {
+      buf.clear();
+      bufferPool.add(buf);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (!isOpen()) {
+      return;
+    }
+
+    cleanBufferPool();
+    super.close();
+  }
+
+  /** Clean direct buffer pool */
+  private void cleanBufferPool() {
+    ByteBuffer buf;
+    while ((buf = bufferPool.poll()) != null) {
+      Utils.freeDirectBuffer(buf);
+    }
+  }
+
+  private class CipherState {
+    private Cipher cipher;
+    private boolean reset;
+
+    public CipherState(Cipher cipher) {
+      this.cipher = cipher;
+      this.reset = false;
+    }
+
+    public Cipher getCipher() {
+      return cipher;
+    }
+
+    public boolean isReset() {
+      return reset;
+    }
+
+    public void reset(boolean reset) {
+      this.reset = reset;
+    }
+  }
+}