diff options
author | Janis Danisevskis <jdanis@google.com> | 2020-10-11 15:01:48 -0700 |
---|---|---|
committer | Janis Danisevskis <jdanis@google.com> | 2020-11-13 19:55:40 -0800 |
commit | 38ab78f0a0b8e123022fe52a7a8ff0943ddd3690 (patch) | |
tree | 01b7b94abc50da9462dbf9c5ce0a3c5fb009204e /keystore | |
parent | 4545933da590ad92faaf075623a0d00a75debfdd (diff) |
Keystore 2.0 SPI: AndroidKeyStoreProvider loads keys from Keystore 2.0
This patch adjusts the AndroidKeyStoreProvider to register all services
with the correct packages names. And the utility functions load key
using the correct Keystore 2.0 methods.
Bug: 159476414
Test: None
Change-Id: I9268fd66d28e89e188e85991bcf90c7f19809232
Diffstat (limited to 'keystore')
-rw-r--r-- | keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java | 2 | ||||
-rw-r--r-- | keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java | 333 |
2 files changed, 148 insertions, 187 deletions
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java index 5b6971007077..dd943d422e62 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java @@ -40,7 +40,7 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { // classes when this provider is instantiated and installed early on during each app's // initialization process. - private static final String PACKAGE_NAME = "android.security.keystore"; + private static final String PACKAGE_NAME = "android.security.keystore2"; private static final String KEYSTORE_SECRET_KEY_CLASS_NAME = PACKAGE_NAME + ".AndroidKeyStoreSecretKey"; private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME = diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index b759733a7573..e7fcbdb84ab3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -17,36 +17,33 @@ package android.security.keystore2; import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.compat.annotation.UnsupportedAppUsage; import android.security.KeyStore; -import android.security.keymaster.ExportResult; -import android.security.keymaster.KeyCharacteristics; +import android.security.KeyStore2; +import android.security.KeyStoreSecurityLevel; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.security.keystore.KeyStoreCryptoOperation; +import android.system.keystore2.Authorization; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyEntryResponse; +import android.system.keystore2.KeyMetadata; +import android.system.keystore2.ResponseCode; -import java.io.IOException; import java.security.KeyFactory; import java.security.KeyPair; -import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.Provider; import java.security.ProviderException; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.interfaces.ECKey; import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; -import java.util.List; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -56,7 +53,6 @@ import javax.crypto.Mac; * * @hide */ -@SystemApi public class AndroidKeyStoreProvider extends Provider { private static final String PROVIDER_NAME = "AndroidKeyStore"; @@ -68,7 +64,7 @@ public class AndroidKeyStoreProvider extends Provider { // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc // for details. - private static final String PACKAGE_NAME = "android.security.keystore"; + private static final String PACKAGE_NAME = "android.security.keystore2"; private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede"; @@ -165,7 +161,6 @@ public class AndroidKeyStoreProvider extends Provider { * @throws IllegalStateException if the provided primitive is not initialized. * @hide */ - @UnsupportedAppUsage public static long getKeyStoreOperationHandle(Object cryptoPrimitive) { if (cryptoPrimitive == null) { throw new NullPointerException(); @@ -191,192 +186,188 @@ public class AndroidKeyStoreProvider extends Provider { return ((KeyStoreCryptoOperation) spi).getOperationHandle(); } - /** @hide **/ - @NonNull - public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey( - @NonNull String alias, - int uid, - @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm, - @NonNull byte[] x509EncodedForm) { - PublicKey publicKey; - try { - KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); - publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm)); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain " + keyAlgorithm + " KeyFactory", e); - } catch (InvalidKeySpecException e) { - throw new ProviderException("Invalid X.509 encoding of public key", e); - } - if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey); - } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey); - } else { - throw new ProviderException("Unsupported Android Keystore public key algorithm: " - + keyAlgorithm); - } - } - - @NonNull - private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey( - @NonNull AndroidKeyStorePublicKey publicKey) { - String keyAlgorithm = publicKey.getAlgorithm(); - if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreECPrivateKey( - publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams()); - } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { - return new AndroidKeyStoreRSAPrivateKey( - publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus()); - } else { - throw new ProviderException("Unsupported Android Keystore public key algorithm: " - + keyAlgorithm); - } - } - - @NonNull - private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore, - @NonNull String alias, int uid) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); - int errorCode = keyStore.getKeyCharacteristics( - alias, null, null, uid, keyCharacteristics); - if (errorCode == KeyStore.KEY_PERMANENTLY_INVALIDATED) { - throw (KeyPermanentlyInvalidatedException) - new KeyPermanentlyInvalidatedException( - "User changed or deleted their auth credentials", - KeyStore.getKeyStoreException(errorCode)); - } - if (errorCode != KeyStore.NO_ERROR) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain information about key") - .initCause(KeyStore.getKeyStoreException(errorCode)); - } - return keyCharacteristics; - } - + /** + * This helper function gets called if the key loaded from the keystore daemon + * is for an asymmetric algorithm. It constructs an instance of {@link AndroidKeyStorePublicKey} + * which implements {@link PublicKey}. + * + * @param descriptor The original key descriptor that was used to load the key. + * + * @param metadata The key metadata which includes the public key material, a reference to the + * stored private key material, the key characteristics. + * @param iSecurityLevel A binder interface that allows using the private key. + * @param algorithm Must indicate EC or RSA. + * @return AndroidKeyStorePublicKey + * @throws UnrecoverableKeyException + * @hide + */ @NonNull - private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - KeyCharacteristics keyCharacteristics) + static AndroidKeyStorePublicKey makeAndroidKeyStorePublicKeyFromKeyEntryResponse( + @NonNull KeyDescriptor descriptor, + @NonNull KeyMetadata metadata, + @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm) throws UnrecoverableKeyException { - ExportResult exportResult = keyStore.exportKey( - privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid); - if (exportResult.resultCode != KeyStore.NO_ERROR) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain X.509 form of public key") - .initCause(KeyStore.getKeyStoreException(exportResult.resultCode)); - } - final byte[] x509EncodedPublicKey = exportResult.exportData; - - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); - if (keymasterAlgorithm == null) { - throw new UnrecoverableKeyException("Key algorithm unknown"); + if (metadata.certificate == null) { + throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key." + + " Keystore has no public certificate stored."); } + final byte[] x509EncodedPublicKey = metadata.certificate; String jcaKeyAlgorithm; try { jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm( - keymasterAlgorithm); + algorithm); } catch (IllegalArgumentException e) { throw (UnrecoverableKeyException) new UnrecoverableKeyException("Failed to load private key") - .initCause(e); + .initCause(e); + } + + PublicKey publicKey; + try { + KeyFactory keyFactory = KeyFactory.getInstance(jcaKeyAlgorithm); + publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedPublicKey)); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException( + "Failed to obtain " + jcaKeyAlgorithm + " KeyFactory", e); + } catch (InvalidKeySpecException e) { + throw new ProviderException("Invalid X.509 encoding of public key", e); } - return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( - privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey); + KeyStoreSecurityLevel securityLevel = iSecurityLevel; + if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { + + return new AndroidKeyStoreECPublicKey(descriptor, metadata, + iSecurityLevel, (ECPublicKey) publicKey); + } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) { + return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, + iSecurityLevel, (RSAPublicKey) publicKey); + } else { + throw new ProviderException("Unsupported Android Keystore public key algorithm: " + + jcaKeyAlgorithm); + } } /** @hide **/ @NonNull public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) + @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); - } - - @NonNull - private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - @NonNull KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - AndroidKeyStorePublicKey publicKey = - loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid, - keyCharacteristics); - AndroidKeyStorePrivateKey privateKey = - AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey); - return new KeyPair(publicKey, privateKey); + AndroidKeyStoreKey key = + loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); + if (key instanceof AndroidKeyStorePublicKey) { + return (AndroidKeyStorePublicKey) key; + } else { + throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); + } } /** @hide **/ @NonNull public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) + @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); - } - - @NonNull - private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid, - @NonNull KeyCharacteristics keyCharacteristics) - throws UnrecoverableKeyException { - KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid, - keyCharacteristics); - return (AndroidKeyStorePrivateKey) keyPair.getPrivate(); + AndroidKeyStoreKey key = + loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); + if (key instanceof AndroidKeyStorePublicKey) { + AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; + return new KeyPair(publicKey, publicKey.getPrivateKey()); + } else { + throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); + } } /** @hide **/ @NonNull public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid) + @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid, - getKeyCharacteristics(keyStore, privateKeyAlias, uid)); + AndroidKeyStoreKey key = + loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); + if (key instanceof AndroidKeyStorePublicKey) { + return ((AndroidKeyStorePublicKey) key).getPrivateKey(); + } else { + throw new UnrecoverableKeyException("No asymmetric key found by the given alias."); + } } + @NonNull - private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore( - @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics) + private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( + @NonNull KeyDescriptor descriptor, + @NonNull KeyEntryResponse response, int algorithm, int digest) throws UnrecoverableKeyException { - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); - if (keymasterAlgorithm == null) { - throw new UnrecoverableKeyException("Key algorithm unknown"); - } - - List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST); - int keymasterDigest; - if (keymasterDigests.isEmpty()) { - keymasterDigest = -1; - } else { - // More than one digest can be permitted for this key. Use the first one to form the - // JCA key algorithm name. - keymasterDigest = keymasterDigests.get(0); - } @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; try { keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( - keymasterAlgorithm, keymasterDigest); + algorithm, digest); } catch (IllegalArgumentException e) { throw (UnrecoverableKeyException) new UnrecoverableKeyException("Unsupported secret key type").initCause(e); } - return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString); + return new AndroidKeyStoreSecretKey(descriptor, + response.metadata, keyAlgorithmString, + new KeyStoreSecurityLevel(response.iSecurityLevel)); } - /** @hide **/ + /** + * Loads an an AndroidKeyStoreKey from the AndroidKeyStore backend. + * + * @param keyStore The keystore2 backend. + * @param alias The alias of the key in the Keystore database. + * @param namespace The a Keystore namespace. This is used by system api only to request + * Android system specific keystore namespace, which can be configured + * in the device's SEPolicy. Third party apps and most system components + * set this parameter to -1 to indicate their application specific namespace. + * TODO b/171806779 link to public Keystore 2.0 documentation. + * See bug for more details for now. + * @hide + **/ @NonNull public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( - @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid) + @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid); - Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM); + KeyDescriptor descriptor = new KeyDescriptor(); + if (namespace == KeyProperties.NAMESPACE_APPLICATION) { + descriptor.nspace = 0; // ignored; + descriptor.domain = Domain.APP; + } else { + descriptor.nspace = namespace; + descriptor.domain = Domain.SELINUX; + } + descriptor.alias = alias; + descriptor.blob = null; + KeyEntryResponse response = null; + try { + response = keyStore.getKeyEntry(descriptor); + } catch (android.security.KeyStoreException e) { + if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) { + throw new KeyPermanentlyInvalidatedException( + "User changed or deleted their auth credentials", + e); + } else { + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Failed to obtain information about key") + .initCause(e); + } + } + + Integer keymasterAlgorithm = null; + // We just need one digest for the algorithm name + int keymasterDigest = -1; + for (Authorization a : response.metadata.authorizations) { + switch (a.keyParameter.tag) { + case KeymasterDefs.KM_TAG_ALGORITHM: + keymasterAlgorithm = a.keyParameter.integer; + break; + case KeymasterDefs.KM_TAG_DIGEST: + if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer; + break; + } + } if (keymasterAlgorithm == null) { throw new UnrecoverableKeyException("Key algorithm unknown"); } @@ -384,45 +375,15 @@ public class AndroidKeyStoreProvider extends Provider { if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC || keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES || keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { - return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid, - keyCharacteristics); + return makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(descriptor, response, + keymasterAlgorithm, keymasterDigest); } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { - return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid, - keyCharacteristics); + return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, + new KeyStoreSecurityLevel(response.iSecurityLevel), + keymasterAlgorithm); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } } - - /** - * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID. - * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID - * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) - * all of which are system. - * - * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is - * no need to invoke {@code load} on it. - * - * @param uid Uid for which the keystore provider is requested. - * @throws KeyStoreException if a KeyStoreSpi implementation for the specified type is not - * available from the specified provider. - * @throws NoSuchProviderException If the specified provider is not registered in the security - * provider list. - * @hide - */ - @SystemApi - @NonNull - public static java.security.KeyStore getKeyStoreForUid(int uid) - throws KeyStoreException, NoSuchProviderException { - java.security.KeyStore result = - java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME); - try { - result.load(new AndroidKeyStoreLoadStoreParameter(uid)); - } catch (NoSuchAlgorithmException | CertificateException | IOException e) { - throw new KeyStoreException( - "Failed to load AndroidKeyStore KeyStore for UID " + uid, e); - } - return result; - } } |