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