Loading keystore/java/android/security/IKeyChainService.aidl +0 −2 Original line number Diff line number Diff line Loading @@ -37,8 +37,6 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags, out KeymasterCertificateChain chain); boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager Loading keystore/java/android/security/KeyChain.java +44 −6 Original line number Diff line number Diff line Loading @@ -44,6 +44,8 @@ import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.system.keystore2.Domain; import android.system.keystore2.KeyDescriptor; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; Loading Loading @@ -682,6 +684,33 @@ public final class KeyChain { return null; } /** * This prefix is used to disambiguate grant aliase strings from normal key alias strings. * Technically, a key alias string can use the same prefix. However, a collision does not * lead to privilege escalation, because grants are access controlled in the Keystore daemon. * @hide */ public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:"; private static KeyDescriptor getGrantDescriptor(String keyid) { KeyDescriptor result = new KeyDescriptor(); result.domain = Domain.GRANT; result.blob = null; result.alias = null; try { result.nspace = Long.parseUnsignedLong( keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */); } catch (NumberFormatException e) { return null; } return result; } /** @hide */ public static String getGrantString(KeyDescriptor key) { return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace); } /** @hide */ @Nullable @WorkerThread public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias) Loading @@ -705,11 +734,23 @@ public final class KeyChain { if (keyId == null) { return null; } if (AndroidKeyStoreProvider.isKeystore2Enabled()) { try { return android.security.keystore2.AndroidKeyStoreProvider .loadAndroidKeyStoreKeyPairFromKeystore( KeyStore2.getInstance(), getGrantDescriptor(keyId)); } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } else { try { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( KeyStore.getInstance(), keyId, KeyStore.UID_SELF); } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } Loading Loading @@ -827,11 +868,8 @@ public final class KeyChain { @Deprecated public static boolean isBoundKeyAlgorithm( @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { if (!isKeyAlgorithmSupported(algorithm)) { return false; } return KeyStore.getInstance().isHardwareBacked(algorithm); // All supported algorithms are hardware backed. Individual keys may not be. return true; } /** @hide */ Loading keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +16 −4 Original line number Diff line number Diff line Loading @@ -273,10 +273,10 @@ public class AndroidKeyStoreProvider extends Provider { /** @hide **/ @NonNull public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; return new KeyPair(publicKey, publicKey.getPrivateKey()); Loading Loading @@ -348,6 +348,18 @@ public class AndroidKeyStoreProvider extends Provider { } descriptor.alias = alias; descriptor.blob = null; final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { return ((AndroidKeyStorePublicKey) key).getPrivateKey(); } else { return key; } } private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyEntryResponse response = null; try { response = keyStore.getKeyEntry(descriptor); Loading Loading @@ -397,7 +409,7 @@ public class AndroidKeyStoreProvider extends Provider { keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, new KeyStoreSecurityLevel(response.iSecurityLevel), keymasterAlgorithm).getPrivateKey(); keymasterAlgorithm); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +43 −22 Original line number Diff line number Diff line Loading @@ -333,6 +333,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -340,6 +341,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.util.ArrayList; Loading Loading @@ -5622,7 +5626,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Get attestation flags, if any. final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags); final boolean deviceIdAttestationRequired = attestationUtilsFlags != null; final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); final String alias = keySpec.getKeystoreAlias(); Preconditions.checkStringNotEmpty(alias, "Empty alias provided"); Loading @@ -5643,6 +5647,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); } if (TextUtils.isEmpty(alias)) { throw new IllegalArgumentException("Empty alias provided."); } // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { Loading @@ -5651,19 +5658,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (deviceIdAttestationRequired) { if (keySpec.getAttestationChallenge() == null) { throw new IllegalArgumentException( "Requested Device ID attestation but challenge is empty."); } KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec); specBuilder.setAttestationIds(attestationUtilsFlags); specBuilder.setDevicePropertiesAttestationIncluded(true); keySpec = specBuilder.build(); } final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); try { try (KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, caller.getUserHandle())) { KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); // Copy the provided keySpec, excluding the attestation challenge, which will be // used later for requesting key attestation record. final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder( keySpec).setAttestationChallenge(null).build(); final int generationResult = keyChain.generateKeyPair(algorithm, new ParcelableKeyGenParameterSpec(noAttestationSpec)); new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { Log.e(LOG_TAG, String.format( "KeyChain failed to generate a keypair, error %d.", generationResult)); Loading @@ -5672,6 +5686,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new ServiceSpecificException( DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE, String.format("KeyChain error: %d", generationResult)); case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS: throw new UnsupportedOperationException( "Device does not support Device ID attestation."); default: logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; Loading @@ -5685,23 +5702,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // that UID. keyChain.setGrant(caller.getUid(), alias, true); final byte[] attestationChallenge = keySpec.getAttestationChallenge(); if (attestationChallenge != null) { final int attestationResult = keyChain.attestKey( alias, attestationChallenge, attestationUtilsFlags, attestationChain); if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) { Log.e(LOG_TAG, String.format( "Attestation for %s failed (rc=%d), deleting key.", alias, attestationResult)); keyChain.removeKeyPair(alias); if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) { throw new UnsupportedOperationException( "Device does not support Device ID attestation."); try { final List<byte[]> encodedCerts = new ArrayList(); final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); final byte[] certChainBytes = keyChain.getCaCertificates(alias); encodedCerts.add(keyChain.getCertificate(alias)); if (certChainBytes != null) { final Collection<X509Certificate> certs = (Collection<X509Certificate>) certFactory.generateCertificates( new ByteArrayInputStream(certChainBytes)); for (X509Certificate cert : certs) { encodedCerts.add(cert.getEncoded()); } } attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); } catch (CertificateException e) { logGenerateKeyPairFailure(caller, isCredentialManagementApp); Log.e(LOG_TAG, "While retrieving certificate chain.", e); return false; } } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR) .setAdmin(caller.getPackageName()) Loading Loading
keystore/java/android/security/IKeyChainService.aidl +0 −2 Original line number Diff line number Diff line Loading @@ -37,8 +37,6 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags, out KeymasterCertificateChain chain); boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager Loading
keystore/java/android/security/KeyChain.java +44 −6 Original line number Diff line number Diff line Loading @@ -44,6 +44,8 @@ import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.system.keystore2.Domain; import android.system.keystore2.KeyDescriptor; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; Loading Loading @@ -682,6 +684,33 @@ public final class KeyChain { return null; } /** * This prefix is used to disambiguate grant aliase strings from normal key alias strings. * Technically, a key alias string can use the same prefix. However, a collision does not * lead to privilege escalation, because grants are access controlled in the Keystore daemon. * @hide */ public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:"; private static KeyDescriptor getGrantDescriptor(String keyid) { KeyDescriptor result = new KeyDescriptor(); result.domain = Domain.GRANT; result.blob = null; result.alias = null; try { result.nspace = Long.parseUnsignedLong( keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */); } catch (NumberFormatException e) { return null; } return result; } /** @hide */ public static String getGrantString(KeyDescriptor key) { return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace); } /** @hide */ @Nullable @WorkerThread public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias) Loading @@ -705,11 +734,23 @@ public final class KeyChain { if (keyId == null) { return null; } if (AndroidKeyStoreProvider.isKeystore2Enabled()) { try { return android.security.keystore2.AndroidKeyStoreProvider .loadAndroidKeyStoreKeyPairFromKeystore( KeyStore2.getInstance(), getGrantDescriptor(keyId)); } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } else { try { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( KeyStore.getInstance(), keyId, KeyStore.UID_SELF); } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } Loading Loading @@ -827,11 +868,8 @@ public final class KeyChain { @Deprecated public static boolean isBoundKeyAlgorithm( @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { if (!isKeyAlgorithmSupported(algorithm)) { return false; } return KeyStore.getInstance().isHardwareBacked(algorithm); // All supported algorithms are hardware backed. Individual keys may not be. return true; } /** @hide */ Loading
keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +16 −4 Original line number Diff line number Diff line Loading @@ -273,10 +273,10 @@ public class AndroidKeyStoreProvider extends Provider { /** @hide **/ @NonNull public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; return new KeyPair(publicKey, publicKey.getPrivateKey()); Loading Loading @@ -348,6 +348,18 @@ public class AndroidKeyStoreProvider extends Provider { } descriptor.alias = alias; descriptor.blob = null; final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { return ((AndroidKeyStorePublicKey) key).getPrivateKey(); } else { return key; } } private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyEntryResponse response = null; try { response = keyStore.getKeyEntry(descriptor); Loading Loading @@ -397,7 +409,7 @@ public class AndroidKeyStoreProvider extends Provider { keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, new KeyStoreSecurityLevel(response.iSecurityLevel), keymasterAlgorithm).getPrivateKey(); keymasterAlgorithm); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +43 −22 Original line number Diff line number Diff line Loading @@ -333,6 +333,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -340,6 +341,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.util.ArrayList; Loading Loading @@ -5622,7 +5626,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Get attestation flags, if any. final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags); final boolean deviceIdAttestationRequired = attestationUtilsFlags != null; final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); final String alias = keySpec.getKeystoreAlias(); Preconditions.checkStringNotEmpty(alias, "Empty alias provided"); Loading @@ -5643,6 +5647,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); } if (TextUtils.isEmpty(alias)) { throw new IllegalArgumentException("Empty alias provided."); } // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { Loading @@ -5651,19 +5658,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (deviceIdAttestationRequired) { if (keySpec.getAttestationChallenge() == null) { throw new IllegalArgumentException( "Requested Device ID attestation but challenge is empty."); } KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec); specBuilder.setAttestationIds(attestationUtilsFlags); specBuilder.setDevicePropertiesAttestationIncluded(true); keySpec = specBuilder.build(); } final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); try { try (KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, caller.getUserHandle())) { KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); // Copy the provided keySpec, excluding the attestation challenge, which will be // used later for requesting key attestation record. final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder( keySpec).setAttestationChallenge(null).build(); final int generationResult = keyChain.generateKeyPair(algorithm, new ParcelableKeyGenParameterSpec(noAttestationSpec)); new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { Log.e(LOG_TAG, String.format( "KeyChain failed to generate a keypair, error %d.", generationResult)); Loading @@ -5672,6 +5686,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new ServiceSpecificException( DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE, String.format("KeyChain error: %d", generationResult)); case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS: throw new UnsupportedOperationException( "Device does not support Device ID attestation."); default: logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; Loading @@ -5685,23 +5702,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // that UID. keyChain.setGrant(caller.getUid(), alias, true); final byte[] attestationChallenge = keySpec.getAttestationChallenge(); if (attestationChallenge != null) { final int attestationResult = keyChain.attestKey( alias, attestationChallenge, attestationUtilsFlags, attestationChain); if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) { Log.e(LOG_TAG, String.format( "Attestation for %s failed (rc=%d), deleting key.", alias, attestationResult)); keyChain.removeKeyPair(alias); if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) { throw new UnsupportedOperationException( "Device does not support Device ID attestation."); try { final List<byte[]> encodedCerts = new ArrayList(); final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); final byte[] certChainBytes = keyChain.getCaCertificates(alias); encodedCerts.add(keyChain.getCertificate(alias)); if (certChainBytes != null) { final Collection<X509Certificate> certs = (Collection<X509Certificate>) certFactory.generateCertificates( new ByteArrayInputStream(certChainBytes)); for (X509Certificate cert : certs) { encodedCerts.add(cert.getEncoded()); } } attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); } catch (CertificateException e) { logGenerateKeyPairFailure(caller, isCredentialManagementApp); Log.e(LOG_TAG, "While retrieving certificate chain.", e); return false; } } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR) .setAdmin(caller.getPackageName()) Loading