Merge "Switch Android Keystore key gen and import to new KeyStore API." into mnc-dev
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ad348f8..893771a 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -181,11 +181,15 @@
     }
 
     public boolean put(String key, byte[] value, int uid, int flags) {
+        return insert(key, value, uid, flags) == NO_ERROR;
+    }
+
+    public int insert(String key, byte[] value, int uid, int flags) {
         try {
-            return mBinder.insert(key, value, uid, flags) == NO_ERROR;
+            return mBinder.insert(key, value, uid, flags);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
+            return SYSTEM_ERROR;
         }
     }
 
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index e555cc0..f37cf07 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -245,4 +245,12 @@
         put("Signature." + algorithm + " SupportedKeyClasses",
                 KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
     }
+
+    public static String[] getSupportedEcdsaSignatureDigests() {
+        return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+    }
+
+    public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() {
+        return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+    }
 }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 4d6178f..688936c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -179,11 +179,15 @@
                 mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
                 mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                         spec.getEncryptionPaddings());
+                if (spec.getSignaturePaddings().length > 0) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Signature paddings not supported for symmetric key algorithms");
+                }
                 mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
                 if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
                         && (spec.isRandomizedEncryptionRequired())) {
                     for (int keymasterBlockMode : mKeymasterBlockModes) {
-                        if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+                        if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
                                 keymasterBlockMode)) {
                             throw new InvalidAlgorithmParameterException(
                                     "Randomized encryption (IND-CPA) required but may be violated"
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index c5ea0f7..69155a8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -16,17 +16,39 @@
 
 package android.security.keystore;
 
-import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.security.Credentials;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
 import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterDefs;
 
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERInteger;
+import com.android.org.bouncycastle.asn1.DERNull;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.Certificate;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.org.bouncycastle.asn1.x509.Time;
+import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.org.bouncycastle.jce.X509Principal;
+import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeConstants;
 import com.android.org.conscrypt.OpenSSLEngine;
 
+import libcore.util.EmptyArray;
+
+import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyFactory;
@@ -41,10 +63,19 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.RSAKeyGenParameterSpec;
 import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Provides a way to create instances of a KeyPair which will be placed in the
@@ -63,13 +94,13 @@
 
     public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
         public RSA() {
-            super(KeyProperties.KEY_ALGORITHM_RSA);
+            super(KeymasterDefs.KM_ALGORITHM_RSA);
         }
     }
 
     public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
         public EC() {
-            super(KeyProperties.KEY_ALGORITHM_EC);
+            super(KeymasterDefs.KM_ALGORITHM_EC);
         }
     }
 
@@ -87,39 +118,296 @@
     private static final int RSA_MIN_KEY_SIZE = 512;
     private static final int RSA_MAX_KEY_SIZE = 8192;
 
-    private final String mAlgorithm;
+    private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
+            new HashMap<String, Integer>();
+    private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
+    static {
+        // Aliases for NIST P-192
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-192", 192);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp192r1", 192);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime192v1", 192);
+
+        // Aliases for NIST P-224
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
+
+        // Aliases for NIST P-256
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+
+        // Aliases for NIST P-384
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
+
+        // Aliases for NIST P-521
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
+
+        SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
+        Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
+    }
+    private final int mOriginalKeymasterAlgorithm;
 
     private KeyStore mKeyStore;
 
     private KeyGenParameterSpec mSpec;
+
+    private String mEntryAlias;
     private boolean mEncryptionAtRestRequired;
-    private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
-    private int mKeyType;
-    private int mKeySize;
+    private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
+    private int mKeymasterAlgorithm = -1;
+    private int mKeySizeBits;
+    private SecureRandom mRng;
 
-    protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
-        mAlgorithm = algorithm;
+    private int[] mKeymasterPurposes;
+    private int[] mKeymasterBlockModes;
+    private int[] mKeymasterEncryptionPaddings;
+    private int[] mKeymasterSignaturePaddings;
+    private int[] mKeymasterDigests;
+
+    private long mRSAPublicExponent;
+
+    protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
+        mOriginalKeymasterAlgorithm = keymasterAlgorithm;
     }
 
-    @KeyProperties.KeyAlgorithmEnum String getAlgorithm() {
-        return mAlgorithm;
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        throw new IllegalArgumentException(
+                KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName()
+                + " required to initialize this KeyPairGenerator");
     }
 
-    /**
-     * Generate a KeyPair which is backed by the Android keystore service. You
-     * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
-     * with an {@link KeyPairGeneratorSpec} as the {@code params}
-     * argument before calling this otherwise an {@code IllegalStateException}
-     * will be thrown.
-     * <p>
-     * This will create an entry in the Android keystore service with a
-     * self-signed certificate using the {@code params} specified in the
-     * {@code initialize(params)} call.
-     *
-     * @throws IllegalStateException when called before calling
-     *             {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
-     * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
-     */
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (params == null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Must supply params of type " + KeyGenParameterSpec.class.getName()
+                        + " or " + KeyPairGeneratorSpec.class.getName());
+            }
+
+            KeyGenParameterSpec spec;
+            boolean encryptionAtRestRequired = false;
+            int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
+            if (params instanceof KeyGenParameterSpec) {
+                spec = (KeyGenParameterSpec) params;
+            } else if (params instanceof KeyPairGeneratorSpec) {
+                // Legacy/deprecated spec
+                KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
+                try {
+                    KeyGenParameterSpec.Builder specBuilder;
+                    String specKeyAlgorithm = legacySpec.getKeyType();
+                    if (specKeyAlgorithm != null) {
+                        // Spec overrides the generator's default key algorithm
+                        try {
+                            keymasterAlgorithm =
+                                    KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+                                            specKeyAlgorithm);
+                        } catch (IllegalArgumentException e) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Invalid key type in parameters", e);
+                        }
+                    }
+                    switch (keymasterAlgorithm) {
+                        case KeymasterDefs.KM_ALGORITHM_EC:
+                            specBuilder = new KeyGenParameterSpec.Builder(
+                                    legacySpec.getKeystoreAlias(),
+                                    KeyProperties.PURPOSE_SIGN
+                                    | KeyProperties.PURPOSE_VERIFY);
+                            specBuilder.setDigests(
+                                    KeyProperties.DIGEST_NONE,
+                                    KeyProperties.DIGEST_MD5,
+                                    KeyProperties.DIGEST_SHA1,
+                                    KeyProperties.DIGEST_SHA224,
+                                    KeyProperties.DIGEST_SHA256,
+                                    KeyProperties.DIGEST_SHA384,
+                                    KeyProperties.DIGEST_SHA512);
+                            break;
+                        case KeymasterDefs.KM_ALGORITHM_RSA:
+                            specBuilder = new KeyGenParameterSpec.Builder(
+                                    legacySpec.getKeystoreAlias(),
+                                    KeyProperties.PURPOSE_ENCRYPT
+                                    | KeyProperties.PURPOSE_DECRYPT
+                                    | KeyProperties.PURPOSE_SIGN
+                                    | KeyProperties.PURPOSE_VERIFY);
+                            specBuilder.setDigests(
+                                    KeyProperties.DIGEST_NONE,
+                                    KeyProperties.DIGEST_MD5,
+                                    KeyProperties.DIGEST_SHA1,
+                                    KeyProperties.DIGEST_SHA224,
+                                    KeyProperties.DIGEST_SHA256,
+                                    KeyProperties.DIGEST_SHA384,
+                                    KeyProperties.DIGEST_SHA512);
+                            specBuilder.setSignaturePaddings(
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+                            specBuilder.setEncryptionPaddings(
+                                    KeyProperties.ENCRYPTION_PADDING_NONE,
+                                    KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+                            // Disable randomized encryption requirement to support encryption
+                            // padding NONE above.
+                            specBuilder.setRandomizedEncryptionRequired(false);
+                            break;
+                        default:
+                            throw new ProviderException(
+                                    "Unsupported algorithm: " + mKeymasterAlgorithm);
+                    }
+
+                    if (legacySpec.getKeySize() != -1) {
+                        specBuilder.setKeySize(legacySpec.getKeySize());
+                    }
+                    if (legacySpec.getAlgorithmParameterSpec() != null) {
+                        specBuilder.setAlgorithmParameterSpec(
+                                legacySpec.getAlgorithmParameterSpec());
+                    }
+                    specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
+                    specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
+                    specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
+                    specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
+                    encryptionAtRestRequired = legacySpec.isEncryptionRequired();
+                    specBuilder.setUserAuthenticationRequired(false);
+
+                    spec = specBuilder.build();
+                } catch (NullPointerException | IllegalArgumentException e) {
+                    throw new InvalidAlgorithmParameterException(e);
+                }
+            } else {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported params class: " + params.getClass().getName()
+                        + ". Supported: " + KeyGenParameterSpec.class.getName()
+                        + ", " + KeyPairGeneratorSpec.class.getName());
+            }
+
+            mEntryAlias = spec.getKeystoreAlias();
+            mSpec = spec;
+            mKeymasterAlgorithm = keymasterAlgorithm;
+            mEncryptionAtRestRequired = encryptionAtRestRequired;
+            mKeySizeBits = spec.getKeySize();
+            initAlgorithmSpecificParameters();
+            if (mKeySizeBits == -1) {
+                mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
+            }
+            checkValidKeySize(keymasterAlgorithm, mKeySizeBits);
+
+            if (spec.getKeystoreAlias() == null) {
+                throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+            }
+
+            String jcaKeyAlgorithm;
+            try {
+                jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
+                        keymasterAlgorithm);
+                mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+                mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+                mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
+                        spec.getEncryptionPaddings());
+                mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster(
+                        spec.getSignaturePaddings());
+                if (spec.isDigestsSpecified()) {
+                    mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+                } else {
+                    mKeymasterDigests = EmptyArray.INT;
+                }
+            } catch (IllegalArgumentException e) {
+                throw new InvalidAlgorithmParameterException(e);
+            }
+
+            mJcaKeyAlgorithm = jcaKeyAlgorithm;
+            mRng = random;
+            mKeyStore = KeyStore.getInstance();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void resetAll() {
+        mEntryAlias = null;
+        mJcaKeyAlgorithm = null;
+        mKeymasterAlgorithm = -1;
+        mKeymasterPurposes = null;
+        mKeymasterBlockModes = null;
+        mKeymasterEncryptionPaddings = null;
+        mKeymasterSignaturePaddings = null;
+        mKeymasterDigests = null;
+        mKeySizeBits = 0;
+        mSpec = null;
+        mRSAPublicExponent = -1;
+        mEncryptionAtRestRequired = false;
+        mRng = null;
+        mKeyStore = null;
+    }
+
+    private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
+        AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec();
+        switch (mKeymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+            {
+                BigInteger publicExponent = null;
+                if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
+                    RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
+                    if (mKeySizeBits == -1) {
+                        mKeySizeBits = rsaSpec.getKeysize();
+                    } else if (mKeySizeBits != rsaSpec.getKeysize()) {
+                        throw new InvalidAlgorithmParameterException("RSA key size must match "
+                                + " between " + mSpec + " and " + algSpecificSpec
+                                + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize());
+                    }
+                    publicExponent = rsaSpec.getPublicExponent();
+                } else if (algSpecificSpec != null) {
+                    throw new InvalidAlgorithmParameterException(
+                        "RSA may only use RSAKeyGenParameterSpec");
+                }
+                if (publicExponent == null) {
+                    publicExponent = RSAKeyGenParameterSpec.F4;
+                }
+                if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
+                    throw new InvalidAlgorithmParameterException(
+                            "RSA public exponent must be positive: " + publicExponent);
+                }
+                if (publicExponent.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported RSA public exponent: " + publicExponent
+                            + ". Only exponents <= " + Long.MAX_VALUE + " supported");
+                }
+                mRSAPublicExponent = publicExponent.longValue();
+                break;
+            }
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                if (algSpecificSpec instanceof ECGenParameterSpec) {
+                    ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
+                    String curveName = ecSpec.getName();
+                    Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
+                            curveName.toLowerCase(Locale.US));
+                    if (ecSpecKeySizeBits == null) {
+                        throw new InvalidAlgorithmParameterException(
+                                "Unsupported EC curve name: " + curveName
+                                + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
+                    }
+                    if (mKeySizeBits == -1) {
+                        mKeySizeBits = ecSpecKeySizeBits;
+                    } else if (mKeySizeBits != ecSpecKeySizeBits) {
+                        throw new InvalidAlgorithmParameterException("EC key size must match "
+                                + " between " + mSpec + " and " + algSpecificSpec
+                                + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits);
+                    }
+                } else if (algSpecificSpec != null) {
+                    throw new InvalidAlgorithmParameterException(
+                        "EC may only use ECGenParameterSpec");
+                }
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+        }
+    }
+
     @Override
     public KeyPair generateKeyPair() {
         if (mKeyStore == null || mSpec == null) {
@@ -134,18 +422,65 @@
                     + ", but the user has not yet entered the credential");
         }
 
-        final String alias = mSpec.getKeystoreAlias();
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
+        args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+        args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+        args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
+        args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
+        args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
+        // TODO: Remove the digest and padding NONE workaround below once Android Keystore returns
+        // keys which are backed by AndroidKeyStoreBCWorkaround provider instead of Conscrypt. The
+        // workaround is needed because Conscrypt (via keystore-engine) uses old KeyStore API which
+        // translates into digest NONE and padding NONE in the new API. keystore-engine cannot be
+        // updated to pass in the correct padding and digest values because it uses
+        // OpenSSL/BoringSSL engine which performs digesting and padding prior before invoking
+        // KeyStore API.
+        if (!com.android.internal.util.ArrayUtils.contains(
+                mKeymasterDigests, KeymasterDefs.KM_DIGEST_NONE)) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+        }
+        if ((!com.android.internal.util.ArrayUtils.contains(
+                mKeymasterSignaturePaddings, KeymasterDefs.KM_PAD_NONE))
+                && (!com.android.internal.util.ArrayUtils.contains(
+                        mKeymasterEncryptionPaddings, KeymasterDefs.KM_PAD_NONE))) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        }
 
-        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+        KeymasterUtils.addUserAuthArgs(args,
+                mSpec.isUserAuthenticationRequired(),
+                mSpec.getUserAuthenticationValidityDurationSeconds());
+        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                (mSpec.getKeyValidityStart() != null)
+                ? mSpec.getKeyValidityStart() : new Date(0));
+        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                (mSpec.getKeyValidityForOriginationEnd() != null)
+                ? mSpec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                (mSpec.getKeyValidityForConsumptionEnd() != null)
+                ? mSpec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+        addAlgorithmSpecificParameters(args);
 
+        byte[] additionalEntropy =
+                KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                        mRng, (mKeySizeBits + 7) / 8);
+
+        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
         boolean success = false;
         try {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
-                    flags, args)) {
-                throw new IllegalStateException("could not generate key in keystore");
+            Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+            KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+            int errorCode = mKeyStore.generateKey(
+                    privateKeyAlias,
+                    args,
+                    additionalEntropy,
+                    flags,
+                    resultingKeyCharacteristics);
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new ProviderException(
+                        "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
             }
 
             final PrivateKey privKey;
@@ -153,7 +488,7 @@
             try {
                 privKey = engine.getPrivateKeyById(privateKeyAlias);
             } catch (InvalidKeyException e) {
-                throw new RuntimeException("Can't get key", e);
+                throw new ProviderException("Failed to obtain generated private key", e);
             }
 
             ExportResult exportResult =
@@ -163,39 +498,45 @@
                 throw new KeyStoreConnectException();
             } else if (exportResult.resultCode != KeyStore.NO_ERROR) {
                 throw new ProviderException(
-                        "Failed to obtain public key in X.509 format",
+                        "Failed to obtain X.509 form of generated public key",
                         KeyStore.getKeyStoreException(exportResult.resultCode));
             }
             final byte[] pubKeyBytes = exportResult.exportData;
 
-
             final PublicKey pubKey;
             try {
-                final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
+                final KeyFactory keyFact = KeyFactory.getInstance(mJcaKeyAlgorithm);
                 pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
             } catch (NoSuchAlgorithmException e) {
-                throw new IllegalStateException("Can't instantiate key generator", e);
+                throw new ProviderException(
+                        "Failed to obtain " + mJcaKeyAlgorithm + " KeyFactory", e);
             } catch (InvalidKeySpecException e) {
-                throw new IllegalStateException("keystore returned invalid key encoding", e);
+                throw new ProviderException("Invalid X.509 encoding of generated public key", e);
             }
 
             final X509Certificate cert;
             try {
-                cert = generateCertificate(privKey, pubKey);
+                cert = generateSelfSignedCertificate(privKey, pubKey);
             } catch (Exception e) {
-                throw new IllegalStateException("Can't generate certificate", e);
+                throw new ProviderException("Failed to generate self-signed certificate", e);
             }
 
             byte[] certBytes;
             try {
                 certBytes = cert.getEncoded();
             } catch (CertificateEncodingException e) {
-                throw new IllegalStateException("Can't get encoding of certificate", e);
+                throw new ProviderException(
+                        "Failed to obtain encoded form of self-signed certificate", e);
             }
 
-            if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
-                    flags)) {
-                throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+            int insertErrorCode = mKeyStore.insert(
+                    Credentials.USER_CERTIFICATE + mEntryAlias,
+                    certBytes,
+                    KeyStore.UID_SELF,
+                    flags);
+            if (insertErrorCode != KeyStore.NO_ERROR) {
+                throw new ProviderException("Failed to store self-signed certificate",
+                        KeyStore.getKeyStoreException(insertErrorCode));
             }
 
             KeyPair result = new KeyPair(pubKey, privKey);
@@ -203,14 +544,41 @@
             return result;
         } finally {
             if (!success) {
-                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+                Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
             }
         }
     }
 
+    private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
+        switch (mKeymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                keymasterArgs.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
+                break;
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+        }
+    }
+
+    private X509Certificate generateSelfSignedCertificate(
+            PrivateKey privateKey, PublicKey publicKey) throws Exception {
+        String signatureAlgorithm =
+                getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
+        if (signatureAlgorithm == null) {
+            // Key cannot be used to sign a certificate
+            return generateSelfSignedCertificateWithFakeSignature(publicKey);
+        } else {
+            // Key can be used to sign a certificate
+            return generateSelfSignedCertificateWithValidSignature(
+                    privateKey, publicKey, signatureAlgorithm);
+        }
+    }
+
     @SuppressWarnings("deprecation")
-    private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
-            throws Exception {
+    private X509Certificate generateSelfSignedCertificateWithValidSignature(
+            PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm)
+                    throws Exception {
         final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
         certGen.setPublicKey(publicKey);
         certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
@@ -218,198 +586,223 @@
         certGen.setIssuerDN(mSpec.getCertificateSubject());
         certGen.setNotBefore(mSpec.getCertificateNotBefore());
         certGen.setNotAfter(mSpec.getCertificateNotAfter());
-        certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
+        certGen.setSignatureAlgorithm(signatureAlgorithm);
         return certGen.generate(privateKey);
     }
 
-    @NonNull
-    private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
-        String result = spec.getKeyType();
-        if (result != null) {
-            return result;
+    @SuppressWarnings("deprecation")
+    private X509Certificate generateSelfSignedCertificateWithFakeSignature(
+            PublicKey publicKey) throws Exception {
+        V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
+        ASN1ObjectIdentifier sigAlgOid;
+        AlgorithmIdentifier sigAlgId;
+        byte[] signature;
+        switch (mKeymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
+                sigAlgId = new AlgorithmIdentifier(sigAlgOid);
+                ASN1EncodableVector v = new ASN1EncodableVector();
+                v.add(new DERInteger(0));
+                v.add(new DERInteger(0));
+                signature = new DERSequence().getEncoded();
+                break;
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption;
+                sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE);
+                signature = new byte[1];
+                break;
+            default:
+                throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm);
         }
-        return getAlgorithm();
+
+        try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) {
+            tbsGenerator.setSubjectPublicKeyInfo(
+                    SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
+        }
+        tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber()));
+        X509Principal subject =
+                new X509Principal(mSpec.getCertificateSubject().getEncoded());
+        tbsGenerator.setSubject(subject);
+        tbsGenerator.setIssuer(subject);
+        tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore()));
+        tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter()));
+        tbsGenerator.setSignature(sigAlgId);
+        TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate();
+
+        ASN1EncodableVector result = new ASN1EncodableVector();
+        result.add(tbsCertificate);
+        result.add(sigAlgId);
+        result.add(new DERBitString(signature));
+        return new X509CertificateObject(Certificate.getInstance(new DERSequence(result)));
     }
 
-    private static int getDefaultKeySize(int keyType) {
-        if (keyType == NativeConstants.EVP_PKEY_EC) {
-            return EC_DEFAULT_KEY_SIZE;
-        } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
-            return RSA_DEFAULT_KEY_SIZE;
+    private static int getDefaultKeySize(int keymasterAlgorithm) {
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                return EC_DEFAULT_KEY_SIZE;
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                return RSA_DEFAULT_KEY_SIZE;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
         }
-        return -1;
     }
 
-    private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
+    private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
             throws InvalidAlgorithmParameterException {
-        if (keyType == NativeConstants.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 == NativeConstants.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 == NativeConstants.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(
-            @KeyProperties.KeyAlgorithmEnum String algorithm) {
-        if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
-            return "sha256WithRSA";
-        } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
-            return "sha256WithECDSA";
-        } else {
-            throw new IllegalArgumentException("Unsupported key type " + algorithm);
-        }
-    }
-
-    private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
-        switch (keyType) {
-            case NativeConstants.EVP_PKEY_RSA:
-                if (spec instanceof RSAKeyGenParameterSpec) {
-                    RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
-                    return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+                    throw new InvalidAlgorithmParameterException("EC key size must be >= "
+                            + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
                 }
                 break;
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+                    throw new InvalidAlgorithmParameterException("RSA key size must be >= "
+                            + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
+                }
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
         }
-        return null;
     }
 
-    @Override
-    public void initialize(int keysize, SecureRandom random) {
-        throw new IllegalArgumentException(
-                "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
-    }
+    /**
+     * Returns the {@code Signature} algorithm to be used for signing a certificate using the
+     * specified key or {@code null} if the key cannot be used for signing a certificate.
+     */
+    @Nullable
+    private static String getCertificateSignatureAlgorithm(
+            int keymasterAlgorithm,
+            int keySizeBits,
+            KeyGenParameterSpec spec) {
+        // Constraints:
+        // 1. Key must be authorized for signing.
+        // 2. Signature digest must be one of key's authorized digests.
+        // 3. For RSA keys, the digest output size must not exceed modulus size minus space needed
+        //    for RSA PKCS#1 signature padding (about 29 bytes: minimum 10 bytes of padding + 15--19
+        //    bytes overhead for encoding digest OID and digest value in DER).
+        // 4. For EC keys, the there is no point in using a digest whose output size is longer than
+        //    key/field size because the digest will be truncated to that size.
 
-    @Override
-    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
-            throws InvalidAlgorithmParameterException {
-        if (params == null) {
-            throw new InvalidAlgorithmParameterException(
-                    "Must supply params of type " + KeyGenParameterSpec.class.getName()
-                    + " or " + KeyPairGeneratorSpec.class.getName());
+        if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
+            // Key not authorized for signing
+            return null;
         }
+        if (!spec.isDigestsSpecified()) {
+            // Key not authorized for any digests -- can't sign
+            return null;
+        }
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+            {
+                Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+                        spec.getDigests(),
+                        AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
 
-        String keyAlgorithm;
-        KeyGenParameterSpec spec;
-        boolean encryptionAtRestRequired = false;
-        if (params instanceof KeyPairGeneratorSpec) {
-            KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
-            try {
-                KeyGenParameterSpec.Builder specBuilder;
-                keyAlgorithm = getKeyAlgorithm(legacySpec).toUpperCase(Locale.US);
-                if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
-                    specBuilder = new KeyGenParameterSpec.Builder(
-                            legacySpec.getKeystoreAlias(),
-                            KeyProperties.PURPOSE_SIGN
-                            | KeyProperties.PURPOSE_VERIFY);
-                    specBuilder.setDigests(
-                            KeyProperties.DIGEST_NONE,
-                            KeyProperties.DIGEST_MD5,
-                            KeyProperties.DIGEST_SHA1,
-                            KeyProperties.DIGEST_SHA224,
-                            KeyProperties.DIGEST_SHA256,
-                            KeyProperties.DIGEST_SHA384,
-                            KeyProperties.DIGEST_SHA512);
-                } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
-                    specBuilder = new KeyGenParameterSpec.Builder(
-                            legacySpec.getKeystoreAlias(),
-                            KeyProperties.PURPOSE_ENCRYPT
-                            | KeyProperties.PURPOSE_DECRYPT
-                            | KeyProperties.PURPOSE_SIGN
-                            | KeyProperties.PURPOSE_VERIFY);
-                    specBuilder.setDigests(
-                            KeyProperties.DIGEST_NONE,
-                            KeyProperties.DIGEST_MD5,
-                            KeyProperties.DIGEST_SHA1,
-                            KeyProperties.DIGEST_SHA224,
-                            KeyProperties.DIGEST_SHA256,
-                            KeyProperties.DIGEST_SHA384,
-                            KeyProperties.DIGEST_SHA512);
-                    specBuilder.setSignaturePaddings(
-                            KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-                    specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
-                    specBuilder.setEncryptionPaddings(
-                            KeyProperties.ENCRYPTION_PADDING_NONE,
-                            KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
-                    // Disable randomized encryption requirement to support encryption padding NONE
-                    // above.
-                    specBuilder.setRandomizedEncryptionRequired(false);
-                } else {
-                    throw new InvalidAlgorithmParameterException(
-                            "Unsupported key algorithm: " + keyAlgorithm);
+                int bestKeymasterDigest = -1;
+                int bestDigestOutputSizeBits = -1;
+                for (int keymasterDigest : availableKeymasterDigests) {
+                    int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+                    if (outputSizeBits == keySizeBits) {
+                        // Perfect match -- use this digest
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                        break;
+                    }
+                    // Not a perfect match -- check against the best digest so far
+                    if (bestKeymasterDigest == -1) {
+                        // First digest tested -- definitely the best so far
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                    } else {
+                        // Prefer output size to be as close to key size as possible, with output
+                        // sizes larger than key size preferred to those smaller than key size.
+                        if (bestDigestOutputSizeBits < keySizeBits) {
+                            // Output size of the best digest so far is smaller than key size.
+                            // Anything larger is a win.
+                            if (outputSizeBits > bestDigestOutputSizeBits) {
+                                bestKeymasterDigest = keymasterDigest;
+                                bestDigestOutputSizeBits = outputSizeBits;
+                            }
+                        } else {
+                            // Output size of the best digest so far is larger than key size.
+                            // Anything smaller is a win, as long as it's not smaller than key size.
+                            if ((outputSizeBits < bestDigestOutputSizeBits)
+                                    && (outputSizeBits >= keySizeBits)) {
+                                bestKeymasterDigest = keymasterDigest;
+                                bestDigestOutputSizeBits = outputSizeBits;
+                            }
+                        }
+                    }
                 }
-
-                if (legacySpec.getKeySize() != -1) {
-                    specBuilder.setKeySize(legacySpec.getKeySize());
+                if (bestKeymasterDigest == -1) {
+                    return null;
                 }
-                if (legacySpec.getAlgorithmParameterSpec() != null) {
-                    specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
-                }
-                specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
-                specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
-                specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
-                specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
-                encryptionAtRestRequired = legacySpec.isEncryptionRequired();
-                specBuilder.setUserAuthenticationRequired(false);
-
-                spec = specBuilder.build();
-            } catch (NullPointerException | IllegalArgumentException e) {
-                throw new InvalidAlgorithmParameterException(e);
+                return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+                        bestKeymasterDigest) + "WithECDSA";
             }
-        } else if (params instanceof KeyGenParameterSpec) {
-            spec = (KeyGenParameterSpec) params;
-            keyAlgorithm = getAlgorithm();
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+            {
+                Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+                        spec.getDigests(),
+                        AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
+
+                // The amount of space available for the digest is less than modulus size because
+                // padding must be at least 10 bytes long, and then there's also the 15--19
+                // bytes overhead for encoding digest OID and digest value in DER.
+                int maxDigestOutputSizeBits = keySizeBits - 29 * 8;
+                int bestKeymasterDigest = -1;
+                int bestDigestOutputSizeBits = -1;
+                for (int keymasterDigest : availableKeymasterDigests) {
+                    int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+                    if (outputSizeBits > maxDigestOutputSizeBits) {
+                        // Digest too long (signature generation will fail) -- skip
+                        continue;
+                    }
+                    if (bestKeymasterDigest == -1) {
+                        // First digest tested -- definitely the best so far
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                    } else {
+                        // The longer the better
+                        if (outputSizeBits > bestDigestOutputSizeBits) {
+                            bestKeymasterDigest = keymasterDigest;
+                            bestDigestOutputSizeBits = outputSizeBits;
+                        }
+                    }
+                }
+                if (bestKeymasterDigest == -1) {
+                    return null;
+                }
+                return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+                        bestKeymasterDigest) + "WithRSA";
+            }
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+        }
+    }
+
+    private static Set<Integer> getAvailableKeymasterSignatureDigests(
+            @KeyProperties.DigestEnum String[] authorizedKeyDigests,
+            @KeyProperties.DigestEnum String[] supportedSignatureDigests) {
+        Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>();
+        for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) {
+            authorizedKeymasterKeyDigests.add(keymasterDigest);
+        }
+        Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>();
+        for (int keymasterDigest
+                : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) {
+            supportedKeymasterSignatureDigests.add(keymasterDigest);
+        }
+        if (authorizedKeymasterKeyDigests.contains(KeymasterDefs.KM_DIGEST_NONE)) {
+            // Key is authorized to be used with any digest
+            return supportedKeymasterSignatureDigests;
         } else {
-            throw new InvalidAlgorithmParameterException(
-                    "Unsupported params class: " + params.getClass().getName()
-                    + ". Supported: " + KeyGenParameterSpec.class.getName()
-                    + ", " + KeyPairGeneratorSpec.class);
+            // Key is authorized to be used only with specific digests.
+            Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
+            result.retainAll(authorizedKeymasterKeyDigests);
+            return result;
         }
-
-        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;
-        mEncryptionAtRestRequired = encryptionAtRestRequired;
-        mKeyStore = KeyStore.getInstance();
     }
 }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 7c9c0cf..c03be63 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -22,6 +22,7 @@
 import libcore.util.EmptyArray;
 
 import android.security.Credentials;
+import android.security.KeyStore;
 import android.security.KeyStoreParameter;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
@@ -39,7 +40,6 @@
 import java.security.KeyStore.Entry;
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.KeyStore.ProtectionParameter;
-import java.security.KeyStore;
 import java.security.KeyStore.SecretKeyEntry;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
@@ -86,7 +86,7 @@
 public class AndroidKeyStoreSpi extends KeyStoreSpi {
     public static final String NAME = "AndroidKeyStore";
 
-    private android.security.KeyStore mKeyStore;
+    private KeyStore mKeyStore;
 
     @Override
     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
@@ -105,8 +105,7 @@
             String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
             int errorCode = mKeyStore.getKeyCharacteristics(
                     keyAliasInKeystore, null, null, keyCharacteristics);
-            if ((errorCode != KeymasterDefs.KM_ERROR_OK)
-                    && (errorCode != android.security.KeyStore.NO_ERROR)) {
+            if (errorCode != KeyStore.NO_ERROR) {
                 throw (UnrecoverableKeyException)
                         new UnrecoverableKeyException("Failed to load information about key")
                                 .initCause(mKeyStore.getInvalidKeyException(alias, errorCode));
@@ -272,107 +271,72 @@
         }
     }
 
+    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
+            throws KeyStoreException {
+        String keyAlgorithm = key.getAlgorithm();
+        KeyProtection.Builder specBuilder;
+        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
+            specBuilder =
+                    new KeyProtection.Builder(
+                            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
+            specBuilder.setDigests(
+                    KeyProperties.DIGEST_NONE,
+                    KeyProperties.DIGEST_MD5,
+                    KeyProperties.DIGEST_SHA1,
+                    KeyProperties.DIGEST_SHA224,
+                    KeyProperties.DIGEST_SHA256,
+                    KeyProperties.DIGEST_SHA384,
+                    KeyProperties.DIGEST_SHA512);
+        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
+            specBuilder =
+                    new KeyProtection.Builder(
+                            KeyProperties.PURPOSE_ENCRYPT
+                            | KeyProperties.PURPOSE_DECRYPT
+                            | KeyProperties.PURPOSE_SIGN
+                            | KeyProperties.PURPOSE_VERIFY);
+            specBuilder.setDigests(
+                    KeyProperties.DIGEST_NONE,
+                    KeyProperties.DIGEST_MD5,
+                    KeyProperties.DIGEST_SHA1,
+                    KeyProperties.DIGEST_SHA224,
+                    KeyProperties.DIGEST_SHA256,
+                    KeyProperties.DIGEST_SHA384,
+                    KeyProperties.DIGEST_SHA512);
+            specBuilder.setSignaturePaddings(
+                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+            specBuilder.setEncryptionPaddings(
+                    KeyProperties.ENCRYPTION_PADDING_NONE,
+                    KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+            // Disable randomized encryption requirement to support encryption padding NONE
+            // above.
+            specBuilder.setRandomizedEncryptionRequired(false);
+        } else {
+            throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
+        }
+        specBuilder.setUserAuthenticationRequired(false);
+
+        return specBuilder.build();
+    }
+
     private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
             java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
         int flags = 0;
         KeyProtection spec;
-        if (param instanceof KeyStoreParameter) {
+        if (param == null) {
+            spec = getLegacyKeyProtectionParameter(key);
+        } else if (param instanceof KeyStoreParameter) {
+            spec = getLegacyKeyProtectionParameter(key);
             KeyStoreParameter legacySpec = (KeyStoreParameter) param;
-            try {
-                String keyAlgorithm = key.getAlgorithm();
-                KeyProtection.Builder specBuilder;
-                if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
-                    specBuilder =
-                            new KeyProtection.Builder(
-                                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
-                    specBuilder.setDigests(
-                            KeyProperties.DIGEST_NONE,
-                            KeyProperties.DIGEST_MD5,
-                            KeyProperties.DIGEST_SHA1,
-                            KeyProperties.DIGEST_SHA224,
-                            KeyProperties.DIGEST_SHA256,
-                            KeyProperties.DIGEST_SHA384,
-                            KeyProperties.DIGEST_SHA512);
-                } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
-                    specBuilder =
-                            new KeyProtection.Builder(
-                                    KeyProperties.PURPOSE_ENCRYPT
-                                    | KeyProperties.PURPOSE_DECRYPT
-                                    | KeyProperties.PURPOSE_SIGN
-                                    | KeyProperties.PURPOSE_VERIFY);
-                    specBuilder.setDigests(
-                            KeyProperties.DIGEST_NONE,
-                            KeyProperties.DIGEST_MD5,
-                            KeyProperties.DIGEST_SHA1,
-                            KeyProperties.DIGEST_SHA224,
-                            KeyProperties.DIGEST_SHA256,
-                            KeyProperties.DIGEST_SHA384,
-                            KeyProperties.DIGEST_SHA512);
-                    specBuilder.setSignaturePaddings(
-                            KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-                    specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
-                    specBuilder.setEncryptionPaddings(
-                            KeyProperties.ENCRYPTION_PADDING_NONE,
-                            KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
-                    // Disable randomized encryption requirement to support encryption padding NONE
-                    // above.
-                    specBuilder.setRandomizedEncryptionRequired(false);
-                } else {
-                    throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
-                }
-                if (legacySpec.isEncryptionRequired()) {
-                    flags = android.security.KeyStore.FLAG_ENCRYPTED;
-                }
-                specBuilder.setUserAuthenticationRequired(false);
-
-                spec = specBuilder.build();
-            } catch (NullPointerException | IllegalArgumentException e) {
-                throw new KeyStoreException("Unsupported protection parameter", e);
+            if (legacySpec.isEncryptionRequired()) {
+                flags = KeyStore.FLAG_ENCRYPTED;
             }
         } else if (param instanceof KeyProtection) {
             spec = (KeyProtection) param;
-        } else if (param != null) {
+        } else {
             throw new KeyStoreException(
                     "Unsupported protection parameter class:" + param.getClass().getName()
-                    + ". Supported: " + KeyStoreParameter.class.getName() + ", "
-                    + KeyProtection.class.getName());
-        } else {
-            spec = null;
-        }
-
-        byte[] keyBytes = null;
-
-        final String pkeyAlias;
-        if (key instanceof OpenSSLKeyHolder) {
-            pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
-        } else {
-            pkeyAlias = null;
-        }
-
-        final boolean shouldReplacePrivateKey;
-        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
-            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
-            if (!alias.equals(keySubalias)) {
-                throw new KeyStoreException("Can only replace keys with same alias: " + alias
-                        + " != " + keySubalias);
-            }
-
-            shouldReplacePrivateKey = false;
-        } else {
-            // Make sure the PrivateKey format is the one we support.
-            final String keyFormat = key.getFormat();
-            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
-                throw new KeyStoreException(
-                        "Only PrivateKeys that can be encoded into PKCS#8 are supported");
-            }
-
-            // Make sure we can actually encode the key.
-            keyBytes = key.getEncoded();
-            if (keyBytes == null) {
-                throw new KeyStoreException("PrivateKey has no encoding");
-            }
-
-            shouldReplacePrivateKey = true;
+                    + ". Supported: " + KeyProtection.class.getName() + ", "
+                    + KeyStoreParameter.class.getName());
         }
 
         // Make sure the chain exists since this is a PrivateKey
@@ -400,7 +364,7 @@
         try {
             userCertBytes = x509chain[0].getEncoded();
         } catch (CertificateEncodingException e) {
-            throw new KeyStoreException("Couldn't encode certificate #1", e);
+            throw new KeyStoreException("Failed to encode certificate #0", e);
         }
 
         /*
@@ -421,7 +385,7 @@
                     certsBytes[i] = x509chain[i + 1].getEncoded();
                     totalCertLength += certsBytes[i].length;
                 } catch (CertificateEncodingException e) {
-                    throw new KeyStoreException("Can't encode Certificate #" + i, e);
+                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
                 }
             }
 
@@ -441,31 +405,150 @@
             chainBytes = null;
         }
 
-        /*
-         * Make sure we clear out all the appropriate types before trying to
-         * write.
-         */
-        if (shouldReplacePrivateKey) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+        final String pkeyAlias;
+        if (key instanceof OpenSSLKeyHolder) {
+            pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
+        } else if (key instanceof AndroidKeyStorePrivateKey) {
+            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
         } else {
-            Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
-            Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+            pkeyAlias = null;
         }
 
-        if (shouldReplacePrivateKey
-                && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
-                        android.security.KeyStore.UID_SELF, flags)) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new KeyStoreException("Couldn't put private key in keystore");
-        } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
-                android.security.KeyStore.UID_SELF, flags)) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new KeyStoreException("Couldn't put certificate #1 in keystore");
-        } else if (chainBytes != null
-                && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
-                        android.security.KeyStore.UID_SELF, flags)) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new KeyStoreException("Couldn't put certificate chain in keystore");
+        byte[] pkcs8EncodedPrivateKeyBytes;
+        KeymasterArguments importArgs;
+        final boolean shouldReplacePrivateKey;
+        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
+            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
+            if (!alias.equals(keySubalias)) {
+                throw new KeyStoreException("Can only replace keys with same alias: " + alias
+                        + " != " + keySubalias);
+            }
+            shouldReplacePrivateKey = false;
+            importArgs = null;
+            pkcs8EncodedPrivateKeyBytes = null;
+        } else {
+            shouldReplacePrivateKey = true;
+            // Make sure the PrivateKey format is the one we support.
+            final String keyFormat = key.getFormat();
+            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+                throw new KeyStoreException(
+                        "Unsupported private key export format: " + keyFormat
+                        + ". Only private keys which export their key material in PKCS#8 format are"
+                        + " supported.");
+            }
+
+            // Make sure we can actually encode the key.
+            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
+            if (pkcs8EncodedPrivateKeyBytes == null) {
+                throw new KeyStoreException("Private key did not export any key material");
+            }
+
+            importArgs = new KeymasterArguments();
+            try {
+                importArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+                                key.getAlgorithm()));
+                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
+                importArgs.addInts(KeymasterDefs.KM_TAG_PURPOSE,
+                        KeyProperties.Purpose.allToKeymaster(purposes));
+                if (spec.isDigestsSpecified()) {
+                    importArgs.addInts(KeymasterDefs.KM_TAG_DIGEST,
+                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
+                }
+
+                importArgs.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE,
+                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
+                int[] keymasterEncryptionPaddings =
+                        KeyProperties.EncryptionPadding.allToKeymaster(
+                                spec.getEncryptionPaddings());
+                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
+                        && (spec.isRandomizedEncryptionRequired())) {
+                    for (int keymasterPadding : keymasterEncryptionPaddings) {
+                        if (!KeymasterUtils
+                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+                                        keymasterPadding)) {
+                            throw new KeyStoreException(
+                                    "Randomized encryption (IND-CPA) required but is violated by"
+                                    + " encryption padding mode: "
+                                    + KeyProperties.EncryptionPadding.fromKeymaster(
+                                            keymasterPadding)
+                                    + ". See KeyProtection documentation.");
+                        }
+                    }
+                }
+                importArgs.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
+                importArgs.addInts(KeymasterDefs.KM_TAG_PADDING,
+                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
+                KeymasterUtils.addUserAuthArgs(importArgs,
+                        spec.isUserAuthenticationRequired(),
+                        spec.getUserAuthenticationValidityDurationSeconds());
+                importArgs.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                        (spec.getKeyValidityStart() != null)
+                                ? spec.getKeyValidityStart() : new Date(0));
+                importArgs.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                        (spec.getKeyValidityForOriginationEnd() != null)
+                                ? spec.getKeyValidityForOriginationEnd()
+                                : new Date(Long.MAX_VALUE));
+                importArgs.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                        (spec.getKeyValidityForConsumptionEnd() != null)
+                                ? spec.getKeyValidityForConsumptionEnd()
+                                : new Date(Long.MAX_VALUE));
+            } catch (IllegalArgumentException e) {
+                throw new KeyStoreException("Invalid parameter", e);
+            }
+        }
+
+
+        boolean success = false;
+        try {
+            // Store the private key, if necessary
+            if (shouldReplacePrivateKey) {
+                // Delete the stored private key and any related entries before importing the
+                // provided key
+                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+                int errorCode = mKeyStore.importKey(
+                        Credentials.USER_PRIVATE_KEY + alias,
+                        importArgs,
+                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
+                        pkcs8EncodedPrivateKeyBytes,
+                        flags,
+                        resultingKeyCharacteristics);
+                if (errorCode != KeyStore.NO_ERROR) {
+                    throw new KeyStoreException("Failed to store private key",
+                            KeyStore.getKeyStoreException(errorCode));
+                }
+            } else {
+                // Keep the stored private key around -- delete all other entry types
+                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+            }
+
+            // Store the leaf certificate
+            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
+                    KeyStore.UID_SELF, flags);
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new KeyStoreException("Failed to store certificate #0",
+                        KeyStore.getKeyStoreException(errorCode));
+            }
+
+            // Store the certificate chain
+            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
+                    KeyStore.UID_SELF, flags);
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new KeyStoreException("Failed to store certificate chain",
+                        KeyStore.getKeyStoreException(errorCode));
+            }
+            success = true;
+        } finally {
+            if (!success) {
+                if (shouldReplacePrivateKey) {
+                    Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+                } else {
+                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+                }
+            }
         }
     }
 
@@ -589,7 +672,8 @@
         if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
                 && (params.isRandomizedEncryptionRequired())) {
             for (int keymasterBlockMode : keymasterBlockModes) {
-                if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
+                if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+                        keymasterBlockMode)) {
                     throw new KeyStoreException(
                             "Randomized encryption (IND-CPA) required but may be violated by block"
                             + " mode: "
@@ -598,9 +682,7 @@
                 }
             }
         }
-        for (int keymasterPurpose : KeyProperties.Purpose.allToKeymaster(purposes)) {
-            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
-        }
+        args.addInts(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
         args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
         if (params.getSignaturePaddings().length > 0) {
             throw new KeyStoreException("Signature paddings not supported for symmetric keys");
@@ -636,7 +718,7 @@
                 keyMaterial,
                 0, // flags
                 new KeyCharacteristics());
-        if (errorCode != android.security.KeyStore.NO_ERROR) {
+        if (errorCode != KeyStore.NO_ERROR) {
             throw new KeyStoreException("Failed to import secret key. Keystore error code: "
                 + errorCode);
         }
@@ -667,7 +749,7 @@
         }
 
         if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
-                android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
             throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
         }
     }
@@ -840,7 +922,7 @@
         }
 
         // Unfortunate name collision.
-        mKeyStore = android.security.KeyStore.getInstance();
+        mKeyStore = KeyStore.getInstance();
     }
 
     @Override
@@ -852,8 +934,9 @@
 
         Credentials.deleteAllTypesForAlias(mKeyStore, alias);
 
-        if (entry instanceof KeyStore.TrustedCertificateEntry) {
-            KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
+        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
+            java.security.KeyStore.TrustedCertificateEntry trE =
+                    (java.security.KeyStore.TrustedCertificateEntry) entry;
             engineSetCertificateEntry(alias, trE.getTrustedCertificate());
             return;
         }
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d861302..19ff9c7 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -214,7 +214,9 @@
     }
 
     /**
-     * Returns the requested key size or {@code -1} if default size should be used.
+     * Returns the requested key size. If {@code -1}, the size should be looked up from
+     * {@link #getAlgorithmParameterSpec()}, if provided, otherwise an algorithm-specific default
+     * size should be used.
      */
     public int getKeySize() {
         return mKeySize;
@@ -465,7 +467,10 @@
          * the modulus size, for EC keys this selects a curve with a matching field size, and for
          * symmetric keys this sets the size of the bitstring which is their key material.
          *
-         * <p>The default key size is specific to each key algorithm.
+         * <p>The default key size is specific to each key algorithm. If key size is not set
+         * via this method, it should be looked up from the algorithm-specific parameters (if any)
+         * provided via
+         * {@link #setAlgorithmParameterSpec(AlgorithmParameterSpec) setAlgorithmParameterSpec}.
          */
         @NonNull
         public Builder setKeySize(int keySize) {
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index e3c2d1d..5af4181 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -168,6 +168,31 @@
     public static abstract class KeyAlgorithm {
         private KeyAlgorithm() {}
 
+        public static int toKeymasterAsymmetricKeyAlgorithm(
+                @NonNull @KeyAlgorithmEnum String algorithm) {
+            if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
+                return KeymasterDefs.KM_ALGORITHM_EC;
+            } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
+                return KeymasterDefs.KM_ALGORITHM_RSA;
+            } else {
+                throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+            }
+        }
+
+        @NonNull
+        public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
+                int keymasterAlgorithm) {
+            switch (keymasterAlgorithm) {
+                case KeymasterDefs.KM_ALGORITHM_EC:
+                    return KEY_ALGORITHM_EC;
+                case KeymasterDefs.KM_ALGORITHM_RSA:
+                    return KEY_ALGORITHM_RSA;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported key algorithm: " + keymasterAlgorithm);
+            }
+        }
+
         public static int toKeymasterSecretKeyAlgorithm(
                 @NonNull @KeyAlgorithmEnum String algorithm) {
             if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
@@ -572,6 +597,28 @@
         }
 
         @NonNull
+        public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
+            switch (digest) {
+                case KeymasterDefs.KM_DIGEST_NONE:
+                    return "NONE";
+                case KeymasterDefs.KM_DIGEST_MD5:
+                    return "MD5";
+                case KeymasterDefs.KM_DIGEST_SHA1:
+                    return "SHA1";
+                case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                    return "SHA224";
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                    return "SHA256";
+                case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                    return "SHA384";
+                case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                    return "SHA512";
+                default:
+                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+            }
+        }
+
+        @NonNull
         public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
             if (digests.isEmpty()) {
                 return EmptyArray.STRING;
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index e7529e1..0639d49 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -50,7 +50,8 @@
         }
     }
 
-    public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
+    public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+            int keymasterBlockMode) {
         switch (keymasterBlockMode) {
             case KeymasterDefs.KM_MODE_ECB:
                 return false;
@@ -63,6 +64,20 @@
         }
     }
 
+    public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+            int keymasterPadding) {
+        switch (keymasterPadding) {
+            case KeymasterDefs.KM_PAD_NONE:
+                return false;
+            case KeymasterDefs.KM_PAD_RSA_OAEP:
+            case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+                return true;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported encryption padding scheme: " + keymasterPadding);
+        }
+    }
+
     /**
      * Adds keymaster arguments to express the key's authorization policy supported by user
      * authentication.