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

Commit 5b30174b authored by Alex Klyubin's avatar Alex Klyubin Committed by Android Git Automerger
Browse files

am 2c378078: Merge "Export KeyFactory backed by Android Keystore." into mnc-dev

* commit '2c378078':
  Export KeyFactory backed by Android Keystore.
parents 271fdefd 2c378078
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security.keystore;

import android.security.Credentials;
import android.security.KeyStore;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactorySpi;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * {@link KeyFactorySpi} backed by Android KeyStore.
 *
 * @hide
 */
public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {

    private final KeyStore mKeyStore = KeyStore.getInstance();

    @Override
    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
            throws InvalidKeySpecException {
        if (keySpecClass == null) {
            throw new InvalidKeySpecException("keySpecClass == null");
        }
        if (!(key instanceof AndroidKeyStorePrivateKey)) {
            throw new InvalidKeySpecException("Only Android KeyStore private keys supported: " +
                    ((key != null) ? key.getClass().getName() : "null"));
        }
        if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpecClass)) {
            throw new InvalidKeySpecException(
                    "Key material export of Android KeyStore keys is not supported");
        }
        if (!KeyInfo.class.equals(keySpecClass)) {
            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
        }
        String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
        String entryAlias;
        if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
            entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
        } else {
            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
        }

        @SuppressWarnings("unchecked")
        T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
                mKeyStore, entryAlias, keyAliasInKeystore);
        return result;
    }

    @Override
    protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
        throw new UnsupportedOperationException(
                "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
                + " " + KeyGenParameterSpec.class.getName());
    }

    @Override
    protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
        throw new UnsupportedOperationException(
                "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
                + " " + KeyGenParameterSpec.class.getName());
    }

    @Override
    protected Key engineTranslateKey(Key arg0) throws InvalidKeyException {
        throw new UnsupportedOperationException(
                "To import a key into Android KeyStore, use KeyStore.setEntry with "
                + KeyProtection.class.getName());
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -53,6 +53,10 @@ public class AndroidKeyStoreProvider extends Provider {
        put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
        put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");

        // java.security.KeyFactory
        putKeyFactoryImpl("EC");
        putKeyFactoryImpl("RSA");

        // javax.crypto.KeyGenerator
        put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
        put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
@@ -101,6 +105,10 @@ public class AndroidKeyStoreProvider extends Provider {
        put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
    }

    private void putKeyFactoryImpl(String algorithm) {
        put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
    }

    /**
     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
     * primitive.
+34 −17
Original line number Diff line number Diff line
@@ -21,9 +21,8 @@ import android.security.KeyStore;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;

import libcore.util.EmptyArray;

import java.security.InvalidKeyException;
import java.security.ProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
@@ -35,7 +34,7 @@ import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.SecretKeySpec;

/**
 * {@link SecretKeyFactorySpi} backed by Android KeyStore.
 * {@link SecretKeyFactorySpi} backed by Android Keystore.
 *
 * @hide
 */
@@ -60,7 +59,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
        if (!KeyInfo.class.equals(keySpecClass)) {
            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
        }
        String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
        String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
        String entryAlias;
        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
@@ -68,11 +67,15 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
        }

        return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore);
    }

    static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) {
        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
        int errorCode =
                mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
                keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
        if (errorCode != KeyStore.NO_ERROR) {
            throw new InvalidKeySpecException("Failed to obtain information about key."
            throw new ProviderException("Failed to obtain information about key."
                    + " Keystore error: " + errorCode);
        }

@@ -81,6 +84,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
        int keySize;
        @KeyProperties.PurposeEnum int purposes;
        String[] encryptionPaddings;
        String[] signaturePaddings;
        @KeyProperties.DigestEnum String[] digests;
        @KeyProperties.BlockModeEnum String[] blockModes;
        int keymasterSwEnforcedUserAuthenticators;
@@ -95,29 +99,40 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
                origin = KeyProperties.Origin.fromKeymaster(
                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
            } else {
                throw new InvalidKeySpecException("Key origin not available");
                throw new ProviderException("Key origin not available");
            }
            Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE);
            if (keySizeInteger == null) {
                throw new InvalidKeySpecException("Key size not available");
                throw new ProviderException("Key size not available");
            }
            keySize = keySizeInteger;
            purposes = KeyProperties.Purpose.allFromKeymaster(
                    keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE));

            List<String> encryptionPaddingsList = new ArrayList<String>();
            List<String> signaturePaddingsList = new ArrayList<String>();
            // Keymaster stores both types of paddings in the same array -- we split it into two.
            for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
                @KeyProperties.EncryptionPaddingEnum String jcaPadding;
                try {
                    jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
                    @KeyProperties.EncryptionPaddingEnum String jcaPadding =
                            KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
                    encryptionPaddingsList.add(jcaPadding);
                } catch (IllegalArgumentException e) {
                    throw new InvalidKeySpecException(
                    try {
                        @KeyProperties.SignaturePaddingEnum String padding =
                                KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
                        signaturePaddingsList.add(padding);
                    } catch (IllegalArgumentException e2) {
                        throw new ProviderException(
                                "Unsupported encryption padding: " + keymasterPadding);
                    }
                encryptionPaddingsList.add(jcaPadding);
                }

            }
            encryptionPaddings =
                    encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
            signaturePaddings =
                    signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);

            digests = KeyProperties.Digest.allFromKeymaster(
                    keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
@@ -128,7 +143,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
            keymasterHwEnforcedUserAuthenticators =
                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
        } catch (IllegalArgumentException e) {
            throw new InvalidKeySpecException("Unsupported key characteristic", e);
            throw new ProviderException("Unsupported key characteristic", e);
        }

        Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
@@ -164,7 +179,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
                keyValidityForConsumptionEnd,
                purposes,
                encryptionPaddings,
                EmptyArray.STRING, // no signature paddings -- this is symmetric crypto
                signaturePaddings,
                digests,
                blockModes,
                userAuthenticationRequired,
@@ -175,12 +190,14 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
    @Override
    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
        throw new UnsupportedOperationException(
                "Key import into Android KeyStore is not supported");
                "To generate secret key in Android KeyStore, use KeyGenerator initialized with "
                        + KeyGenParameterSpec.class.getName());
    }

    @Override
    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
        throw new UnsupportedOperationException(
                "Key import into Android KeyStore is not supported");
                "To import a secret key into Android KeyStore, use KeyStore.setEntry with "
                + KeyProtection.class.getName());
    }
}