Loading keystore/java/android/security/keystore/AttestationUtils.java +41 −12 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.res.Resources; import android.os.Build; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; Loading @@ -34,9 +33,14 @@ import android.util.ArraySet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.util.Collection; import java.util.Random; import java.util.Set; /** Loading Loading @@ -256,22 +260,47 @@ public abstract class AttestationUtils { @NonNull public static X509Certificate[] attestDeviceIds(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws DeviceIdAttestationException { final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId( context, idTypes, attestationChallenge); String keystoreAlias = generateRandomAlias(); KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) .setDigests(KeyProperties.DIGEST_SHA256) .setAttestationChallenge(attestationChallenge); // Perform attestation. final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain); if (errorCode != KeyStore.NO_ERROR) { throw new DeviceIdAttestationException("Unable to perform attestation", KeyStore.getKeyStoreException(errorCode)); if (idTypes != null) { builder.setAttestationIds(idTypes); builder.setDevicePropertiesAttestationIncluded(true); } try { return parseCertificateChain(outChain); } catch (KeyAttestationException e) { throw new DeviceIdAttestationException(e.getMessage(), e); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); keyPairGenerator.initialize(builder.build()); keyPairGenerator.generateKeyPair(); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); X509Certificate[] certificateChain = (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias); keyStore.deleteEntry(keystoreAlias); return certificateChain; } catch (Exception e) { throw new DeviceIdAttestationException("Unable to perform attestation", e); } } private static String generateRandomAlias() { Random random = new SecureRandom(); StringBuilder builder = new StringBuilder(); // Pick random uppercase letters, A-Z. 20 of them gives us ~94 bits of entropy, which // should prevent any conflicts with app-selected aliases, even for very unlucky users. for (int i = 0; i < 20; ++i) { builder.append(random.nextInt(26) + 'A'); } return builder.toString(); } /** Loading Loading
keystore/java/android/security/keystore/AttestationUtils.java +41 −12 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.res.Resources; import android.os.Build; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; Loading @@ -34,9 +33,14 @@ import android.util.ArraySet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.util.Collection; import java.util.Random; import java.util.Set; /** Loading Loading @@ -256,22 +260,47 @@ public abstract class AttestationUtils { @NonNull public static X509Certificate[] attestDeviceIds(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws DeviceIdAttestationException { final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId( context, idTypes, attestationChallenge); String keystoreAlias = generateRandomAlias(); KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) .setDigests(KeyProperties.DIGEST_SHA256) .setAttestationChallenge(attestationChallenge); // Perform attestation. final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain); if (errorCode != KeyStore.NO_ERROR) { throw new DeviceIdAttestationException("Unable to perform attestation", KeyStore.getKeyStoreException(errorCode)); if (idTypes != null) { builder.setAttestationIds(idTypes); builder.setDevicePropertiesAttestationIncluded(true); } try { return parseCertificateChain(outChain); } catch (KeyAttestationException e) { throw new DeviceIdAttestationException(e.getMessage(), e); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); keyPairGenerator.initialize(builder.build()); keyPairGenerator.generateKeyPair(); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); X509Certificate[] certificateChain = (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias); keyStore.deleteEntry(keystoreAlias); return certificateChain; } catch (Exception e) { throw new DeviceIdAttestationException("Unable to perform attestation", e); } } private static String generateRandomAlias() { Random random = new SecureRandom(); StringBuilder builder = new StringBuilder(); // Pick random uppercase letters, A-Z. 20 of them gives us ~94 bits of entropy, which // should prevent any conflicts with app-selected aliases, even for very unlucky users. for (int i = 0; i < 20; ++i) { builder.append(random.nextInt(26) + 'A'); } return builder.toString(); } /** Loading