Merge "Add KeyPairGenerator.EC backed by Android KeyStore."
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index a0ffb5f..9d9a173 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -50,10 +50,50 @@
  *
  * {@hide}
  */
-public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
+public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
+
+    public static class RSA extends AndroidKeyPairGenerator {
+        public RSA() {
+            super("RSA");
+        }
+    }
+
+    public static class EC extends AndroidKeyPairGenerator {
+        public EC() {
+            super("EC");
+        }
+    }
+
+    /*
+     * These must be kept in sync with system/security/keystore/defaults.h
+     */
+
+    /* EC */
+    private static final int EC_DEFAULT_KEY_SIZE = 256;
+    private static final int EC_MIN_KEY_SIZE = 192;
+    private static final int EC_MAX_KEY_SIZE = 521;
+
+    /* RSA */
+    private static final int RSA_DEFAULT_KEY_SIZE = 2048;
+    private static final int RSA_MIN_KEY_SIZE = 512;
+    private static final int RSA_MAX_KEY_SIZE = 8192;
+
+    private final String mAlgorithm;
+
     private android.security.KeyStore mKeyStore;
 
     private KeyPairGeneratorSpec mSpec;
+    private String mKeyAlgorithm;
+    private int mKeyType;
+    private int mKeySize;
+
+    protected AndroidKeyPairGenerator(String algorithm) {
+        mAlgorithm = algorithm;
+    }
+
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
 
     /**
      * Generate a KeyPair which is backed by the Android keystore service. You
@@ -88,12 +128,11 @@
 
         Credentials.deleteAllTypesForAlias(mKeyStore, alias);
 
-        final int keyType = KeyStore.getKeyTypeForAlgorithm(mSpec.getKeyType());
-        byte[][] args = getArgsForKeyType(keyType, mSpec.getAlgorithmParameterSpec());
+        byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
 
         final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
-        if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, keyType,
-                mSpec.getKeySize(), mSpec.getFlags(), args)) {
+        if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
+                mSpec.getFlags(), args)) {
             throw new IllegalStateException("could not generate key in keystore");
         }
 
@@ -109,7 +148,7 @@
 
         final PublicKey pubKey;
         try {
-            final KeyFactory keyFact = KeyFactory.getInstance(mSpec.getKeyType());
+            final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
             pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
         } catch (NoSuchAlgorithmException e) {
             throw new IllegalStateException("Can't instantiate key generator", e);
@@ -117,18 +156,9 @@
             throw new IllegalStateException("keystore returned invalid key encoding", e);
         }
 
-        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
-        certGen.setPublicKey(pubKey);
-        certGen.setSerialNumber(mSpec.getSerialNumber());
-        certGen.setSubjectDN(mSpec.getSubjectDN());
-        certGen.setIssuerDN(mSpec.getSubjectDN());
-        certGen.setNotBefore(mSpec.getStartDate());
-        certGen.setNotAfter(mSpec.getEndDate());
-        certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyType(mSpec.getKeyType()));
-
         final X509Certificate cert;
         try {
-            cert = certGen.generate(privKey);
+            cert = generateCertificate(privKey, pubKey);
         } catch (Exception e) {
             Credentials.deleteAllTypesForAlias(mKeyStore, alias);
             throw new IllegalStateException("Can't generate certificate", e);
@@ -151,13 +181,78 @@
         return new KeyPair(pubKey, privKey);
     }
 
-    private static String getDefaultSignatureAlgorithmForKeyType(String keyType) {
-        if ("RSA".equalsIgnoreCase(keyType)) {
+    @SuppressWarnings("deprecation")
+    private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
+            throws Exception {
+        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+        certGen.setPublicKey(publicKey);
+        certGen.setSerialNumber(mSpec.getSerialNumber());
+        certGen.setSubjectDN(mSpec.getSubjectDN());
+        certGen.setIssuerDN(mSpec.getSubjectDN());
+        certGen.setNotBefore(mSpec.getStartDate());
+        certGen.setNotAfter(mSpec.getEndDate());
+        certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
+        return certGen.generate(privateKey);
+    }
+
+    private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
+        String result = spec.getKeyType();
+        if (result != null) {
+            return result;
+        }
+        return getAlgorithm();
+    }
+
+    private static int getDefaultKeySize(int keyType) {
+        if (keyType == NativeCrypto.EVP_PKEY_EC) {
+            return EC_DEFAULT_KEY_SIZE;
+        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+            return RSA_DEFAULT_KEY_SIZE;
+        }
+        return -1;
+    }
+
+    private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
+            throws InvalidAlgorithmParameterException {
+        if (keyType == NativeCrypto.EVP_PKEY_EC) {
+            if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+                throw new InvalidAlgorithmParameterException("EC keys must be >= "
+                        + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
+            }
+        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+            if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+                throw new InvalidAlgorithmParameterException("RSA keys must be >= "
+                        + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
+            }
+        } else {
+            throw new InvalidAlgorithmParameterException(
+                "Unsupported key algorithm: " + keyAlgorithm);
+        }
+    }
+
+    private static void checkCorrectParametersSpec(int keyType, int keySize,
+            AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
+        if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
+            if (spec instanceof RSAKeyGenParameterSpec) {
+                RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
+                if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
+                    throw new InvalidAlgorithmParameterException("RSA key size must match: "
+                            + keySize + " vs " + rsaSpec.getKeysize());
+                }
+            } else {
+                throw new InvalidAlgorithmParameterException(
+                    "RSA may only use RSAKeyGenParameterSpec");
+            }
+        }
+    }
+
+    private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
+        if ("RSA".equalsIgnoreCase(algorithm)) {
             return "sha256WithRSA";
-        } else if ("EC".equalsIgnoreCase(keyType)) {
+        } else if ("EC".equalsIgnoreCase(algorithm)) {
             return "sha256WithECDSA";
         } else {
-            throw new IllegalArgumentException("Unsupported key type " + keyType);
+            throw new IllegalArgumentException("Unsupported key type " + algorithm);
         }
     }
 
@@ -190,7 +285,26 @@
         }
 
         KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
+        String keyAlgorithm = getKeyAlgorithm(spec);
+        int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
+        if (keyType == -1) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported key algorithm: " + keyAlgorithm);
+        }
+        int keySize = spec.getKeySize();
+        if (keySize == -1) {
+            keySize = getDefaultKeySize(keyType);
+            if (keySize == -1) {
+                throw new InvalidAlgorithmParameterException(
+                    "Unsupported key algorithm: " + keyAlgorithm);
+            }
+        }
+        checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
+        checkValidKeySize(keyAlgorithm, keyType, keySize);
 
+        mKeyAlgorithm = keyAlgorithm;
+        mKeyType = keyType;
+        mKeySize = keySize;
         mSpec = spec;
         mKeyStore = android.security.KeyStore.getInstance();
     }
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index b17e450..9081e92 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -33,6 +33,7 @@
         put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
 
         // java.security.KeyPairGenerator
-        put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.class.getName());
+        put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
+        put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());
     }
 }
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index 6b67f43..8196434 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -16,8 +16,6 @@
 
 package android.security;
 
-import com.android.org.conscrypt.NativeCrypto;
-
 import android.content.Context;
 import android.text.TextUtils;
 
@@ -26,7 +24,6 @@
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.RSAKeyGenParameterSpec;
 import java.util.Date;
 
 import javax.security.auth.x500.X500Principal;
@@ -54,19 +51,6 @@
  * certificate signed by a real Certificate Authority.
  */
 public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
-    /*
-     * These must be kept in sync with system/security/keystore/defaults.h
-     */
-
-    /* EC */
-    private static final int EC_DEFAULT_KEY_SIZE = 256;
-    private static final int EC_MIN_KEY_SIZE = 192;
-    private static final int EC_MAX_KEY_SIZE = 521;
-
-    /* RSA */
-    private static final int RSA_DEFAULT_KEY_SIZE = 2048;
-    private static final int RSA_MIN_KEY_SIZE = 512;
-    private static final int RSA_MAX_KEY_SIZE = 8192;
 
     private final Context mContext;
 
@@ -139,13 +123,6 @@
             throw new IllegalArgumentException("endDate < startDate");
         }
 
-        final int keyTypeInt = KeyStore.getKeyTypeForAlgorithm(keyType);
-        if (keySize == -1) {
-            keySize = getDefaultKeySizeForType(keyTypeInt);
-        }
-        checkCorrectParametersSpec(keyTypeInt, keySize, spec);
-        checkValidKeySize(keyTypeInt, keySize);
-
         mContext = context;
         mKeystoreAlias = keyStoreAlias;
         mKeyType = keyType;
@@ -158,46 +135,6 @@
         mFlags = flags;
     }
 
-    private static int getDefaultKeySizeForType(int keyType) {
-        if (keyType == NativeCrypto.EVP_PKEY_EC) {
-            return EC_DEFAULT_KEY_SIZE;
-        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
-            return RSA_DEFAULT_KEY_SIZE;
-        }
-        throw new IllegalArgumentException("Invalid key type " + keyType);
-    }
-
-    private static void checkValidKeySize(int keyType, int keySize) {
-        if (keyType == NativeCrypto.EVP_PKEY_EC) {
-            if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
-                throw new IllegalArgumentException("EC keys must be >= " + EC_MIN_KEY_SIZE
-                        + " and <= " + EC_MAX_KEY_SIZE);
-            }
-        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
-            if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
-                throw new IllegalArgumentException("RSA keys must be >= " + RSA_MIN_KEY_SIZE
-                        + " and <= " + RSA_MAX_KEY_SIZE);
-            }
-        } else {
-            throw new IllegalArgumentException("Invalid key type " + keyType);
-        }
-    }
-
-    private static void checkCorrectParametersSpec(int keyType, int keySize,
-            AlgorithmParameterSpec spec) {
-        if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
-            if (spec instanceof RSAKeyGenParameterSpec) {
-                RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
-                if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
-                    throw new IllegalArgumentException("RSA key size must match: " + keySize
-                            + " vs " + rsaSpec.getKeysize());
-                }
-            } else {
-                throw new IllegalArgumentException("RSA may only use RSAKeyGenParameterSpec");
-            }
-        }
-    }
-
     /**
      * Gets the Android context used for operations with this instance.
      */
@@ -311,7 +248,7 @@
 
         private String mKeystoreAlias;
 
-        private String mKeyType = "RSA";
+        private String mKeyType;
 
         private int mKeySize = -1;
 
@@ -360,9 +297,7 @@
             if (keyType == null) {
                 throw new NullPointerException("keyType == null");
             } else {
-                try {
-                    KeyStore.getKeyTypeForAlgorithm(keyType);
-                } catch (IllegalArgumentException e) {
+                if (KeyStore.getKeyTypeForAlgorithm(keyType) == -1) {
                     throw new NoSuchAlgorithmException("Unsupported key type: " + keyType);
                 }
             }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 797067e..e753a7c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -68,13 +68,13 @@
         return new KeyStore(keystore);
     }
 
-    static int getKeyTypeForAlgorithm(String keyType) throws IllegalArgumentException {
+    static int getKeyTypeForAlgorithm(String keyType) {
         if ("RSA".equalsIgnoreCase(keyType)) {
             return NativeCrypto.EVP_PKEY_RSA;
         } else if ("EC".equalsIgnoreCase(keyType)) {
             return NativeCrypto.EVP_PKEY_EC;
         } else {
-            throw new IllegalArgumentException("Unsupported key type: " + keyType);
+            return -1;
         }
     }