You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2016/06/28 12:35:34 UTC

commons-crypto git commit: CRYPTO-63 Add JNA binding

Repository: commons-crypto
Updated Branches:
  refs/heads/master 2a585651d -> b6006e563


CRYPTO-63 Add JNA binding

Adapted from PR https://github.com/apache/commons-crypto/pull/47
Made classes package-protected
TODO benchmarking

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

Branch: refs/heads/master
Commit: b6006e563f63c745c964a87fae459200ae24573b
Parents: 2a58565
Author: Sebb <se...@apache.org>
Authored: Tue Jun 28 13:35:30 2016 +0100
Committer: Sebb <se...@apache.org>
Committed: Tue Jun 28 13:35:30 2016 +0100

----------------------------------------------------------------------
 pom.xml                                         |   6 +
 .../commons/crypto/jna/OpensslJnaCipher.java    | 331 +++++++++++++++++++
 .../crypto/jna/OpensslJnaCryptoRandom.java      | 190 +++++++++++
 .../commons/crypto/jna/OpensslNativeJna.java    |  99 ++++++
 .../apache/commons/crypto/jna/package-info.java |  22 ++
 .../crypto/cipher/AbstractCipherTest.java       |  11 +-
 .../crypto/jna/OpensslJnaCipherTest.java        |  35 ++
 .../crypto/jna/OpensslJnaCryptoRandomTest.java  |  47 +++
 .../crypto/stream/AbstractCipherStreamTest.java |  13 +
 .../stream/PositionedCryptoInputStreamTest.java |   1 +
 10 files changed, 753 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 6b55286..a549564 100644
--- a/pom.xml
+++ b/pom.xml
@@ -189,6 +189,7 @@ The following provides more details on the included cryptographic software:
     <commons.release.version>1.0.0</commons.release.version>
     <commons.release.desc>(Requires Java ${maven.compiler.target} or later)</commons.release.desc>
     <commons.rc.version>RC1</commons.rc.version>
+    <jna.version>4.2.2</jna.version>
 
     <!-- properties not related to versioning -->
     <commons.jira.id>CRYPTO</commons.jira.id>
@@ -535,5 +536,10 @@ The following provides more details on the included cryptographic software:
       <version>${junit.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>net.java.dev.jna</groupId>
+      <artifactId>jna</artifactId>
+      <version>${jna.version}</version>
+      </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/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..c3ac372
--- /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.
+ */
+public 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 #ENCRYPT_MODE} or {@link #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/b6006e56/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..7915d86
--- /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/b6006e56/src/main/java/org/apache/commons/crypto/jna/OpensslNativeJna.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/jna/OpensslNativeJna.java b/src/main/java/org/apache/commons/crypto/jna/OpensslNativeJna.java
new file mode 100644
index 0000000..5ffa25b
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/jna/OpensslNativeJna.java
@@ -0,0 +1,99 @@
+/**
+ * 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 com.sun.jna.Native;
+import com.sun.jna.NativeLong;
+import com.sun.jna.ptr.PointerByReference;
+
+class OpensslNativeJna {
+
+    static final int OPENSSL_INIT_ENGINE_RDRAND = 0x00000200;
+
+    static final int OOSL_JNA_ENCRYPT_MODE = 1;
+    static final int OOSL_JNA_DECRYPT_MODE = 0;
+
+    static {
+        Native.register("crypto");
+        ERR_load_crypto_strings();
+    }
+
+    //misc
+    public static native NativeLong SSLeay();
+    public static native String SSLeay_version(int type);
+    public static native void ERR_load_crypto_strings();
+    public static native NativeLong ERR_peek_error();
+    public static native String ERR_error_string(NativeLong err, char[] null_);
+    //String ERR_lib_error_string(NativeLong err);
+    //String ERR_func_error_string(NativeLong err);
+    //String ERR_reason_error_string(NativeLong err);
+    
+    //en-/decryption
+    public static native PointerByReference EVP_CIPHER_CTX_new();
+    public static native void EVP_CIPHER_CTX_init(PointerByReference p);
+    public static native int EVP_CIPHER_CTX_set_padding(PointerByReference c, int pad);
+    public static native PointerByReference EVP_aes_128_cbc();
+    public static native PointerByReference EVP_aes_128_ctr();
+    public static native PointerByReference EVP_aes_192_cbc();
+    public static native PointerByReference EVP_aes_192_ctr();
+    public static native PointerByReference EVP_aes_256_cbc();
+    public static native PointerByReference EVP_aes_256_ctr();
+    public static native int EVP_CipherInit_ex(PointerByReference ctx, PointerByReference cipher, PointerByReference impl, byte key[], byte iv[], int enc);
+    public static native int EVP_CipherUpdate(PointerByReference ctx, ByteBuffer bout, int[] outl, ByteBuffer in, int inl);
+    public static native int EVP_CipherFinal_ex(PointerByReference ctx, ByteBuffer bout, int[] outl);   
+    public static native void EVP_CIPHER_CTX_free(PointerByReference c);
+    public static native void EVP_CIPHER_CTX_cleanup(PointerByReference c);
+    
+    //Random generator
+    public static native PointerByReference RAND_get_rand_method();
+    public static native PointerByReference RAND_SSLeay();
+    public static native int RAND_bytes(ByteBuffer buf, int num);
+    public static native int ENGINE_finish(PointerByReference e);
+    public static native int ENGINE_free(PointerByReference e);
+    public static native int ENGINE_cleanup();
+    public static native int ENGINE_init(PointerByReference e);
+    public static native int ENGINE_set_default(PointerByReference e, int flags);
+    public static native PointerByReference ENGINE_by_id(String id);
+    public static native void ENGINE_load_rdrand();
+    
+    //TODO callback multithreading
+    /*public interface Id_function_cb extends Callback {
+        long invoke ();
+    }
+   
+    public interface Locking_function_cb extends Callback {
+        void invoke(int mode, int n, String file, int line);
+    }
+    
+    public static final Id_function_cb default_id_function = new Id_function_cb() {
+        
+        @Override
+        public long invoke() {
+            //id always positive
+            long id = Thread.currentThread().getId();
+            return id;
+        }
+    };
+    
+    int CRYPTO_num_locks();
+    void CRYPTO_set_id_callback(Id_function_cb id_function);
+    void CRYPTO_set_locking_callback(Locking_function_cb locking_function);*/
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/main/java/org/apache/commons/crypto/jna/package-info.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/crypto/jna/package-info.java b/src/main/java/org/apache/commons/crypto/jna/package-info.java
new file mode 100644
index 0000000..9254491
--- /dev/null
+++ b/src/main/java/org/apache/commons/crypto/jna/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.
+ */
+/**
+ * JNA classes
+ */
+package org.apache.commons.crypto.jna;
+

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java b/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
index aa95db0..2f79335 100644
--- a/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
+++ b/src/test/java/org/apache/commons/crypto/cipher/AbstractCipherTest.java
@@ -17,6 +17,8 @@
  */
 package org.apache.commons.crypto.cipher;
 
+import static org.junit.Assert.assertNotNull;
+
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
@@ -29,6 +31,7 @@ import javax.crypto.spec.SecretKeySpec;
 import javax.xml.bind.DatatypeConverter;
 
 import org.apache.commons.crypto.conf.ConfigurationKeys;
+import org.apache.commons.crypto.jna.OpensslJnaCipher;
 import org.apache.commons.crypto.utils.ReflectionUtils;
 import org.apache.commons.crypto.utils.Utils;
 import org.junit.Assert;
@@ -41,12 +44,15 @@ public abstract class AbstractCipherTest {
 
     public static final String JCE_CIPHER_CLASSNAME = JceCipher.class.getName();
 
+    public static final String OPENSSLJNA_CIPHER_CLASSNAME = OpensslJnaCipher.class.getName();
+
     // data
     public static final int BYTEBUFFER_SIZE = 1000;
+
     public String[] cipherTests = null;
     Properties props = null;
-    String cipherClass = null;
-    String[] transformations = null;
+    protected String cipherClass = null;
+    protected String[] transformations = null;
 
     // cipher
     static final byte[] KEY = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
@@ -72,6 +78,7 @@ public abstract class AbstractCipherTest {
         for (String tran : transformations) {
             /** uses the small data set in {@link TestData} */
             cipherTests = TestData.getTestData(tran);
+            assertNotNull(tran, cipherTests);
             for (int i = 0; i != cipherTests.length; i += 5) {
                 byte[] key = DatatypeConverter
                         .parseHexBinary(cipherTests[i + 1]);

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCipherTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCipherTest.java b/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCipherTest.java
new file mode 100644
index 0000000..8416484
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCipherTest.java
@@ -0,0 +1,35 @@
+/**
+ * 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 org.apache.commons.crypto.cipher.AbstractCipherTest;
+import org.apache.commons.crypto.jna.OpensslJnaCipher;
+
+public class OpensslJnaCipherTest extends AbstractCipherTest {
+
+    @Override
+    public void init() {
+        transformations = new String[] {
+                "AES/CBC/NoPadding",
+                "AES/CBC/PKCS5Padding",
+                "AES/CTR/NoPadding"
+                };
+        cipherClass = OpensslJnaCipher.class.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCryptoRandomTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCryptoRandomTest.java b/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCryptoRandomTest.java
new file mode 100644
index 0000000..455bc28
--- /dev/null
+++ b/src/test/java/org/apache/commons/crypto/jna/OpensslJnaCryptoRandomTest.java
@@ -0,0 +1,47 @@
+/**
+ * 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.security.GeneralSecurityException;
+import java.util.Properties;
+
+import org.apache.commons.crypto.conf.ConfigurationKeys;
+import org.apache.commons.crypto.jna.OpensslJnaCryptoRandom;
+import org.apache.commons.crypto.random.AbstractRandomTest;
+import org.apache.commons.crypto.random.CryptoRandom;
+import org.apache.commons.crypto.random.CryptoRandomFactory;
+
+import static junit.framework.Assert.fail;
+
+public class OpensslJnaCryptoRandomTest extends AbstractRandomTest {
+
+    @Override
+    public CryptoRandom getCryptoRandom() throws GeneralSecurityException {
+        Properties props = new Properties();
+        props.setProperty(
+                ConfigurationKeys.SECURE_RANDOM_CLASSES_KEY,
+                OpensslJnaCryptoRandom.class.getName());
+        CryptoRandom random = CryptoRandomFactory.getCryptoRandom(props);
+        if (!(random instanceof OpensslJnaCryptoRandom)) {
+            fail("The CryptoRandom should be: "
+                    + OpensslJnaCryptoRandom.class.getName());
+        }
+        return random;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
index 34274e4..25b8e95 100644
--- a/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
+++ b/src/test/java/org/apache/commons/crypto/stream/AbstractCipherStreamTest.java
@@ -71,9 +71,11 @@ public abstract class AbstractCipherStreamTest {
     public void testSkip() throws Exception {
         doSkipTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, false);
         doSkipTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, false);
+        doSkipTest(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, false);
 
         doSkipTest(AbstractCipherTest.JCE_CIPHER_CLASSNAME, true);
         doSkipTest(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, true);
+        doSkipTest(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, true);
     }
 
     /** Test byte buffer read with different buffer size. */
@@ -81,9 +83,11 @@ public abstract class AbstractCipherStreamTest {
     public void testByteBufferRead() throws Exception {
         doByteBufferRead(AbstractCipherTest.JCE_CIPHER_CLASSNAME, false);
         doByteBufferRead(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, false);
+        doByteBufferRead(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, false);
 
         doByteBufferRead(AbstractCipherTest.JCE_CIPHER_CLASSNAME, true);
         doByteBufferRead(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, true);
+        doByteBufferRead(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, true);
     }
 
     /** Test byte buffer write. */
@@ -92,9 +96,11 @@ public abstract class AbstractCipherStreamTest {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         doByteBufferWrite(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, false);
         doByteBufferWrite(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, false);
+        doByteBufferWrite(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, baos, false);
 
         doByteBufferWrite(AbstractCipherTest.JCE_CIPHER_CLASSNAME, baos, true);
         doByteBufferWrite(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, baos, true);
+        doByteBufferWrite(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, baos, true);
     }
 
     private void doSkipTest(String cipherClass, boolean withChannel)
@@ -308,18 +314,25 @@ public abstract class AbstractCipherStreamTest {
     public void testReadWrite() throws Exception {
         doReadWriteTest(0, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
         doReadWriteTest(0, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(0, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
         // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff
         for (int i = 0; i < 8; i++) {
             iv[8 + i] = (byte) 0xff;
         }
         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.JCE_CIPHER_CLASSNAME, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, iv);
         doReadWriteTest(count, AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
+        doReadWriteTest(count, AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME, AbstractCipherTest.JCE_CIPHER_CLASSNAME, iv);
     }
 
     private void doReadWriteTest(int count, String encCipherClass,

http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/b6006e56/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java b/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
index 758c095..1e39edb 100644
--- a/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
+++ b/src/test/java/org/apache/commons/crypto/stream/PositionedCryptoInputStreamTest.java
@@ -97,6 +97,7 @@ public class PositionedCryptoInputStreamTest {
     public void doTest() throws Exception {
         testCipher(AbstractCipherTest.JCE_CIPHER_CLASSNAME);
         testCipher(AbstractCipherTest.OPENSSL_CIPHER_CLASSNAME);
+        testCipher(AbstractCipherTest.OPENSSLJNA_CIPHER_CLASSNAME);
     }
 
     private void testCipher(String cipherClass) throws Exception {