am 13bf4477: Merge "NativeCrypto: be more tolerant during translateKey"

* commit '13bf44777fbbfa387b25da07b4779347c273eb53':
  NativeCrypto: be more tolerant during translateKey
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java
index 2bf6f57..13c7ef8 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAKeyFactory.java
@@ -36,20 +36,14 @@
 
     @Override
     protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof DSAPublicKeySpec) {
-            DSAPublicKeySpec dsaKeySpec = (DSAPublicKeySpec) keySpec;
-
-            return new OpenSSLDSAPublicKey(dsaKeySpec);
+            return new OpenSSLDSAPublicKey((DSAPublicKeySpec) keySpec);
         } else if (keySpec instanceof X509EncodedKeySpec) {
-            X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
-                return new OpenSSLDSAPublicKey(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeCrypto.EVP_PKEY_DSA);
         }
         throw new InvalidKeySpecException("Must use DSAPublicKeySpec or X509EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -57,20 +51,15 @@
 
     @Override
     protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof DSAPrivateKeySpec) {
-            DSAPrivateKeySpec dsaKeySpec = (DSAPrivateKeySpec) keySpec;
-
-            return new OpenSSLDSAPrivateKey(dsaKeySpec);
+            return new OpenSSLDSAPrivateKey((DSAPrivateKeySpec) keySpec);
         } else if (keySpec instanceof PKCS8EncodedKeySpec) {
-            PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
-                return new OpenSSLDSAPrivateKey(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+                    NativeCrypto.EVP_PKEY_DSA);
         }
         throw new InvalidKeySpecException("Must use DSAPrivateKeySpec or PKCS8EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -87,46 +76,63 @@
             throw new InvalidKeySpecException("keySpec == null");
         }
 
-        if (key instanceof DSAPublicKey) {
+        if (!"DSA".equals(key.getAlgorithm())) {
+            throw new InvalidKeySpecException("Key must be a DSA key");
+        }
+
+        if (key instanceof DSAPublicKey && DSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
             DSAPublicKey dsaKey = (DSAPublicKey) key;
-
-            if (DSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger y = dsaKey.getY();
-
-                DSAParams params = dsaKey.getParams();
-                BigInteger p = params.getP();
-                BigInteger q = params.getQ();
-                BigInteger g = params.getG();
-
-                return (T) new DSAPublicKeySpec(y, p, q, g);
-            } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new X509EncodedKeySpec(key.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be DSAPublicKeySpec or X509EncodedKeySpec; was " + keySpec.getName());
+            DSAParams params = dsaKey.getParams();
+            return (T) new DSAPublicKeySpec(dsaKey.getY(), params.getP(), params.getQ(),
+                    params.getG());
+        } else if (key instanceof PublicKey && DSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid X.509 encoding");
             }
-        } else if (key instanceof DSAPrivateKey) {
+            DSAPublicKey dsaKey =
+                    (DSAPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            DSAParams params = dsaKey.getParams();
+            return (T) new DSAPublicKeySpec(dsaKey.getY(), params.getP(), params.getQ(),
+                    params.getG());
+        } else if (key instanceof DSAPrivateKey
+                && DSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
             DSAPrivateKey dsaKey = (DSAPrivateKey) key;
-
-            if (DSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger x = dsaKey.getX();
-
-                DSAParams params = dsaKey.getParams();
-                BigInteger p = params.getP();
-                BigInteger q = params.getQ();
-                BigInteger g = params.getG();
-
-                return (T) new DSAPrivateKeySpec(x, p, q, g);
-            } else if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new PKCS8EncodedKeySpec(dsaKey.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be DSAPrivateKeySpec or PKCS8EncodedKeySpec; was "
-                                + keySpec.getName());
+            DSAParams params = dsaKey.getParams();
+            return (T) new DSAPrivateKeySpec(dsaKey.getX(), params.getP(), params.getQ(),
+                    params.getG());
+        } else if (key instanceof PrivateKey && DSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
             }
+            DSAPrivateKey dsaKey =
+                    (DSAPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            DSAParams params = dsaKey.getParams();
+            return (T) new DSAPrivateKeySpec(dsaKey.getX(), params.getP(), params.getQ(),
+                    params.getG());
+        } else if (key instanceof PrivateKey
+                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new PKCS8EncodedKeySpec(encoded);
+        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be X.509; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new X509EncodedKeySpec(encoded);
         } else {
-            throw new InvalidKeySpecException("Must be DSAPublicKey or DSAPrivateKey; was "
-                    + key.getClass().getName());
+            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
         }
     }
 
@@ -166,8 +172,20 @@
             } catch (InvalidKeySpecException e) {
                 throw new InvalidKeyException(e);
             }
+        } else if ("PKCS#8".equals(key.getFormat())) {
+            try {
+                return engineGeneratePrivate(new PKCS8EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ("X.509".equals(key.getFormat())) {
+            try {
+                return engineGeneratePublic(new X509EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
         } else {
-            throw new InvalidKeyException("Key must be DSAPublicKey or DSAPrivateKey; was "
+            throw new InvalidKeyException("Key must be DSA public or private key; was "
                     + key.getClass().getName());
         }
     }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECKeyFactory.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECKeyFactory.java
index e7fec4a..a4a43f9 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECKeyFactory.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECKeyFactory.java
@@ -37,20 +37,14 @@
 
     @Override
     protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof ECPublicKeySpec) {
-            ECPublicKeySpec ecKeySpec = (ECPublicKeySpec) keySpec;
-
-            return new OpenSSLECPublicKey(ecKeySpec);
+            return new OpenSSLECPublicKey((ECPublicKeySpec) keySpec);
         } else if (keySpec instanceof X509EncodedKeySpec) {
-            X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
-                return new OpenSSLECPublicKey(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeCrypto.EVP_PKEY_EC);
         }
         throw new InvalidKeySpecException("Must use ECPublicKeySpec or X509EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -58,20 +52,15 @@
 
     @Override
     protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof ECPrivateKeySpec) {
-            ECPrivateKeySpec ecKeySpec = (ECPrivateKeySpec) keySpec;
-
-            return new OpenSSLECPrivateKey(ecKeySpec);
+            return new OpenSSLECPrivateKey((ECPrivateKeySpec) keySpec);
         } else if (keySpec instanceof PKCS8EncodedKeySpec) {
-            PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
-                return new OpenSSLECPrivateKey(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+                    NativeCrypto.EVP_PKEY_EC);
         }
         throw new InvalidKeySpecException("Must use ECPrivateKeySpec or PKCS8EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -88,39 +77,54 @@
             throw new InvalidKeySpecException("keySpec == null");
         }
 
-        if (key instanceof ECPublicKey) {
+        if (!"EC".equals(key.getAlgorithm())) {
+            throw new InvalidKeySpecException("Key must be an EC key");
+        }
+
+        if (key instanceof ECPublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
             ECPublicKey ecKey = (ECPublicKey) key;
-
-            if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
-                ECParameterSpec params = ecKey.getParams();
-
-                ECPoint w = ecKey.getW();
-
-                return (T) new ECPublicKeySpec(w, params);
-            } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new X509EncodedKeySpec(key.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be ECPublicKeySpec or X509EncodedKeySpec; was " + keySpec.getName());
+            return (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
+        } else if (key instanceof PublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid X.509 encoding");
             }
-        } else if (key instanceof ECPrivateKey) {
+            ECPublicKey ecKey = (ECPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            return (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
+        } else if (key instanceof ECPrivateKey
+                && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
             ECPrivateKey ecKey = (ECPrivateKey) key;
-
-            if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
-                ECParameterSpec params = ecKey.getParams();
-
-                BigInteger s = ecKey.getS();
-
-                return (T) new ECPrivateKeySpec(s, params);
-            } else if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new PKCS8EncodedKeySpec(ecKey.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be ECPrivateKeySpec or PKCS8EncodedKeySpec; was " + keySpec.getName());
+            return (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
+        } else if (key instanceof PrivateKey && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
             }
+            ECPrivateKey ecKey =
+                    (ECPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            return (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
+        } else if (key instanceof PrivateKey
+                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new PKCS8EncodedKeySpec(encoded);
+        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be X.509; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new X509EncodedKeySpec(encoded);
         } else {
-            throw new InvalidKeySpecException("Must be ECPublicKey or ECPrivateKey; was "
-                    + key.getClass().getName());
+            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
         }
     }
 
@@ -154,8 +158,20 @@
             } catch (InvalidKeySpecException e) {
                 throw new InvalidKeyException(e);
             }
+        } else if ("PKCS#8".equals(key.getFormat())) {
+            try {
+                return engineGeneratePrivate(new PKCS8EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ("X.509".equals(key.getFormat())) {
+            try {
+                return engineGeneratePublic(new X509EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
         } else {
-            throw new InvalidKeyException("Key must be ECPublicKey or ECPrivateKey; was "
+            throw new InvalidKeyException("Key must be EC public or private key; was "
                     + key.getClass().getName());
         }
     }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPrivateKey.java
index 10ccf61..b0be998 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPrivateKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPrivateKey.java
@@ -51,8 +51,7 @@
 
     public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
         try {
-            OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecKeySpec
-                    .getParams());
+            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
             final BigInteger privKey = ecKeySpec.getS();
             key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0,
                     privKey.toByteArray()));
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPublicKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPublicKey.java
index 9b667ad..b591336 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPublicKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECPublicKey.java
@@ -52,7 +52,7 @@
 
     public OpenSSLECPublicKey(ECPublicKeySpec ecKeySpec) throws InvalidKeySpecException {
         try {
-            OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
+            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
             OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(
                     NativeCrypto.get_EC_GROUP_type(group.getContext()), group, ecKeySpec.getW());
             key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(),
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
index bdc06e9..921a4b6 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
@@ -19,6 +19,9 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 
 import javax.crypto.SecretKey;
 
@@ -75,6 +78,28 @@
         }
     }
 
+    static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type)
+            throws InvalidKeySpecException {
+        X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
+
+        final OpenSSLKey key;
+        try {
+            key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+
+        if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) {
+            throw new InvalidKeySpecException("Unexpected key type");
+        }
+
+        try {
+            return key.getPublicKey();
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
     public PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
             case NativeCrypto.EVP_PKEY_RSA:
@@ -88,6 +113,28 @@
         }
     }
 
+    static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)
+            throws InvalidKeySpecException {
+        PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
+
+        final OpenSSLKey key;
+        try {
+            key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+
+        if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) {
+            throw new InvalidKeySpecException("Unexpected key type");
+        }
+
+        try {
+            return key.getPrivateKey();
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
     public SecretKey getSecretKey(String algorithm) throws NoSuchAlgorithmException {
         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
             case NativeCrypto.EVP_PKEY_HMAC:
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java
index e8b0afa..fc6c561 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAKeyFactory.java
@@ -37,20 +37,14 @@
 
     @Override
     protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof RSAPublicKeySpec) {
-            RSAPublicKeySpec rsaKeySpec = (RSAPublicKeySpec) keySpec;
-
-            return new OpenSSLRSAPublicKey(rsaKeySpec);
+            return new OpenSSLRSAPublicKey((RSAPublicKeySpec) keySpec);
         } else if (keySpec instanceof X509EncodedKeySpec) {
-            X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
-                return new OpenSSLRSAPublicKey(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeCrypto.EVP_PKEY_RSA);
         }
         throw new InvalidKeySpecException("Must use RSAPublicKeySpec or X509EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -58,24 +52,17 @@
 
     @Override
     protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
         if (keySpec instanceof RSAPrivateCrtKeySpec) {
-            RSAPrivateCrtKeySpec rsaKeySpec = (RSAPrivateCrtKeySpec) keySpec;
-
-            return new OpenSSLRSAPrivateCrtKey(rsaKeySpec);
+            return new OpenSSLRSAPrivateCrtKey((RSAPrivateCrtKeySpec) keySpec);
         } else if (keySpec instanceof RSAPrivateKeySpec) {
-            RSAPrivateKeySpec rsaKeySpec = (RSAPrivateKeySpec) keySpec;
-
-            return new OpenSSLRSAPrivateKey(rsaKeySpec);
+            return new OpenSSLRSAPrivateKey((RSAPrivateKeySpec) keySpec);
         } else if (keySpec instanceof PKCS8EncodedKeySpec) {
-            PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec;
-
-            try {
-                final OpenSSLKey key = new OpenSSLKey(
-                        NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
-                return OpenSSLRSAPrivateKey.getInstance(key);
-            } catch (Exception e) {
-                throw new InvalidKeySpecException(e);
-            }
+            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+                    NativeCrypto.EVP_PKEY_RSA);
         }
         throw new InvalidKeySpecException("Must use RSAPublicKeySpec or PKCS8EncodedKeySpec; was "
                 + keySpec.getClass().getName());
@@ -92,57 +79,83 @@
             throw new InvalidKeySpecException("keySpec == null");
         }
 
-        if (key instanceof RSAPublicKey) {
+        if (!"RSA".equals(key.getAlgorithm())) {
+            throw new InvalidKeySpecException("Key must be a RSA key");
+        }
+
+        if (key instanceof RSAPublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
             RSAPublicKey rsaKey = (RSAPublicKey) key;
-
-            if (RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger modulus = rsaKey.getModulus();
-                BigInteger publicExponent = rsaKey.getPublicExponent();
-                return (T) new RSAPublicKeySpec(modulus, publicExponent);
-            } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new X509EncodedKeySpec(key.getEncoded());
-            } else {
-                throw new InvalidKeySpecException("Must be RSAPublicKeySpec or X509EncodedKeySpec");
+            return (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
+        } else if (key instanceof PublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid X.509 encoding");
             }
-        } else if (key instanceof RSAPrivateCrtKey) {
+            RSAPublicKey rsaKey =
+                    (RSAPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            return (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
+        } else if (key instanceof RSAPrivateCrtKey
+                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
-
-            if (RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger modulus = rsaKey.getModulus();
-                BigInteger publicExponent = rsaKey.getPublicExponent();
-                BigInteger privateExponent = rsaKey.getPrivateExponent();
-                BigInteger primeP = rsaKey.getPrimeP();
-                BigInteger primeQ = rsaKey.getPrimeQ();
-                BigInteger primeExponentP = rsaKey.getPrimeExponentP();
-                BigInteger primeExponentQ = rsaKey.getPrimeExponentQ();
-                BigInteger crtCoefficient = rsaKey.getCrtCoefficient();
-                return (T) new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent,
-                        primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
-            } else if (RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger modulus = rsaKey.getModulus();
-                BigInteger privateExponent = rsaKey.getPrivateExponent();
-                return (T) new RSAPrivateKeySpec(modulus, privateExponent);
-            } else if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new PKCS8EncodedKeySpec(rsaKey.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be RSAPrivateKeySpec or or RSAPrivateCrtKeySpec or PKCS8EncodedKeySpec");
-            }
-        } else if (key instanceof RSAPrivateKey) {
+            return (T) new RSAPrivateCrtKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent(),
+                    rsaKey.getPrivateExponent(), rsaKey.getPrimeP(), rsaKey.getPrimeQ(),
+                    rsaKey.getPrimeExponentP(), rsaKey.getPrimeExponentQ(),
+                    rsaKey.getCrtCoefficient());
+        } else if (key instanceof RSAPrivateCrtKey
+                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+            return (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+        } else if (key instanceof RSAPrivateKey
+                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
             RSAPrivateKey rsaKey = (RSAPrivateKey) key;
-
-            if (RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
-                BigInteger modulus = rsaKey.getModulus();
-                BigInteger privateExponent = rsaKey.getPrivateExponent();
-                return (T) new RSAPrivateKeySpec(modulus, privateExponent);
-            } else if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
-                return (T) new PKCS8EncodedKeySpec(rsaKey.getEncoded());
-            } else {
-                throw new InvalidKeySpecException(
-                        "Must be RSAPrivateKeySpec or PKCS8EncodedKeySpec");
+            return (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+        } else if (key instanceof PrivateKey
+                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
             }
+            RSAPrivateKey privKey =
+                    (RSAPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            if (privKey instanceof RSAPrivateCrtKey) {
+                RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) privKey;
+                return (T) new RSAPrivateCrtKeySpec(rsaKey.getModulus(),
+                        rsaKey.getPublicExponent(), rsaKey.getPrivateExponent(),
+                        rsaKey.getPrimeP(), rsaKey.getPrimeQ(), rsaKey.getPrimeExponentP(),
+                        rsaKey.getPrimeExponentQ(), rsaKey.getCrtCoefficient());
+            } else {
+                throw new InvalidKeySpecException("Encoded key is not an RSAPrivateCrtKey");
+            }
+        } else if (key instanceof PrivateKey && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
+            }
+            RSAPrivateKey rsaKey =
+                    (RSAPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            return (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+        } else if (key instanceof PrivateKey
+                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new PKCS8EncodedKeySpec(encoded);
+        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be X.509; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            return (T) new X509EncodedKeySpec(encoded);
         } else {
-            throw new InvalidKeySpecException("Must be RSAPublicKey or RSAPrivateKey");
+            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
         }
     }
 
@@ -189,9 +202,21 @@
             } catch (InvalidKeySpecException e) {
                 throw new InvalidKeyException(e);
             }
+        } else if ("PKCS#8".equals(key.getFormat())) {
+            try {
+                return engineGeneratePrivate(new PKCS8EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ("X.509".equals(key.getFormat())) {
+            try {
+                return engineGeneratePublic(new X509EncodedKeySpec(key.getEncoded()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
         } else {
-            throw new InvalidKeyException(
-                    "Key must be RSAPublicKey or RSAPrivateCrtKey or RSAPrivateKey");
+            throw new InvalidKeyException("Key must be an RSA public or private key; was "
+                    + key.getClass().getName());
         }
     }
 }