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

[2/2] commons-crypto git commit: [CRYPTO-94] Consistently camel-case type names: OpenSsl* (except native, that's next.)

[CRYPTO-94] Consistently camel-case type names: OpenSsl* (except native,
that's next.)

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

Branch: refs/heads/master
Commit: 859bdaa0489fe8e4e1e224b38975e73afceda506
Parents: 4f356a7
Author: Gary Gregory <gg...@apache.org>
Authored: Thu Jun 30 09:17:43 2016 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Thu Jun 30 09:17:43 2016 -0700

----------------------------------------------------------------------
 README.md                                       |   2 +-
 pom.xml                                         |   2 +-
 .../crypto/cipher/CryptoCipherFactory.java      |   8 +-
 .../apache/commons/crypto/cipher/OpenSsl.java   | 366 +++++++++++++++++++
 .../commons/crypto/cipher/OpenSslCipher.java    | 222 +++++++++++
 .../apache/commons/crypto/cipher/Openssl.java   | 366 -------------------
 .../commons/crypto/cipher/OpensslCipher.java    | 222 -----------
 .../commons/crypto/cipher/OpensslNative.java    |   6 +-
 .../commons/crypto/conf/ConfigurationKeys.java  |   8 +-
 .../commons/crypto/jna/OpenSslJnaCipher.java    | 331 +++++++++++++++++
 .../crypto/jna/OpenSslJnaCryptoRandom.java      | 190 ++++++++++
 .../apache/commons/crypto/jna/OpensslJna.java   |   4 +-
 .../commons/crypto/jna/OpensslJnaCipher.java    | 331 -----------------
 .../crypto/jna/OpensslJnaCryptoRandom.java      | 190 ----------
 .../crypto/random/CryptoRandomFactory.java      |   6 +-
 .../crypto/random/OpenSslCryptoRandom.java      | 147 ++++++++
 .../crypto/random/OpensslCryptoRandom.java      | 147 --------
 .../apache/commons/crypto/CryptoBenchmark.java  |   8 +-
 .../crypto/cipher/AbstractCipherTest.java       |   2 +-
 .../crypto/cipher/CryptoCipherFactoryTest.java  |   4 +-
 .../crypto/cipher/OpensslCipherTest.java        |  36 +-
 .../crypto/jna/OpensslJnaCipherTest.java        |   4 +-
 .../crypto/jna/OpensslJnaCryptoRandomTest.java  |   8 +-
 .../crypto/random/CryptoRandomFactoryTest.java  |   2 +-
 .../crypto/random/OpensslCryptoRandomTest.java  |   6 +-
 25 files changed, 1309 insertions(+), 1309 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 5ab073a..c1bf802 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ Features
 1. Cipher API for low level cryptographic operations.
 2. Java stream API (CryptoInputStream/CryptoOutputStream) for high level stream encyrption/decryption.
 3. Both optimized with high performance AES encryption/decryption. (1400 MB/s - 1700 MB/s throughput in modern Xeon processors).
-4. JNI-based implementation to achieve comparable performance to the native C++ version based on Openssl.
+4. JNI-based implementation to achieve comparable performance to the native C++ version based on OpenSsl.
 5. Portable across various operating systems (currently only Linux);
    Apache Commons Crypto loads the library according to your machine environment (it checks system properties, `os.name` and `os.arch`).
 6. Simple usage. Add the commons-crypto-(version).jar file to your classpath.

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0d3892d..e5dbcdf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@ Features
 1. Cipher API for low level cryptographic operations.
 2. Java stream API (CryptoInputStream/CryptoOutputStream) for high level stream encyrption/decryption.
 3. Both optimized with high performance AES encryption/decryption. (1400 MB/s - 1700 MB/s throughput in modern Xeon processors).
-4. JNI-based implementation to achieve comparable performance to the native C++ version based on Openssl.
+4. JNI-based implementation to achieve comparable performance to the native C++ version based on OpenSsl.
 5. Portable across various operating systems (currently only Linux);
    Apache Commons Crypto loads the library according to your machine environment (it checks system properties, `os.name` and `os.arch`).
 6. Simple usage. Add the commons-crypto-(version).jar file to your classpath.

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/cipher/CryptoCipherFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/CryptoCipherFactory.java b/src/main/java/org/apache/commons/crypto/cipher/CryptoCipherFactory.java
index c05be6d..12aa797 100644
--- a/src/main/java/org/apache/commons/crypto/cipher/CryptoCipherFactory.java
+++ b/src/main/java/org/apache/commons/crypto/cipher/CryptoCipherFactory.java
@@ -47,7 +47,7 @@ public class CryptoCipherFactory {
          * <p>
          * This implementation does not use any properties
          */
-        OPENSSL(OpensslCipher.class),
+        OPENSSL(OpenSslCipher.class),
         
         /**
          * The JCE cipher implementation from the JVM
@@ -91,7 +91,7 @@ public class CryptoCipherFactory {
     }
 
     /**
-     * The default value (OpensslCipher,JceCipher) for crypto cipher.
+     * The default value (OpenSslCipher,JceCipher) for crypto cipher.
      */
     private static final String CIPHER_CLASSES_DEFAULT = 
             CipherProvider.OPENSSL.getClassName()
@@ -109,7 +109,7 @@ public class CryptoCipherFactory {
      *
      * @param props  the configuration properties (uses ConfigurationKeys.CIPHER_CLASSES_KEY)
      * @param transformation  algorithm/mode/padding
-     * @return CryptoCipher  the cipher  (defaults to OpensslCipher)
+     * @return CryptoCipher  the cipher  (defaults to OpenSslCipher)
      * @throws GeneralSecurityException if cipher initialize failed
      * @throws IllegalArgumentException if no classname(s)
      */
@@ -151,7 +151,7 @@ public class CryptoCipherFactory {
      * <i>AES/CBC/PKCS5Padding</i>.
      * See the Java Cryptography Architecture Standard Algorithm Name Documentation
      * for information about standard transformation names.
-     * @return CryptoCipher the cipher object (defaults to OpensslCipher if available, else JceCipher)
+     * @return CryptoCipher the cipher object (defaults to OpenSslCipher if available, else JceCipher)
      * @throws GeneralSecurityException if JCE cipher initialize failed
      */
     public static CryptoCipher getCryptoCipher(String transformation)

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/cipher/OpenSsl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/OpenSsl.java b/src/main/java/org/apache/commons/crypto/cipher/OpenSsl.java
new file mode 100644
index 0000000..6c72ea5
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/cipher/OpenSsl.java
@@ -0,0 +1,366 @@
+/**
+ * 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;
+import java.security.NoSuchAlgorithmException;
+import java.util.StringTokenizer;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.commons.crypto.Crypto;
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * OpenSSL cryptographic wrapper using JNI. Currently only AES-CTR is supported.
+ * It's flexible to add other crypto algorithms/modes.
+ */
+final class OpenSsl {
+
+    // Mode constant defined by OpenSsl JNI
+    public static final int ENCRYPT_MODE = 1;
+    public static final int DECRYPT_MODE = 0;
+
+    /** Currently only support AES/CTR/NoPadding. */
+    private static enum AlgorithmMode {
+        AES_CTR, AES_CBC;
+
+        /**
+         * Gets the mode.
+         *
+         * @param algorithm the algorithm.
+         * @param mode the mode.
+         * @return the Algorithm mode.
+         * @throws NoSuchAlgorithmException if the algorithm is not available.
+         */
+        static int get(String algorithm, String mode)
+                throws NoSuchAlgorithmException {
+            try {
+                return AlgorithmMode.valueOf(algorithm + "_" + mode).ordinal();
+            } catch (Exception e) {
+                throw new NoSuchAlgorithmException(
+                        "Doesn't support algorithm: " + algorithm
+                                + " and mode: " + mode);
+            }
+        }
+    }
+
+    private static enum Padding {
+        NoPadding, PKCS5Padding;
+
+        /**
+         * Gets the Padding instance.
+         *
+         * @param padding the padding.
+         * @return the value of Padding.
+         * @throws NoSuchPaddingException if the padding is not available.
+         */
+        static int get(String padding) throws NoSuchPaddingException {
+            try {
+                return Padding.valueOf(padding).ordinal();
+            } catch (Exception e) {
+                throw new NoSuchPaddingException("Doesn't support padding: "
+                        + padding);
+            }
+        }
+    }
+
+    private long context = 0;
+    private final int algorithm;
+    private final int padding;
+
+    private static final String loadingFailureReason;
+
+    static {
+        String loadingFailure = null;
+        try {
+            if (Crypto.isNativeCodeLoaded()) {
+                OpensslNative.initIDs();
+            }
+        } catch (Exception t) {
+            loadingFailure = t.getMessage();
+        } finally {
+            loadingFailureReason = loadingFailure;
+        }
+    }
+
+    /**
+     * Gets the failure reason when loading OpenSsl native.
+     *
+     * @return the failure reason.
+     */
+    public static String getLoadingFailureReason() {
+        return loadingFailureReason;
+    }
+
+    /**
+     * Constructs a {@link OpenSsl} instance based on context, algorithm and padding.
+     *
+     * @param context the context.
+     * @param algorithm the algorithm.
+     * @param padding the padding.
+     */
+    private OpenSsl(long context, int algorithm, int padding) {
+        this.context = context;
+        this.algorithm = algorithm;
+        this.padding = padding;
+    }
+
+    /**
+     * Return an <code>OpenSslCipher</code> object that implements the specified
+     * transformation.
+     *
+     * @param transformation the name of the transformation, e.g.,
+     *        AES/CTR/NoPadding.
+     * @return OpenSslCipher an <code>OpenSslCipher</code> object
+     * @throws NoSuchAlgorithmException if <code>transformation</code> is null,
+     *         empty, in an invalid format, or if OpenSsl doesn't implement the
+     *         specified algorithm.
+     * @throws NoSuchPaddingException if <code>transformation</code> contains a
+     *         padding scheme that is not available.
+     */
+    public static OpenSsl getInstance(String transformation)
+            throws NoSuchAlgorithmException, NoSuchPaddingException {
+        Transform transform = tokenizeTransformation(transformation);
+        int algorithmMode = AlgorithmMode.get(transform.algorithm,
+                transform.mode);
+        int padding = Padding.get(transform.padding);
+        long context = OpensslNative.initContext(algorithmMode, padding);
+        return new OpenSsl(context, algorithmMode, padding);
+    }
+
+    /** Nested class for algorithm, mode and padding. */
+    private static class Transform {
+        final String algorithm;
+        final String mode;
+        final String padding;
+
+        /**
+         * Constructs a {@link Transform} based on the algorithm, mode and padding.
+         *
+         * @param algorithm the algorithm
+         * @param mode the mode.
+         * @param padding the padding.
+         */
+        public Transform(String algorithm, String mode, String padding) {
+            this.algorithm = algorithm;
+            this.mode = mode;
+            this.padding = padding;
+        }
+    }
+
+    /**
+     * Gets the tokens of transformation.
+     *
+     * @param transformation the transformation.
+     * @return the {@link Transform} instance.
+     * @throws NoSuchAlgorithmException if the transformation is null.
+     */
+    private static Transform tokenizeTransformation(String transformation)
+            throws NoSuchAlgorithmException {
+        if (transformation == null) {
+            throw new NoSuchAlgorithmException("No transformation given.");
+        }
+
+        /*
+         * Array containing the components of a Cipher transformation: index 0:
+         * algorithm (e.g., AES) index 1: mode (e.g., CTR) index 2: padding
+         * (e.g., NoPadding)
+         */
+        String[] parts = new String[3];
+        int count = 0;
+        StringTokenizer parser = new StringTokenizer(transformation, "/");
+        while (parser.hasMoreTokens() && count < 3) {
+            parts[count++] = parser.nextToken().trim();
+        }
+        if (count != 3 || parser.hasMoreTokens()) {
+            throw new NoSuchAlgorithmException(
+                    "Invalid transformation format: " + transformation);
+        }
+        return new Transform(parts[0], parts[1], parts[2]);
+    }
+
+    /**
+     * Initialize this cipher with a key and IV.
+     *
+     * @param mode {@link #ENCRYPT_MODE} or {@link #DECRYPT_MODE}
+     * @param key crypto key
+     * @param iv crypto iv
+     */
+    public void init(int mode, byte[] key, byte[] iv) {
+        context = OpensslNative
+                .init(context, mode, algorithm, padding, key, iv);
+    }
+
+    /**
+     * <p>
+     * Continues a multiple-part encryption or decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     * </p>
+     *
+     * <p>
+     * All <code>input.remaining()</code> bytes starting at
+     * <code>input.position()</code> are processed. The result is stored in the
+     * output buffer.
+     * </p>
+     *
+     * <p>
+     * Upon return, the input buffer's position will be equal to its limit; its
+     * limit will not have changed. The output buffer's position will have
+     * advanced by n, when n is the value returned by this method; the output
+     * buffer's limit will not have changed.
+     * </p>
+     *
+     * If <code>output.remaining()</code> bytes are insufficient to hold the
+     * result, a <code>ShortBufferException</code> is thrown.
+     *
+     * @param input the input ByteBuffer
+     * @param output the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         buffer
+     */
+    public int update(ByteBuffer input, ByteBuffer output)
+            throws ShortBufferException {
+        checkState();
+        Utils.checkArgument(input.isDirect() && output.isDirect(),
+                "Direct buffers are required.");
+        int len = OpensslNative.update(context, input, input.position(),
+                input.remaining(), output, output.position(),
+                output.remaining());
+        input.position(input.limit());
+        output.position(output.position() + len);
+        return len;
+    }
+
+    /**
+     * Continues a multiple-part encryption/decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     *
+     * @param input the input byte array
+     * @param inputOffset the offset in input where the input starts
+     * @param inputLen the input length
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         byte array
+     */
+    public int update(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset) throws ShortBufferException {
+        checkState();
+        return OpensslNative.updateByteArray(context, input, inputOffset,
+                inputLen, output, outputOffset, output.length - outputOffset);
+    }
+
+    /**
+     * <p>
+     * Finishes a multiple-part operation. The data is encrypted or decrypted,
+     * depending on how this cipher was initialized.
+     * </p>
+     *
+     * <p>
+     * The result is stored in the output buffer. Upon return, the output
+     * buffer's position will have advanced by n, where n is the value returned
+     * by this method; the output buffer's limit will not have changed.
+     * </p>
+     *
+     * <p>
+     * If <code>output.remaining()</code> bytes are insufficient to hold the
+     * result, a <code>ShortBufferException</code> is thrown.
+     * </p>
+     *
+     * <p>
+     * Upon finishing, this method resets this cipher object to the state it was
+     * in when previously initialized. That is, the object is available to
+     * encrypt or decrypt more data.
+     * </p>
+     *
+     * If any exception is thrown, this cipher object need to be reset before it
+     * can be used again.
+     *
+     * @param output the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws ShortBufferException if the given output byte array is too small
+     *         to hold the result.
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     */
+    public int doFinal(ByteBuffer output) throws ShortBufferException,
+            IllegalBlockSizeException, BadPaddingException {
+        checkState();
+        Utils.checkArgument(output.isDirect(), "Direct buffer is required.");
+        int len = OpensslNative.doFinal(context, output, output.position(),
+                output.remaining());
+        output.position(output.position() + len);
+        return len;
+    }
+
+    /**
+     * Encrypts or decrypts data in a single-part operation, or finishes a
+     * multiple-part operation.
+     *
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if the given output byte array is too small
+     *         to hold the result
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     */
+    public int doFinal(byte[] output, int outputOffset)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        checkState();
+        return OpensslNative.doFinalByteArray(context, output, outputOffset,
+                output.length - outputOffset);
+    }
+
+    /** Forcibly clean the context. */
+    public void clean() {
+        if (context != 0) {
+            OpensslNative.clean(context);
+            context = 0;
+        }
+    }
+
+    /** Checks whether context is initialized. */
+    private void checkState() {
+        Utils.checkState(context != 0);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        clean();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/cipher/OpenSslCipher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/OpenSslCipher.java b/src/main/java/org/apache/commons/crypto/cipher/OpenSslCipher.java
new file mode 100644
index 0000000..af25df3
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/cipher/OpenSslCipher.java
@@ -0,0 +1,222 @@
+/**
+ * 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;
+import java.security.GeneralSecurityException;
+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.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.commons.crypto.utils.Utils;
+
+/**
+ * Implements the CryptoCipher using JNI into OpenSSL.
+ */
+class OpenSslCipher implements CryptoCipher {
+
+    private final OpenSsl cipher;
+
+    private final String transformation;
+
+    private final static int AES_BLOCK_SIZE = 16;
+
+    /**
+     * Constructs a {@link CryptoCipher} using JNI into OpenSSL
+     *
+     * @param props  properties for OpenSSL cipher (unused)
+     * @param transformation  transformation for OpenSSL cipher (algorithm/mode/padding)
+     * @throws GeneralSecurityException if OpenSSL cipher initialize failed
+     */
+    // N.B. this class is not public/protected so does not appear in the main Javadoc
+    // Please ensure that property use is documented in the enum CryptoRandomFactory.RandomProvider
+    public OpenSslCipher(Properties props, String transformation) // NOPMD
+            throws GeneralSecurityException {
+        this.transformation = transformation;
+
+        String loadingFailureReason = OpenSsl.getLoadingFailureReason();
+        if (loadingFailureReason != null) {
+            throw new RuntimeException(loadingFailureReason);
+        }
+
+        cipher = OpenSsl.getInstance(transformation);
+    }
+
+    /**
+     * Returns the block size (in bytes).
+     *
+     * @return the block size (in bytes), or 0 if the underlying algorithm is
+     * not a block cipher
+     */
+    @Override
+    public final int getBlockSize() {
+        return AES_BLOCK_SIZE;
+    }
+
+    /**
+     * Returns the algorithm name of this {@code CryptoCipher} object.
+     *
+     * <p>This is the same name that was specified in one of the
+     * {@code CryptoCipherFactory#getInstance} calls that created this
+     * {@code CryptoCipher} object..
+     *
+     * @return the algorithm name of this {@code CryptoCipher} object.
+     */
+    @Override
+    public String getAlgorithm() {
+        return transformation;
+    }
+
+    /**
+     * Initializes the cipher with mode, key and iv.
+     *
+     * @param mode {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
+     * @param key crypto key for the cipher
+     * @param params the algorithm parameters
+     * @throws InvalidKeyException If key length is invalid
+     * @throws InvalidAlgorithmParameterException if IV length is wrong
+     */
+    @Override
+    public void init(int mode, Key key, AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        Utils.checkNotNull(key);
+        Utils.checkNotNull(params);
+
+        int cipherMode = OpenSsl.DECRYPT_MODE;
+        if (mode == Cipher.ENCRYPT_MODE) {
+            cipherMode = OpenSsl.ENCRYPT_MODE;
+        }
+        byte[] iv;
+        if (params instanceof IvParameterSpec) {
+            iv = ((IvParameterSpec) params).getIV();
+        } else {
+            // other AlgorithmParameterSpec such as GCMParameterSpec is not
+            // supported now.
+            throw new InvalidAlgorithmParameterException("Illegal parameters");
+        }
+        cipher.init(cipherMode, key.getEncoded(), iv);
+    }
+
+    /**
+     * Continues a multiple-part encryption/decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     *
+     * @param inBuffer the input ByteBuffer
+     * @param outBuffer the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         buffer
+     */
+    @Override
+    public int update(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException {
+        return cipher.update(inBuffer, outBuffer);
+    }
+
+    /**
+     * Continues a multiple-part encryption/decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     *
+     * @param input the input byte array
+     * @param inputOffset the offset in input where the input starts
+     * @param inputLen the input length
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         byte array
+     */
+    @Override
+    public int update(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset) throws ShortBufferException {
+        return cipher
+                .update(input, inputOffset, inputLen, output, outputOffset);
+    }
+
+    /**
+     * Encrypts or decrypts data in a single-part operation, or finishes a
+     * multiple-part operation. The data is encrypted or decrypted, depending on
+     * how this cipher was initialized.
+     *
+     * @param inBuffer the input ByteBuffer
+     * @param outBuffer the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     * @throws ShortBufferException if the given output buffer is too small to
+     *         hold the result
+     */
+    @Override
+    public int doFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int n = cipher.update(inBuffer, outBuffer);
+        return n + cipher.doFinal(outBuffer);
+    }
+
+    /**
+     * Encrypts or decrypts data in a single-part operation, or finishes a
+     * multiple-part operation.
+     *
+     * @param input the input byte array
+     * @param inputOffset the offset in input where the input starts
+     * @param inputLen the input length
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if the given output byte array is too small
+     *         to hold the result
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     */
+    @Override
+    public int doFinal(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset) throws ShortBufferException,
+            IllegalBlockSizeException, BadPaddingException {
+        int n = cipher.update(input, inputOffset, inputLen, output,
+                outputOffset);
+        return n + cipher.doFinal(output, outputOffset + n);
+    }
+
+    /**
+     * Closes the OpenSSL cipher. Clean the OpenSsl native context.
+     */
+    @Override
+    public void close() {
+        cipher.clean();
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/cipher/Openssl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/Openssl.java b/src/main/java/org/apache/commons/crypto/cipher/Openssl.java
deleted file mode 100644
index 3a9b146..0000000
--- a/src/main/java/org/apache/commons/crypto/cipher/Openssl.java
+++ /dev/null
@@ -1,366 +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.cipher;
-
-import java.nio.ByteBuffer;
-import java.security.NoSuchAlgorithmException;
-import java.util.StringTokenizer;
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.commons.crypto.Crypto;
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * OpenSSL cryptographic wrapper using JNI. Currently only AES-CTR is supported.
- * It's flexible to add other crypto algorithms/modes.
- */
-final class Openssl {
-
-    // Mode constant defined by Openssl JNI
-    public static final int ENCRYPT_MODE = 1;
-    public static final int DECRYPT_MODE = 0;
-
-    /** Currently only support AES/CTR/NoPadding. */
-    private static enum AlgorithmMode {
-        AES_CTR, AES_CBC;
-
-        /**
-         * Gets the mode.
-         *
-         * @param algorithm the algorithm.
-         * @param mode the mode.
-         * @return the Algorithm mode.
-         * @throws NoSuchAlgorithmException if the algorithm is not available.
-         */
-        static int get(String algorithm, String mode)
-                throws NoSuchAlgorithmException {
-            try {
-                return AlgorithmMode.valueOf(algorithm + "_" + mode).ordinal();
-            } catch (Exception e) {
-                throw new NoSuchAlgorithmException(
-                        "Doesn't support algorithm: " + algorithm
-                                + " and mode: " + mode);
-            }
-        }
-    }
-
-    private static enum Padding {
-        NoPadding, PKCS5Padding;
-
-        /**
-         * Gets the Padding instance.
-         *
-         * @param padding the padding.
-         * @return the value of Padding.
-         * @throws NoSuchPaddingException if the padding is not available.
-         */
-        static int get(String padding) throws NoSuchPaddingException {
-            try {
-                return Padding.valueOf(padding).ordinal();
-            } catch (Exception e) {
-                throw new NoSuchPaddingException("Doesn't support padding: "
-                        + padding);
-            }
-        }
-    }
-
-    private long context = 0;
-    private final int algorithm;
-    private final int padding;
-
-    private static final String loadingFailureReason;
-
-    static {
-        String loadingFailure = null;
-        try {
-            if (Crypto.isNativeCodeLoaded()) {
-                OpensslNative.initIDs();
-            }
-        } catch (Exception t) {
-            loadingFailure = t.getMessage();
-        } finally {
-            loadingFailureReason = loadingFailure;
-        }
-    }
-
-    /**
-     * Gets the failure reason when loading Openssl native.
-     *
-     * @return the failure reason.
-     */
-    public static String getLoadingFailureReason() {
-        return loadingFailureReason;
-    }
-
-    /**
-     * Constructs a {@link Openssl} instance based on context, algorithm and padding.
-     *
-     * @param context the context.
-     * @param algorithm the algorithm.
-     * @param padding the padding.
-     */
-    private Openssl(long context, int algorithm, int padding) {
-        this.context = context;
-        this.algorithm = algorithm;
-        this.padding = padding;
-    }
-
-    /**
-     * Return an <code>OpensslCipher</code> object that implements the specified
-     * transformation.
-     *
-     * @param transformation the name of the transformation, e.g.,
-     *        AES/CTR/NoPadding.
-     * @return OpensslCipher an <code>OpensslCipher</code> object
-     * @throws NoSuchAlgorithmException if <code>transformation</code> is null,
-     *         empty, in an invalid format, or if Openssl doesn't implement the
-     *         specified algorithm.
-     * @throws NoSuchPaddingException if <code>transformation</code> contains a
-     *         padding scheme that is not available.
-     */
-    public static Openssl getInstance(String transformation)
-            throws NoSuchAlgorithmException, NoSuchPaddingException {
-        Transform transform = tokenizeTransformation(transformation);
-        int algorithmMode = AlgorithmMode.get(transform.algorithm,
-                transform.mode);
-        int padding = Padding.get(transform.padding);
-        long context = OpensslNative.initContext(algorithmMode, padding);
-        return new Openssl(context, algorithmMode, padding);
-    }
-
-    /** Nested class for algorithm, mode and padding. */
-    private static class Transform {
-        final String algorithm;
-        final String mode;
-        final String padding;
-
-        /**
-         * Constructs a {@link Transform} based on the algorithm, mode and padding.
-         *
-         * @param algorithm the algorithm
-         * @param mode the mode.
-         * @param padding the padding.
-         */
-        public Transform(String algorithm, String mode, String padding) {
-            this.algorithm = algorithm;
-            this.mode = mode;
-            this.padding = padding;
-        }
-    }
-
-    /**
-     * Gets the tokens of transformation.
-     *
-     * @param transformation the transformation.
-     * @return the {@link Transform} instance.
-     * @throws NoSuchAlgorithmException if the transformation is null.
-     */
-    private static Transform tokenizeTransformation(String transformation)
-            throws NoSuchAlgorithmException {
-        if (transformation == null) {
-            throw new NoSuchAlgorithmException("No transformation given.");
-        }
-
-        /*
-         * Array containing the components of a Cipher transformation: index 0:
-         * algorithm (e.g., AES) index 1: mode (e.g., CTR) index 2: padding
-         * (e.g., NoPadding)
-         */
-        String[] parts = new String[3];
-        int count = 0;
-        StringTokenizer parser = new StringTokenizer(transformation, "/");
-        while (parser.hasMoreTokens() && count < 3) {
-            parts[count++] = parser.nextToken().trim();
-        }
-        if (count != 3 || parser.hasMoreTokens()) {
-            throw new NoSuchAlgorithmException(
-                    "Invalid transformation format: " + transformation);
-        }
-        return new Transform(parts[0], parts[1], parts[2]);
-    }
-
-    /**
-     * Initialize this cipher with a key and IV.
-     *
-     * @param mode {@link #ENCRYPT_MODE} or {@link #DECRYPT_MODE}
-     * @param key crypto key
-     * @param iv crypto iv
-     */
-    public void init(int mode, byte[] key, byte[] iv) {
-        context = OpensslNative
-                .init(context, mode, algorithm, padding, key, iv);
-    }
-
-    /**
-     * <p>
-     * Continues a multiple-part encryption or decryption operation. The data is
-     * encrypted or decrypted, depending on how this cipher was initialized.
-     * </p>
-     *
-     * <p>
-     * All <code>input.remaining()</code> bytes starting at
-     * <code>input.position()</code> are processed. The result is stored in the
-     * output buffer.
-     * </p>
-     *
-     * <p>
-     * Upon return, the input buffer's position will be equal to its limit; its
-     * limit will not have changed. The output buffer's position will have
-     * advanced by n, when n is the value returned by this method; the output
-     * buffer's limit will not have changed.
-     * </p>
-     *
-     * If <code>output.remaining()</code> bytes are insufficient to hold the
-     * result, a <code>ShortBufferException</code> is thrown.
-     *
-     * @param input the input ByteBuffer
-     * @param output the output ByteBuffer
-     * @return int number of bytes stored in <code>output</code>
-     * @throws ShortBufferException if there is insufficient space in the output
-     *         buffer
-     */
-    public int update(ByteBuffer input, ByteBuffer output)
-            throws ShortBufferException {
-        checkState();
-        Utils.checkArgument(input.isDirect() && output.isDirect(),
-                "Direct buffers are required.");
-        int len = OpensslNative.update(context, input, input.position(),
-                input.remaining(), output, output.position(),
-                output.remaining());
-        input.position(input.limit());
-        output.position(output.position() + len);
-        return len;
-    }
-
-    /**
-     * Continues a multiple-part encryption/decryption operation. The data is
-     * encrypted or decrypted, depending on how this cipher was initialized.
-     *
-     * @param input the input byte array
-     * @param inputOffset the offset in input where the input starts
-     * @param inputLen the input length
-     * @param output the byte array for the result
-     * @param outputOffset the offset in output where the result is stored
-     * @return the number of bytes stored in output
-     * @throws ShortBufferException if there is insufficient space in the output
-     *         byte array
-     */
-    public int update(byte[] input, int inputOffset, int inputLen,
-            byte[] output, int outputOffset) throws ShortBufferException {
-        checkState();
-        return OpensslNative.updateByteArray(context, input, inputOffset,
-                inputLen, output, outputOffset, output.length - outputOffset);
-    }
-
-    /**
-     * <p>
-     * Finishes a multiple-part operation. The data is encrypted or decrypted,
-     * depending on how this cipher was initialized.
-     * </p>
-     *
-     * <p>
-     * The result is stored in the output buffer. Upon return, the output
-     * buffer's position will have advanced by n, where n is the value returned
-     * by this method; the output buffer's limit will not have changed.
-     * </p>
-     *
-     * <p>
-     * If <code>output.remaining()</code> bytes are insufficient to hold the
-     * result, a <code>ShortBufferException</code> is thrown.
-     * </p>
-     *
-     * <p>
-     * Upon finishing, this method resets this cipher object to the state it was
-     * in when previously initialized. That is, the object is available to
-     * encrypt or decrypt more data.
-     * </p>
-     *
-     * If any exception is thrown, this cipher object need to be reset before it
-     * can be used again.
-     *
-     * @param output the output ByteBuffer
-     * @return int number of bytes stored in <code>output</code>
-     * @throws ShortBufferException if the given output byte array is too small
-     *         to hold the result.
-     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
-     *         padding has been requested (only in encryption mode), and the
-     *         total input length of the data processed by this cipher is not a
-     *         multiple of block size; or if this encryption algorithm is unable
-     *         to process the input data provided.
-     * @throws BadPaddingException if this cipher is in decryption mode, and
-     *         (un)padding has been requested, but the decrypted data is not
-     *         bounded by the appropriate padding bytes
-     */
-    public int doFinal(ByteBuffer output) throws ShortBufferException,
-            IllegalBlockSizeException, BadPaddingException {
-        checkState();
-        Utils.checkArgument(output.isDirect(), "Direct buffer is required.");
-        int len = OpensslNative.doFinal(context, output, output.position(),
-                output.remaining());
-        output.position(output.position() + len);
-        return len;
-    }
-
-    /**
-     * Encrypts or decrypts data in a single-part operation, or finishes a
-     * multiple-part operation.
-     *
-     * @param output the byte array for the result
-     * @param outputOffset the offset in output where the result is stored
-     * @return the number of bytes stored in output
-     * @throws ShortBufferException if the given output byte array is too small
-     *         to hold the result
-     * @throws BadPaddingException if this cipher is in decryption mode, and
-     *         (un)padding has been requested, but the decrypted data is not
-     *         bounded by the appropriate padding bytes
-     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
-     *         padding has been requested (only in encryption mode), and the
-     *         total input length of the data processed by this cipher is not a
-     *         multiple of block size; or if this encryption algorithm is unable
-     *         to process the input data provided.
-     */
-    public int doFinal(byte[] output, int outputOffset)
-            throws ShortBufferException, IllegalBlockSizeException,
-            BadPaddingException {
-        checkState();
-        return OpensslNative.doFinalByteArray(context, output, outputOffset,
-                output.length - outputOffset);
-    }
-
-    /** Forcibly clean the context. */
-    public void clean() {
-        if (context != 0) {
-            OpensslNative.clean(context);
-            context = 0;
-        }
-    }
-
-    /** Checks whether context is initialized. */
-    private void checkState() {
-        Utils.checkState(context != 0);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        clean();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/cipher/OpensslCipher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/cipher/OpensslCipher.java b/src/main/java/org/apache/commons/crypto/cipher/OpensslCipher.java
deleted file mode 100644
index 06e3798..0000000
--- a/src/main/java/org/apache/commons/crypto/cipher/OpensslCipher.java
+++ /dev/null
@@ -1,222 +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.cipher;
-
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-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.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-
-import org.apache.commons.crypto.utils.Utils;
-
-/**
- * Implements the CryptoCipher using JNI into OpenSSL.
- */
-class OpensslCipher implements CryptoCipher {
-
-    private final Openssl cipher;
-
-    private final String transformation;
-
-    private final static int AES_BLOCK_SIZE = 16;
-
-    /**
-     * Constructs a {@link CryptoCipher} using JNI into OpenSSL
-     *
-     * @param props  properties for OpenSSL cipher (unused)
-     * @param transformation  transformation for OpenSSL cipher (algorithm/mode/padding)
-     * @throws GeneralSecurityException if OpenSSL cipher initialize failed
-     */
-    // N.B. this class is not public/protected so does not appear in the main Javadoc
-    // Please ensure that property use is documented in the enum CryptoRandomFactory.RandomProvider
-    public OpensslCipher(Properties props, String transformation) // NOPMD
-            throws GeneralSecurityException {
-        this.transformation = transformation;
-
-        String loadingFailureReason = Openssl.getLoadingFailureReason();
-        if (loadingFailureReason != null) {
-            throw new RuntimeException(loadingFailureReason);
-        }
-
-        cipher = Openssl.getInstance(transformation);
-    }
-
-    /**
-     * Returns the block size (in bytes).
-     *
-     * @return the block size (in bytes), or 0 if the underlying algorithm is
-     * not a block cipher
-     */
-    @Override
-    public final int getBlockSize() {
-        return AES_BLOCK_SIZE;
-    }
-
-    /**
-     * Returns the algorithm name of this {@code CryptoCipher} object.
-     *
-     * <p>This is the same name that was specified in one of the
-     * {@code CryptoCipherFactory#getInstance} calls that created this
-     * {@code CryptoCipher} object..
-     *
-     * @return the algorithm name of this {@code CryptoCipher} object.
-     */
-    @Override
-    public String getAlgorithm() {
-        return transformation;
-    }
-
-    /**
-     * Initializes the cipher with mode, key and iv.
-     *
-     * @param mode {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
-     * @param key crypto key for the cipher
-     * @param params the algorithm parameters
-     * @throws InvalidKeyException If key length is invalid
-     * @throws InvalidAlgorithmParameterException if IV length is wrong
-     */
-    @Override
-    public void init(int mode, Key key, AlgorithmParameterSpec params)
-            throws InvalidKeyException, InvalidAlgorithmParameterException {
-        Utils.checkNotNull(key);
-        Utils.checkNotNull(params);
-
-        int cipherMode = Openssl.DECRYPT_MODE;
-        if (mode == Cipher.ENCRYPT_MODE) {
-            cipherMode = Openssl.ENCRYPT_MODE;
-        }
-        byte[] iv;
-        if (params instanceof IvParameterSpec) {
-            iv = ((IvParameterSpec) params).getIV();
-        } else {
-            // other AlgorithmParameterSpec such as GCMParameterSpec is not
-            // supported now.
-            throw new InvalidAlgorithmParameterException("Illegal parameters");
-        }
-        cipher.init(cipherMode, key.getEncoded(), iv);
-    }
-
-    /**
-     * Continues a multiple-part encryption/decryption operation. The data is
-     * encrypted or decrypted, depending on how this cipher was initialized.
-     *
-     * @param inBuffer the input ByteBuffer
-     * @param outBuffer the output ByteBuffer
-     * @return int number of bytes stored in <code>output</code>
-     * @throws ShortBufferException if there is insufficient space in the output
-     *         buffer
-     */
-    @Override
-    public int update(ByteBuffer inBuffer, ByteBuffer outBuffer)
-            throws ShortBufferException {
-        return cipher.update(inBuffer, outBuffer);
-    }
-
-    /**
-     * Continues a multiple-part encryption/decryption operation. The data is
-     * encrypted or decrypted, depending on how this cipher was initialized.
-     *
-     * @param input the input byte array
-     * @param inputOffset the offset in input where the input starts
-     * @param inputLen the input length
-     * @param output the byte array for the result
-     * @param outputOffset the offset in output where the result is stored
-     * @return the number of bytes stored in output
-     * @throws ShortBufferException if there is insufficient space in the output
-     *         byte array
-     */
-    @Override
-    public int update(byte[] input, int inputOffset, int inputLen,
-            byte[] output, int outputOffset) throws ShortBufferException {
-        return cipher
-                .update(input, inputOffset, inputLen, output, outputOffset);
-    }
-
-    /**
-     * Encrypts or decrypts data in a single-part operation, or finishes a
-     * multiple-part operation. The data is encrypted or decrypted, depending on
-     * how this cipher was initialized.
-     *
-     * @param inBuffer the input ByteBuffer
-     * @param outBuffer the output ByteBuffer
-     * @return int number of bytes stored in <code>output</code>
-     * @throws BadPaddingException if this cipher is in decryption mode, and
-     *         (un)padding has been requested, but the decrypted data is not
-     *         bounded by the appropriate padding bytes
-     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
-     *         padding has been requested (only in encryption mode), and the
-     *         total input length of the data processed by this cipher is not a
-     *         multiple of block size; or if this encryption algorithm is unable
-     *         to process the input data provided.
-     * @throws ShortBufferException if the given output buffer is too small to
-     *         hold the result
-     */
-    @Override
-    public int doFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
-            throws ShortBufferException, IllegalBlockSizeException,
-            BadPaddingException {
-        int n = cipher.update(inBuffer, outBuffer);
-        return n + cipher.doFinal(outBuffer);
-    }
-
-    /**
-     * Encrypts or decrypts data in a single-part operation, or finishes a
-     * multiple-part operation.
-     *
-     * @param input the input byte array
-     * @param inputOffset the offset in input where the input starts
-     * @param inputLen the input length
-     * @param output the byte array for the result
-     * @param outputOffset the offset in output where the result is stored
-     * @return the number of bytes stored in output
-     * @throws ShortBufferException if the given output byte array is too small
-     *         to hold the result
-     * @throws BadPaddingException if this cipher is in decryption mode, and
-     *         (un)padding has been requested, but the decrypted data is not
-     *         bounded by the appropriate padding bytes
-     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
-     *         padding has been requested (only in encryption mode), and the
-     *         total input length of the data processed by this cipher is not a
-     *         multiple of block size; or if this encryption algorithm is unable
-     *         to process the input data provided.
-     */
-    @Override
-    public int doFinal(byte[] input, int inputOffset, int inputLen,
-            byte[] output, int outputOffset) throws ShortBufferException,
-            IllegalBlockSizeException, BadPaddingException {
-        int n = cipher.update(input, inputOffset, inputLen, output,
-                outputOffset);
-        return n + cipher.doFinal(output, outputOffset + n);
-    }
-
-    /**
-     * Closes the OpenSSL cipher. Clean the Openssl native context.
-     */
-    @Override
-    public void close() {
-        cipher.clean();
-    }
-}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/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
index 2db91e5..9bac6dd 100644
--- a/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java
+++ b/src/main/java/org/apache/commons/crypto/cipher/OpensslNative.java
@@ -20,7 +20,7 @@ package org.apache.commons.crypto.cipher;
 import java.nio.ByteBuffer;
 
 /**
- * JNI interface of {@link Openssl} implementation. The native method in this
+ * JNI interface of {@link OpenSsl} implementation. The native method in this
  * class is defined in OpensslNative.h (generated by javah).
  */
 class OpensslNative {
@@ -50,8 +50,8 @@ class OpensslNative {
      *
      * @param context The cipher context address
      * @param mode ENCRYPT_MODE or DECRYPT_MODE
-     * @param alg Algorithm Mode of Openssl
-     * @param padding the padding mode of Openssl cipher
+     * @param alg Algorithm Mode of OpenSsl
+     * @param padding the padding mode of OpenSsl cipher
      * @param key crypto key
      * @param iv crypto iv
      * @return the context address of cipher

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/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
index 875d113..d5c19a4 100644
--- a/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java
+++ b/src/main/java/org/apache/commons/crypto/conf/ConfigurationKeys.java
@@ -37,10 +37,10 @@ public class ConfigurationKeys {
      * The configuration key of implementation class for crypto cipher. The
      * values of CIPHER_CLASSES_KEY can be
      * "org.apache.commons.crypto.cipher.JceCipher" or
-     * "org.apache.commons.crypto.cipher.OpensslCipher". Or it can be a comma
+     * "org.apache.commons.crypto.cipher.OpenSslCipher". Or it can be a comma
      * separated list. The "org.apache.commons.crypto.cipher.JceCipher" use jce
      * provider to implement CryptoCipher and the
-     * "org.apache.commons.crypto.cipher.OpensslCipher" use jni into openssl to
+     * "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).
      */
@@ -80,11 +80,11 @@ public class ConfigurationKeys {
      * The configuration key of the implementation class for secure random. The
      * values of SECURE_RANDOM_CLASSES_KEY can be
      * "org.apache.commons.crypto.random.JavaCryptoRandom" or
-     * "org.apache.commons.crypto.random.OpensslCryptoRandom". Or it takes a
+     * "org.apache.commons.crypto.random.OpenSslCryptoRandom". Or it takes a
      * comma separated list. The
      * "org.apache.commons.crypto.random.JavaCryptoRandom" use java to implement
      * {@link org.apache.commons.crypto.random.CryptoRandom} and the
-     * "org.apache.commons.crypto.random.OpensslCryptoRandom" use jni into
+     * "org.apache.commons.crypto.random.OpenSslCryptoRandom" 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).
      */

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCipher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCipher.java b/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCipher.java
new file mode 100644
index 0000000..adef3cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCipher.java
@@ -0,0 +1,331 @@
+/**
+ * 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.jna;
+
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.commons.crypto.cipher.CryptoCipher;
+import org.apache.commons.crypto.utils.Utils;
+
+import com.sun.jna.NativeLong;
+import com.sun.jna.ptr.PointerByReference;
+
+/**
+ * Implements the CryptoCipher using JNA into OpenSSL.
+ */
+class OpenSslJnaCipher implements CryptoCipher {
+
+    private final static int AES_BLOCK_SIZE = 16;
+    
+    private PointerByReference algo;
+    private final PointerByReference context;
+    private final AlgorithmMode algMode;
+    private final int padding;
+    private final String transformation;
+
+    /**
+     * Constructs a {@link CryptoCipher} using JNA into OpenSSL
+     *
+     * @param props properties for OpenSSL cipher
+     * @param transformation transformation for OpenSSL cipher
+     * @throws GeneralSecurityException if OpenSSL cipher initialize failed
+     */
+    public OpenSslJnaCipher(Properties props, String transformation)
+            throws GeneralSecurityException {
+        this.transformation = transformation;
+        Transform transform = tokenizeTransformation(transformation);
+        algMode = AlgorithmMode.get(transform.algorithm, transform.mode);
+        
+        if(algMode != AlgorithmMode.AES_CBC && algMode != AlgorithmMode.AES_CTR) {
+            throw new GeneralSecurityException("unknown algorithm "+transform.algorithm + "_" + transform.mode);
+        }
+
+        padding = Padding.get(transform.padding);
+        context = OpensslNativeJna.EVP_CIPHER_CTX_new();
+
+    }
+
+    /**
+     * Initializes the cipher with mode, key and iv.
+     *
+     * @param mode {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
+     * @param key crypto key for the cipher
+     * @param params the algorithm parameters
+     * @throws InvalidKeyException If key length is invalid
+     * @throws InvalidAlgorithmParameterException if IV length is wrong
+     */
+    @Override
+    public void init(int mode, Key key, AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        Utils.checkNotNull(key);
+        Utils.checkNotNull(params);
+        int cipherMode = OpensslNativeJna.OOSL_JNA_DECRYPT_MODE;
+        if (mode == Cipher.ENCRYPT_MODE) {
+            cipherMode = OpensslNativeJna.OOSL_JNA_ENCRYPT_MODE;
+        }
+        byte[] iv;
+        if (params instanceof IvParameterSpec) {
+            iv = ((IvParameterSpec) params).getIV();
+        } else {
+            // other AlgorithmParameterSpec such as GCMParameterSpec is not
+            // supported now.
+            throw new InvalidAlgorithmParameterException("Illegal parameters");
+        }
+        
+       if(algMode == AlgorithmMode.AES_CBC) {
+            switch(key.getEncoded().length) {
+                case 16: algo = OpensslNativeJna.EVP_aes_128_cbc(); break;
+                case 24: algo = OpensslNativeJna.EVP_aes_192_cbc(); break;
+                case 32: algo = OpensslNativeJna.EVP_aes_256_cbc(); break;
+                default: throw new InvalidKeyException("keysize unsupported ("+key.getEncoded().length+")");
+            }
+
+        } else {
+            switch(key.getEncoded().length) {
+                case 16: algo = OpensslNativeJna.EVP_aes_128_ctr(); break;
+                case 24: algo = OpensslNativeJna.EVP_aes_192_ctr(); break;
+                case 32: algo = OpensslNativeJna.EVP_aes_256_ctr(); break;
+                default: throw new InvalidKeyException("keysize unsupported ("+key.getEncoded().length+")");
+            }
+        }
+        
+        int retVal = OpensslNativeJna.EVP_CipherInit_ex(context, algo, null, key.getEncoded(), iv, cipherMode);
+        throwOnError(retVal);
+        OpensslNativeJna.EVP_CIPHER_CTX_set_padding(context, padding);
+    }
+
+    /**
+     * Continues a multiple-part encryption/decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     *
+     * @param inBuffer the input ByteBuffer
+     * @param outBuffer the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         buffer
+     */
+    @Override
+    public int update(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException {
+        int[] outlen = new int[1];
+        int retVal = OpensslNativeJna.EVP_CipherUpdate(context, outBuffer, outlen, inBuffer, inBuffer.remaining());
+        throwOnError(retVal);
+        int len = outlen[0];
+        inBuffer.position(inBuffer.limit());
+        outBuffer.position(outBuffer.position() + len);
+        return len;
+    }
+
+    /**
+     * Continues a multiple-part encryption/decryption operation. The data is
+     * encrypted or decrypted, depending on how this cipher was initialized.
+     *
+     * @param input the input byte array
+     * @param inputOffset the offset in input where the input starts
+     * @param inputLen the input length
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if there is insufficient space in the output
+     *         byte array
+     */
+    @Override
+    public int update(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset) throws ShortBufferException {
+        ByteBuffer outputBuf = ByteBuffer.wrap(output, outputOffset, output.length-outputOffset);
+        ByteBuffer inputBuf = ByteBuffer.wrap(input, inputOffset, inputLen);
+        return update(inputBuf, outputBuf);
+    }
+    /**
+     * Encrypts or decrypts data in a single-part operation, or finishes a
+     * multiple-part operation. The data is encrypted or decrypted, depending on
+     * how this cipher was initialized.
+     *
+     * @param inBuffer the input ByteBuffer
+     * @param outBuffer the output ByteBuffer
+     * @return int number of bytes stored in <code>output</code>
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     * @throws ShortBufferException if the given output buffer is too small to
+     *         hold the result
+     */
+    @Override
+    public int doFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int uptLen = update(inBuffer, outBuffer);
+        int[] outlen = new int[1];
+        int retVal = OpensslNativeJna.EVP_CipherFinal_ex(context, outBuffer, outlen);
+        throwOnError(retVal);
+        int len = uptLen + outlen[0];
+        outBuffer.position(outBuffer.position() + outlen[0]);
+        return len;
+    }
+
+    /**
+     * Encrypts or decrypts data in a single-part operation, or finishes a
+     * multiple-part operation.
+     *
+     * @param input the input byte array
+     * @param inputOffset the offset in input where the input starts
+     * @param inputLen the input length
+     * @param output the byte array for the result
+     * @param outputOffset the offset in output where the result is stored
+     * @return the number of bytes stored in output
+     * @throws ShortBufferException if the given output byte array is too small
+     *         to hold the result
+     * @throws BadPaddingException if this cipher is in decryption mode, and
+     *         (un)padding has been requested, but the decrypted data is not
+     *         bounded by the appropriate padding bytes
+     * @throws IllegalBlockSizeException if this cipher is a block cipher, no
+     *         padding has been requested (only in encryption mode), and the
+     *         total input length of the data processed by this cipher is not a
+     *         multiple of block size; or if this encryption algorithm is unable
+     *         to process the input data provided.
+     */
+    @Override
+    public int doFinal(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset) throws ShortBufferException,
+            IllegalBlockSizeException, BadPaddingException {
+        ByteBuffer outputBuf = ByteBuffer.wrap(output, outputOffset, output.length-outputOffset);
+        ByteBuffer inputBuf = ByteBuffer.wrap(input, inputOffset, inputLen);
+        return doFinal(inputBuf, outputBuf);
+    }
+
+    /**
+     * Closes the OpenSSL cipher. Clean the OpenSsl native context.
+     */
+    @Override
+    public void close() {
+        if(context != null) {
+            OpensslNativeJna.EVP_CIPHER_CTX_cleanup(context);
+            OpensslNativeJna.EVP_CIPHER_CTX_free(context);
+        }
+    }
+    
+    private void throwOnError(int retVal) {  
+        if(retVal != 1) {
+            NativeLong err = OpensslNativeJna.ERR_peek_error();
+            String errdesc = OpensslNativeJna.ERR_error_string(err, null);
+            
+            if(context != null) {
+                OpensslNativeJna.EVP_CIPHER_CTX_cleanup(context);
+            }
+            throw new RuntimeException("return code "+retVal+" from openssl. Err code is "+err+": "+errdesc);
+        }
+    }
+
+    //TODO DUPLICATED CODE, needs cleanup
+    /** Nested class for algorithm, mode and padding. */
+    private static class Transform {
+        final String algorithm;
+        final String mode;
+        final String padding;
+
+        public Transform(String algorithm, String mode, String padding) {
+            this.algorithm = algorithm;
+            this.mode = mode;
+            this.padding = padding;
+        }
+    }
+
+    private static Transform tokenizeTransformation(String transformation)
+            throws NoSuchAlgorithmException {
+        if (transformation == null) {
+            throw new NoSuchAlgorithmException("No transformation given.");
+        }
+
+        /*
+         * Array containing the components of a Cipher transformation: index 0:
+         * algorithm (e.g., AES) index 1: mode (e.g., CTR) index 2: padding
+         * (e.g., NoPadding)
+         */
+        String[] parts = new String[3];
+        int count = 0;
+        StringTokenizer parser = new StringTokenizer(transformation, "/");
+        while (parser.hasMoreTokens() && count < 3) {
+            parts[count++] = parser.nextToken().trim();
+        }
+        if (count != 3 || parser.hasMoreTokens()) {
+            throw new NoSuchAlgorithmException(
+                    "Invalid transformation format: " + transformation);
+        }
+        return new Transform(parts[0], parts[1], parts[2]);
+    }
+    
+    /** Currently only support AES/CTR/NoPadding. */
+    private static enum AlgorithmMode {
+        AES_CTR, AES_CBC;
+
+        static AlgorithmMode get(String algorithm, String mode)
+                throws NoSuchAlgorithmException {
+            try {
+                return AlgorithmMode.valueOf(algorithm + "_" + mode);
+            } catch (Exception e) {
+                throw new NoSuchAlgorithmException(
+                        "Doesn't support algorithm: " + algorithm
+                                + " and mode: " + mode);
+            }
+        }
+    }
+
+    private static enum Padding {
+        NoPadding, PKCS5Padding;
+
+        static int get(String padding) throws NoSuchPaddingException {
+            try {
+                return Padding.valueOf(padding).ordinal();
+            } catch (Exception e) {
+                throw new NoSuchPaddingException("Doesn't support padding: "
+                        + padding);
+            }
+        }
+    }
+
+    @Override
+    public int getBlockSize() {
+        return AES_BLOCK_SIZE;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return transformation;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCryptoRandom.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCryptoRandom.java b/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCryptoRandom.java
new file mode 100644
index 0000000..1f4e971
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/jna/OpenSslJnaCryptoRandom.java
@@ -0,0 +1,190 @@
+/**
+ * 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.jna;
+
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.commons.crypto.random.CryptoRandom;
+import org.apache.commons.crypto.utils.Utils;
+
+import com.sun.jna.NativeLong;
+import com.sun.jna.ptr.PointerByReference;
+
+/**
+ * <p>
+ * OpenSSL secure random using JNA. This implementation is thread-safe.
+ * </p>
+ *
+ * <p>
+ * If using an Intel chipset with RDRAND, the high-performance hardware random
+ * number generator will be used and it's much faster than 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 <a href="https://wiki.openssl.org/index.php/Random_Numbers">
+ *      https://wiki.openssl.org/index.php/Random_Numbers</a>
+ * @see <a href="http://en.wikipedia.org/wiki/RdRand">
+ *      http://en.wikipedia.org/wiki/RdRand</a>
+ */
+class OpenSslJnaCryptoRandom extends Random implements CryptoRandom {
+    private static final long serialVersionUID = -7128193502768749585L;
+    private final boolean rdrandEnabled;
+    private PointerByReference rdrandEngine;
+
+    /**
+     * Constructs a {@link OpenSslJnaCryptoRandom}.
+     *
+     * @param props the configuration properties (not used)
+     * @throws NoSuchAlgorithmException if no Provider supports a
+     *         SecureRandomSpi implementation for the specified algorithm.
+     */
+    public OpenSslJnaCryptoRandom(Properties props)
+            throws NoSuchAlgorithmException {
+
+        boolean rdrandLoaded = false;
+        try {
+            OpensslNativeJna.ENGINE_load_rdrand();
+            rdrandEngine = OpensslNativeJna.ENGINE_by_id("rdrand");
+            int ENGINE_METHOD_RAND = 0x0008;
+            if(rdrandEngine != null) {
+                int rc = OpensslNativeJna.ENGINE_init(rdrandEngine);
+                
+                if(rc != 0) {
+                    int rc2 = OpensslNativeJna.ENGINE_set_default(rdrandEngine, ENGINE_METHOD_RAND);
+                    if(rc2 != 0) {
+                        rdrandLoaded = true;
+                    }
+                }
+            }
+            
+        } catch (Exception e) {
+            throw new NoSuchAlgorithmException();
+        }
+        
+        rdrandEnabled = rdrandLoaded;
+        
+        if(!rdrandLoaded) {
+            closeRdrandEngine();
+        }
+    }
+
+    /**
+     * 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) {
+        
+        synchronized (OpenSslJnaCryptoRandom.class) {
+            //this method is synchronized for now
+            //to support multithreading https://wiki.openssl.org/index.php/Manual:Threads(3) needs to be done
+            
+            if(rdrandEnabled && OpensslNativeJna.RAND_get_rand_method().equals(OpensslNativeJna.RAND_SSLeay())) {
+                close();
+                throw new RuntimeException("rdrand should be used but default is detected");
+            }
+            
+            ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
+            int retVal = OpensslNativeJna.RAND_bytes(buf, bytes.length);
+            throwOnError(retVal);
+            buf.rewind();
+            buf.get(bytes,0, bytes.length);
+        }
+    }
+
+    /**
+     * Overrides {@link OpenSslJnaCryptoRandom}. For {@link OpenSslJnaCryptoRandom},
+     * we don't need to set seed.
+     *
+     * @param seed the initial seed.
+     */
+    @Override
+    public void setSeed(long seed) {
+        // Self-seeding.
+    }
+
+    /**
+     * Overrides 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
+     *        {@literal <=} <code>numBits</code> {@literal <=} 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() {
+        closeRdrandEngine();
+        OpensslNativeJna.ENGINE_cleanup();
+        
+        //cleanup locks
+        //OpensslNativeJna.CRYPTO_set_locking_callback(null);
+        //LOCK.unlock();
+    }
+    
+    private void closeRdrandEngine() {
+        
+        if(rdrandEngine != null) {
+            OpensslNativeJna.ENGINE_finish(rdrandEngine);
+            OpensslNativeJna.ENGINE_free(rdrandEngine);
+        }
+    }
+
+    /**
+     * Checks if rdrand engine is used to retrieve random bytes
+     * 
+     * @return true if rdrand is used, false if default engine is used
+     */
+    public boolean isRdrandEnabled() {
+        return rdrandEnabled;
+    }
+    
+    private void throwOnError(int retVal) {  
+        if(retVal != 1) {
+            NativeLong err = OpensslNativeJna.ERR_peek_error();
+            String errdesc = OpensslNativeJna.ERR_error_string(err, null);
+            close();
+            throw new RuntimeException("return code "+retVal+" from openssl. Err code is "+err+": "+errdesc);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/859bdaa0/src/main/java/org/apache/commons/crypto/jna/OpensslJna.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/jna/OpensslJna.java b/src/main/java/org/apache/commons/crypto/jna/OpensslJna.java
index e27f96c..2d255ab 100644
--- a/src/main/java/org/apache/commons/crypto/jna/OpensslJna.java
+++ b/src/main/java/org/apache/commons/crypto/jna/OpensslJna.java
@@ -26,10 +26,10 @@ import org.apache.commons.crypto.random.CryptoRandom;
 public final class OpensslJna {
 
     public static Class<? extends CryptoCipher> getCipherClass() {
-        return OpensslJnaCipher.class;
+        return OpenSslJnaCipher.class;
     }
 
     public static Class<? extends CryptoRandom> getRandomClass() {
-        return OpensslJnaCryptoRandom.class;
+        return OpenSslJnaCryptoRandom.class;
     }
 }