Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a0b8f4d authored by Alex Klyubin's avatar Alex Klyubin Committed by Android (Google) Code Review
Browse files

Merge "Switch Android Keystore key gen and import to new KeyStore API." into mnc-dev

parents 8ef015be 3ceb1a04
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -181,11 +181,15 @@ public class KeyStore {
    }

    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;
        }
    }

+8 −0
Original line number Diff line number Diff line
@@ -245,4 +245,12 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
        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"};
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -179,11 +179,15 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
                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"
+611 −218

File changed.

Preview size limit exceeded, changes collapsed.

+213 −130
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.org.conscrypt.OpenSSLKeyHolder;
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.Key;
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 @@ import javax.crypto.SecretKey;
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
            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,13 +271,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        }
    }

    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) {
            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
            try {
    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
            throws KeyStoreException {
        String keyAlgorithm = key.getAlgorithm();
        KeyProtection.Builder specBuilder;
        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
@@ -310,7 +304,6 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                    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);
@@ -320,59 +313,30 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        } 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);
            }
        } else if (param instanceof KeyProtection) {
            spec = (KeyProtection) param;
        } else if (param != null) {
            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;
        return specBuilder.build();
    }

        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);
    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
        int flags = 0;
        KeyProtection spec;
        if (param == null) {
            spec = getLegacyKeyProtectionParameter(key);
        } else if (param instanceof KeyStoreParameter) {
            spec = getLegacyKeyProtectionParameter(key);
            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
            if (legacySpec.isEncryptionRequired()) {
                flags = KeyStore.FLAG_ENCRYPTED;
            }

            shouldReplacePrivateKey = false;
        } else if (param instanceof KeyProtection) {
            spec = (KeyProtection) param;
        } 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;
                    "Unsupported protection parameter class:" + param.getClass().getName()
                    + ". Supported: " + KeyProtection.class.getName() + ", "
                    + KeyStoreParameter.class.getName());
        }

        // Make sure the chain exists since this is a PrivateKey
@@ -400,7 +364,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                    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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
            chainBytes = null;
        }

        /*
         * Make sure we clear out all the appropriate types before trying to
         * write.
         */
        final String pkeyAlias;
        if (key instanceof OpenSSLKeyHolder) {
            pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
        } else if (key instanceof AndroidKeyStorePrivateKey) {
            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
        } else {
            pkeyAlias = null;
        }

        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);
            }

        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)) {
            // 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);
            throw new KeyStoreException("Couldn't put certificate chain in keystore");
                } else {
                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
                }
            }
        }
    }

@@ -589,7 +672,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                }
            }
        }
        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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        }

        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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
        }

        // Unfortunate name collision.
        mKeyStore = android.security.KeyStore.getInstance();
        mKeyStore = KeyStore.getInstance();
    }

    @Override
@@ -852,8 +934,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {

        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;
        }
Loading