Switch OpenSSLECDHKeyAgreement to KeyFactory.translateKey.

OpenSSL KeyFactory.translateKey encapsulates all the functionality
for translating arbitrary Key instances to OpenSSL-backed Key
instances. Thus, there's no need to replicate that functionality
elsewhere.

(cherry picked from commit 0469e3a6a9b5e854b8b985039de8ba4f6e6037bd)

Change-Id: I4caa0021e51a83be6932617117275fd033b6d5f7
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index b24ad0e..dfc512f 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -206,6 +206,8 @@
 
     public static native byte[] EC_GROUP_get_order(long groupCtx);
 
+    public static native int EC_GROUP_get_degree(long groupCtx);
+
     public static native byte[] EC_GROUP_get_cofactor(long groupCtx);
 
     public static native long EC_POINT_new(long groupRef);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECDHKeyAgreement.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECDHKeyAgreement.java
index 096e300..5146156 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECDHKeyAgreement.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLECDHKeyAgreement.java
@@ -19,9 +19,10 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
 import java.security.spec.AlgorithmParameterSpec;
 
 import javax.crypto.KeyAgreementSpi;
@@ -54,36 +55,14 @@
         if (!lastPhase) {
             throw new IllegalStateException("ECDH only has one phase");
         }
+
         if (key == null) {
             throw new InvalidKeyException("key == null");
         }
-        if (!(key instanceof ECPublicKey)) {
-            throw new InvalidKeyException("This phase requires an ECPublicKey. Actual key type: "
-                + key.getClass());
+        if (!(key instanceof PublicKey)) {
+            throw new InvalidKeyException("Not a public key: " + key.getClass());
         }
-        ECPublicKey publicKey = (ECPublicKey) key;
-
-        OpenSSLKey openSslPublicKey;
-        if (publicKey instanceof OpenSSLECPublicKey) {
-            // OpenSSL-backed key
-            openSslPublicKey = ((OpenSSLECPublicKey) publicKey).getOpenSSLKey();
-        } else {
-            // Not an OpenSSL-backed key -- create an OpenSSL-backed key from its X.509 encoding
-            if (!"X.509".equals(publicKey.getFormat())) {
-                throw new InvalidKeyException("Non-OpenSSL public key (" + publicKey.getClass()
-                    + ") offers unsupported encoding format: " + publicKey.getFormat());
-            }
-            byte[] encoded = publicKey.getEncoded();
-            if (encoded == null) {
-                throw new InvalidKeyException("Non-OpenSSL public key (" + publicKey.getClass()
-                    + ") does not provide encoded form");
-            }
-            try {
-                openSslPublicKey = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(encoded));
-            } catch (Exception e) {
-                throw new InvalidKeyException("Failed to decode X.509 encoded public key", e);
-            }
-        }
+        OpenSSLKey openSslPublicKey = translateKeyToEcOpenSSLKey(key);
 
         byte[] buffer = new byte[mExpectedResultLength];
         int actualResultLength = NativeCrypto.ECDH_compute_key(
@@ -142,35 +121,15 @@
         if (key == null) {
             throw new InvalidKeyException("key == null");
         }
-        if (!(key instanceof ECPrivateKey)) {
-            throw new InvalidKeyException("Not an EC private key: " + key.getClass());
+        if (!(key instanceof PrivateKey)) {
+            throw new InvalidKeyException("Not a private key: " + key.getClass());
         }
-        ECPrivateKey privateKey = (ECPrivateKey) key;
-        mExpectedResultLength =
-                (privateKey.getParams().getCurve().getField().getFieldSize() + 7) / 8;
 
-        OpenSSLKey openSslPrivateKey;
-        if (privateKey instanceof OpenSSLECPrivateKey) {
-            // OpenSSL-backed key
-            openSslPrivateKey = ((OpenSSLECPrivateKey) privateKey).getOpenSSLKey();
-        } else {
-            // Not an OpenSSL-backed key -- create an OpenSSL-backed key from its PKCS#8 encoding
-            if (!"PKCS#8".equals(privateKey.getFormat())) {
-                throw new InvalidKeyException("Non-OpenSSL private key (" + privateKey.getClass()
-                    + ") offers unsupported encoding format: " + privateKey.getFormat());
-            }
-            byte[] encoded = privateKey.getEncoded();
-            if (encoded == null) {
-                throw new InvalidKeyException("Non-OpenSSL private key (" + privateKey.getClass()
-                    + ") does not provide encoded form");
-            }
-            try {
-                openSslPrivateKey = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
-            } catch (Exception e) {
-                throw new InvalidKeyException("Failed to decode PKCS#8 encoded private key", e);
-            }
-        }
-        mOpenSslPrivateKey = openSslPrivateKey;
+        OpenSSLKey openSslKey = translateKeyToEcOpenSSLKey(key);
+        int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(NativeCrypto.EC_KEY_get0_group(
+                openSslKey.getPkeyContext()));
+        mExpectedResultLength = (fieldSizeBits + 7) / 8;
+        mOpenSslPrivateKey = openSslKey;
     }
 
     @Override
@@ -188,4 +147,13 @@
             throw new IllegalStateException("Key agreement not completed");
         }
     }
+
+    private static OpenSSLKey translateKeyToEcOpenSSLKey(Key key) throws InvalidKeyException {
+        try {
+            return ((OpenSSLKeyHolder) KeyFactory.getInstance(
+                    "EC", OpenSSLProvider.PROVIDER_NAME).translateKey(key)).getOpenSSLKey();
+        } catch (Exception e) {
+            throw new InvalidKeyException("Failed to translate key to OpenSSL EC key", e);
+        }
+    }
 }
diff --git a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index ee6a57e..0921d8b 100644
--- a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -2360,6 +2360,22 @@
     return orderArray;
 }
 
+static jint NativeCrypto_EC_GROUP_get_degree(JNIEnv* env, jclass, jlong groupRef)
+{
+    const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
+    JNI_TRACE("EC_GROUP_get_degree(%p)", group);
+
+    jint degree = EC_GROUP_get_degree(group);
+    if (degree == 0) {
+      JNI_TRACE("EC_GROUP_get_degree(%p) => unsupported", group);
+      jniThrowRuntimeException(env, "not supported");
+      return 0;
+    }
+
+    JNI_TRACE("EC_GROUP_get_degree(%p) => %d", group, degree);
+    return degree;
+}
+
 static jbyteArray NativeCrypto_EC_GROUP_get_cofactor(JNIEnv* env, jclass, jlong groupRef)
 {
     const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
@@ -7821,6 +7837,7 @@
     NATIVE_METHOD(NativeCrypto, EC_GROUP_get_curve_name, "(J)Ljava/lang/String;"),
     NATIVE_METHOD(NativeCrypto, EC_GROUP_get_curve, "(J)[[B"),
     NATIVE_METHOD(NativeCrypto, EC_GROUP_get_order, "(J)[B"),
+    NATIVE_METHOD(NativeCrypto, EC_GROUP_get_degree, "(J)I"),
     NATIVE_METHOD(NativeCrypto, EC_GROUP_get_cofactor, "(J)[B"),
     NATIVE_METHOD(NativeCrypto, EC_GROUP_clear_free, "(J)V"),
     NATIVE_METHOD(NativeCrypto, EC_GROUP_cmp, "(JJ)Z"),