Repository: google/conscrypt
Issue: isAssignableFrom checks in KeyFactorySpi.engineGetKeySpec appear to be backwards

I would like to report a minor bug in Conscrypt's KeyFactorySpi.engineGetKeySpec implementations. This bug appears to impact both [OpenSSLRSAKeyFactory](https://github.com/google/conscrypt/blob/6e29d195147e73f824ae3a7576f791eb4bb165df/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java#L77-L181) and [OpenSSLECKeyFactory](https://github.com/google/conscrypt/blob/6e29d195147e73f824ae3a7576f791eb4bb165df/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java#L76-L145). Specifically, the "isAssignableFrom" checks in "engineGetKeySpec" appear to be backwards. The intent of these checks is to ensure that the KeySpec implementation understood by the KeyFactorySpi implements or extends the requested KeySpec and thus can be safely cast to it. Instead, it incorrectly checks to see if the requested KeySpec implements or extends the concrete implementation. The vast majority of the time the requested KeySpec is equal to the concrete implementation so the inversion does not matter.

There are two user/developer facing implications of these bugs.

* If a caller requests a subclass of the known KeySpec implementation, then the OpenJDK engineGetKeySpec implementations will incorrectly throw a "ClassCastException" rather than an "InvalidKeySpec".
* If a caller requests a RSAPrivateKeySpec for an RSAPrivateCrtKey, then sun.security.rsa.RSAKeyFactory.engineGetKeySpec will return an instance of RSAPrivateKeySpec rather than opportunistically returning the subclass RSAPrivateCrtKeySpec (as appears to be the intention). This means that the CRT parameters will be lost and any keys created from the returned KeySpec will lack the CRT parameters and be less efficient.


Both of these behaviors can be seen for the RSAKeyFactory in the following sample code:
```
import org.conscrypt.OpenSSLProvider;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.spec.*;

public class BadSpecChecks {
    public static void main(String[] args) throws Exception {
        Security.insertProviderAt(new OpenSSLProvider(), 1);
        KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
        kg.initialize(2048);
        KeyPair pair = kg.generateKeyPair();

        KeyFactory factory = KeyFactory.getInstance("RSA");

        System.out.println("Since RSAPrivateCrtKeySpec inherits from RSAPrivateKeySpec, we'd expect this next line to return an instance of RSAPrivateKeySpec (because the private key has CRT parts). It doesn't.");
        KeySpec spec = factory.getKeySpec(pair.getPrivate(), RSAPrivateKeySpec.class);
        System.out.println(spec.getClass());

        System.out.println("This next line should give an InvalidKeySpec exception");
        try {
            FakeX509Spec oddSpec = factory.getKeySpec(pair.getPublic(), FakeX509Spec.class);
            System.out.println("This line shouldn't be reached.");
        } catch (final InvalidKeySpecException ex) {
            System.out.println("Caught expected exception");
            ex.printStackTrace(System.out);
        }
    }

    public static class FakeX509Spec extends X509EncodedKeySpec {
        public FakeX509Spec(byte[] encodedKey) {
            super(encodedKey);
        }

        public FakeX509Spec(byte[] encodedKey, String algorithm) {
            super(encodedKey, algorithm);
        }
    }
}
```

Please note that this bug is equivalent to [JDK-8254717](https://bugs.openjdk.java.net/browse/JDK-8254717)

Changed files:
common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java, common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java, common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java, common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java

diff --git a/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
index 596e8711..b10e3391 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
@@ -87,12 +87,12 @@ public final class OpenSSLECKeyFactory extends KeyFactorySpi {
             throw new InvalidKeySpecException("Key must be an EC key");
         }
 
-        if (key instanceof ECPublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+        if (key instanceof ECPublicKey && keySpec.isAssignableFrom(ECPublicKeySpec.class)) {
             ECPublicKey ecKey = (ECPublicKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
             return result;
-        } else if (key instanceof PublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PublicKey && keySpec.isAssignableFrom(ECPublicKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"X.509".equals(key.getFormat()) || encoded == null) {
                 throw new InvalidKeySpecException("Not a valid X.509 encoding");
@@ -102,12 +102,12 @@ public final class OpenSSLECKeyFactory extends KeyFactorySpi {
             T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
             return result;
         } else if (key instanceof ECPrivateKey
-                && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(ECPrivateKeySpec.class)) {
             ECPrivateKey ecKey = (ECPrivateKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
             return result;
-        } else if (key instanceof PrivateKey && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PrivateKey && keySpec.isAssignableFrom(ECPrivateKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
                 throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
@@ -118,7 +118,7 @@ public final class OpenSSLECKeyFactory extends KeyFactorySpi {
             T result = (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
             return result;
         } else if (key instanceof PrivateKey
-                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"PKCS#8".equals(key.getFormat())) {
                 throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
@@ -128,7 +128,7 @@ public final class OpenSSLECKeyFactory extends KeyFactorySpi {
             }
             @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded);
             return result;
-        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PublicKey && keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"X.509".equals(key.getFormat())) {
                 throw new InvalidKeySpecException("Encoding type must be X.509; was "
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
index ce08d50e..073821e0 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
@@ -88,12 +88,12 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
             throw new InvalidKeySpecException("Key must be a RSA key");
         }
 
-        if (key instanceof RSAPublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+        if (key instanceof RSAPublicKey && keySpec.isAssignableFrom(RSAPublicKeySpec.class)) {
             RSAPublicKey rsaKey = (RSAPublicKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
             return result;
-        } else if (key instanceof PublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PublicKey && keySpec.isAssignableFrom(RSAPublicKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"X.509".equals(key.getFormat()) || encoded == null) {
                 throw new InvalidKeySpecException("Not a valid X.509 encoding");
@@ -104,7 +104,7 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
             T result = (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
             return result;
         } else if (key instanceof RSAPrivateCrtKey
-                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(RSAPrivateCrtKeySpec.class)) {
             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new RSAPrivateCrtKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent(),
@@ -113,19 +113,19 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
                     rsaKey.getCrtCoefficient());
             return result;
         } else if (key instanceof RSAPrivateCrtKey
-                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(RSAPrivateKeySpec.class)) {
             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
             return result;
         } else if (key instanceof RSAPrivateKey
-                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(RSAPrivateKeySpec.class)) {
             RSAPrivateKey rsaKey = (RSAPrivateKey) key;
             @SuppressWarnings("unchecked")
             T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
             return result;
         } else if (key instanceof PrivateKey
-                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(RSAPrivateCrtKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
                 throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
@@ -143,7 +143,7 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
             } else {
                 throw new InvalidKeySpecException("Encoded key is not an RSAPrivateCrtKey");
             }
-        } else if (key instanceof PrivateKey && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PrivateKey && keySpec.isAssignableFrom(RSAPrivateKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
                 throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
@@ -154,7 +154,7 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
             T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
             return result;
         } else if (key instanceof PrivateKey
-                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+                && keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"PKCS#8".equals(key.getFormat())) {
                 throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
@@ -164,7 +164,7 @@ public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
             }
             @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded);
             return result;
-        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+        } else if (key instanceof PublicKey && keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
             final byte[] encoded = key.getEncoded();
             if (!"X.509".equals(key.getFormat())) {
                 throw new InvalidKeySpecException("Encoding type must be X.509; was "
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java
index ee8fd515..dab3852e 100644
--- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java
+++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java
@@ -15,54 +15,129 @@
  */
 package org.conscrypt.java.security;
 
+import static org.junit.Assert.fail;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
 import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
 import java.util.List;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
-public class KeyFactoryTestEC extends
-    AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> {
-
-  public KeyFactoryTestEC() {
-    super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class);
-  }
-
-  @Override
-  public ServiceTester customizeTester(ServiceTester tester) {
-    // BC's EC keys always use explicit params, even though it's a bad idea, and we don't support
-    // those, so don't test BC keys
-    return tester.skipProvider("BC");
-  }
-
-  @Override
-  protected void check(KeyPair keyPair) throws Exception {
-    new SignatureHelper("SHA256withECDSA").test(keyPair);
-  }
-
-  @Override
-  protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
-    return Arrays.asList(
-        new KeyPair(
-            DefaultKeys.getPublicKey("EC"),
-            DefaultKeys.getPrivateKey("EC")
-        ),
-        new KeyPair(
-            new TestPublicKey(DefaultKeys.getPublicKey("EC")),
-            new TestPrivateKey(DefaultKeys.getPrivateKey("EC"))
-        ),
-        new KeyPair(
-            new TestECPublicKey((ECPublicKey)DefaultKeys.getPublicKey("EC")),
-            new TestECPrivateKey((ECPrivateKey)DefaultKeys.getPrivateKey("EC"))
-        )
-    );
-  }
+public class KeyFactoryTestEC extends AbstractKeyFactoryTest<ECPublicKeySpec, ECPrivateKeySpec> {
+    public KeyFactoryTestEC() {
+        super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class);
+    }
+
+    @Override
+    public ServiceTester customizeTester(ServiceTester tester) {
+        // BC's EC keys always use explicit params, even though it's a bad idea, and we don't
+        // support those, so don't test BC keys
+        return tester.skipProvider("BC");
+    }
+
+    @Override
+    protected void check(KeyPair keyPair) throws Exception {
+        new SignatureHelper("SHA256withECDSA").test(keyPair);
+    }
+
+    @Override
+    protected List<KeyPair> getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
+        return Arrays.asList(
+                new KeyPair(DefaultKeys.getPublicKey("EC"), DefaultKeys.getPrivateKey("EC")),
+                new KeyPair(new TestPublicKey(DefaultKeys.getPublicKey("EC")),
+                        new TestPrivateKey(DefaultKeys.getPrivateKey("EC"))),
+                new KeyPair(new TestECPublicKey((ECPublicKey) DefaultKeys.getPublicKey("EC")),
+                        new TestECPrivateKey((ECPrivateKey) DefaultKeys.getPrivateKey("EC"))));
+    }
+
+    @Test
+    public void shouldThrowInvalidKeySpecException_whenKeySpecIsOdd() throws Exception {
+        Provider p = Security.getProvider(StandardNames.JSSE_PROVIDER_NAME);
+        final KeyFactory factory = KeyFactory.getInstance("EC", p);
+
+        try {
+            factory.getKeySpec(new TestECPublicKey((ECPublicKey) DefaultKeys.getPublicKey("EC")),
+                    FakeECPublicKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPublicKey("EC"), FakeECPublicKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(new TestECPrivateKey((ECPrivateKey) DefaultKeys.getPrivateKey("EC")),
+                    FakeECPrivateKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPrivateKey("EC"), FakeECPrivateKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPrivateKey("EC"), FakePKCS8.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPublicKey("EC"), FakeX509.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+    }
+
+    private static class FakeECPublicKeySpec extends ECPublicKeySpec {
+        public FakeECPublicKeySpec(ECPoint w, ECParameterSpec params) {
+            super(w, params);
+        }
+    }
+
+    private static class FakeECPrivateKeySpec extends ECPrivateKeySpec {
+        public FakeECPrivateKeySpec(BigInteger s, ECParameterSpec params) {
+            super(s, params);
+        }
+    }
+
+    private static class FakePKCS8 extends PKCS8EncodedKeySpec {
+        public FakePKCS8(byte[] encodedKey) {
+            super(encodedKey);
+        }
+    }
+
+    private static class FakeX509 extends X509EncodedKeySpec {
+        public FakeX509(byte[] encodedKey) {
+            super(encodedKey);
+        }
+    }
 }
diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
index c8fc2da0..76608ad5 100644
--- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
+++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java
@@ -17,12 +17,15 @@ package org.conscrypt.java.security;
 
 import static org.junit.Assert.fail;
 
+import java.math.BigInteger;
 import java.security.KeyFactory;
 import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.PrivateKey;
 import java.security.Provider;
 import java.security.PublicKey;
 import java.security.Security;
+import java.security.interfaces.RSAPrivateCrtKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
@@ -34,9 +37,7 @@ import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
-public class KeyFactoryTestRSA extends
-        AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> {
-
+public class KeyFactoryTestRSA extends AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> {
     public KeyFactoryTestRSA() {
         super("RSA", RSAPublicKeySpec.class, RSAPrivateKeySpec.class);
     }
@@ -65,13 +66,13 @@ public class KeyFactoryTestRSA extends
     }
 
     @Test
-    public void testInvalidKeySpec() throws Exception {
+    public void shouldThrowInvalidKeySpec_whenKeyIsInvalid() throws Exception {
         Provider p = Security.getProvider(StandardNames.JSSE_PROVIDER_NAME);
         final KeyFactory factory = KeyFactory.getInstance("RSA", p);
 
         try {
             factory.getKeySpec(new TestPrivateKey(DefaultKeys.getPrivateKey("RSA"), "Invalid"),
-                RSAPrivateKeySpec.class);
+                    RSAPrivateKeySpec.class);
             fail();
         } catch (InvalidKeySpecException e) {
             // expected
@@ -79,7 +80,7 @@ public class KeyFactoryTestRSA extends
 
         try {
             factory.getKeySpec(new TestPrivateKey(DefaultKeys.getPrivateKey("RSA"), "Invalid"),
-                RSAPrivateCrtKeySpec.class);
+                    RSAPrivateCrtKeySpec.class);
             fail();
         } catch (InvalidKeySpecException e) {
             // expected
@@ -87,10 +88,86 @@ public class KeyFactoryTestRSA extends
 
         try {
             factory.getKeySpec(new TestPublicKey(DefaultKeys.getPublicKey("RSA"), "Invalid"),
-                RSAPublicKeySpec.class);
+                    RSAPublicKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void shouldThrowInvalidKeySpec_whenKeySpecIsOdd() throws Exception {
+        Provider p = Security.getProvider(StandardNames.JSSE_PROVIDER_NAME);
+        final KeyFactory factory = KeyFactory.getInstance("RSA", p);
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPublicKey("RSA"), FakeRSAPublicKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(generateRsaKey(), FakeRSAPrivateCrtKeySpec.class);
             fail();
         } catch (InvalidKeySpecException e) {
             // expected
         }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPrivateKey("RSA"), FakeRSAPrivateCrtKeySpec.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPrivateKey("RSA"), FakePKCS8.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+
+        try {
+            factory.getKeySpec(DefaultKeys.getPublicKey("RSA"), FakeX509.class);
+            fail();
+        } catch (InvalidKeySpecException e) {
+            // expected
+        }
+    }
+
+    private static RSAPrivateCrtKey generateRsaKey() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair keyPair = kpg.generateKeyPair();
+        return (RSAPrivateCrtKey) keyPair.getPrivate();
+    }
+
+    private static class FakeRSAPublicKeySpec extends RSAPublicKeySpec {
+        public FakeRSAPublicKeySpec(BigInteger modulus, BigInteger publicExponent) {
+            super(modulus, publicExponent);
+        }
+    }
+
+    private static class FakePKCS8 extends PKCS8EncodedKeySpec {
+        public FakePKCS8(byte[] encodedKey) {
+            super(encodedKey);
+        }
+    }
+
+    private static class FakeRSAPrivateCrtKeySpec extends RSAPrivateCrtKeySpec {
+        public FakeRSAPrivateCrtKeySpec(BigInteger modulus, BigInteger publicExponent,
+                BigInteger privateExponent, BigInteger primeP, BigInteger primeQ,
+                BigInteger primeExponentP, BigInteger primeExponentQ, BigInteger crtCoefficient) {
+            super(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP,
+                    primeExponentQ, crtCoefficient);
+        }
+    }
+
+    private static class FakeX509 extends X509EncodedKeySpec {
+        public FakeX509(byte[] encodedKey) {
+            super(encodedKey);
+        }
     }
 }