diff options
8 files changed, 241 insertions, 84 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 23f490aa6275..f9d181e1c966 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12134,6 +12134,7 @@ package android.content.pm { field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels"; field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris"; + field public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = "android.hardware.keystore.app_attest_key"; field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key"; field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key"; field public static final String FEATURE_LEANBACK = "android.software.leanback"; @@ -36245,6 +36246,7 @@ package android.security.keystore { public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec { method @Nullable public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec(); + method @Nullable public String getAttestKeyAlias(); method public byte[] getAttestationChallenge(); method @NonNull public String[] getBlockModes(); method @NonNull public java.util.Date getCertificateNotAfter(); @@ -36279,6 +36281,7 @@ package android.security.keystore { ctor public KeyGenParameterSpec.Builder(@NonNull String, int); method @NonNull public android.security.keystore.KeyGenParameterSpec build(); method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(@NonNull java.security.spec.AlgorithmParameterSpec); + method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestKeyAlias(@Nullable String); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(@NonNull java.util.Date); @@ -36376,6 +36379,7 @@ package android.security.keystore { field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8 field public static final int ORIGIN_UNKNOWN = 4; // 0x4 field public static final int PURPOSE_AGREE_KEY = 64; // 0x40 + field public static final int PURPOSE_ATTEST_KEY = 128; // 0x80 field public static final int PURPOSE_DECRYPT = 2; // 0x2 field public static final int PURPOSE_ENCRYPT = 1; // 0x1 field public static final int PURPOSE_SIGN = 4; // 0x4 diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 04e0468fde90..d82f60f1b91f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3280,6 +3280,15 @@ public abstract class PackageManager { public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has + * a Keystore implementation that can create application-specific attestation keys. + * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias}. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = + "android.hardware.keystore.app_attest_key"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index a79b197d3faa..5a89cdf1d340 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -188,6 +188,7 @@ public final class KeymasterDefs { public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY; public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY; public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY; + public static final int KM_PURPOSE_ATTEST_KEY = KeyPurpose.ATTEST_KEY; // Key formats. public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509; diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 2b0d7e53b749..c79c12cd3343 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -279,8 +279,8 @@ import javax.security.auth.x500.X500Principal; * } */ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs { - - private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final X500Principal DEFAULT_CERT_SUBJECT = + new X500Principal("CN=Android Keystore Key"); private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970 private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 @@ -317,6 +317,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mUnlockedDeviceRequired; private final boolean mCriticalToDeviceEncryption; private final int mMaxUsageCount; + private final String mAttestKeyAlias; /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. @@ -358,7 +359,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean userConfirmationRequired, boolean unlockedDeviceRequired, boolean criticalToDeviceEncryption, - int maxUsageCount) { + int maxUsageCount, + String attestKeyAlias) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -413,6 +415,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired = unlockedDeviceRequired; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mMaxUsageCount = maxUsageCount; + mAttestKeyAlias = attestKeyAlias; } /** @@ -869,6 +872,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Returns the alias of the attestation key that will be used to sign the attestation + * certificate of the generated key. Note that an attestation certificate will only be + * generated if an attestation challenge is set. + * + * @see Builder#setAttestKeyAlias(String) + */ + @Nullable + public String getAttestKeyAlias() { + return mAttestKeyAlias; + } + + /** * Builder of {@link KeyGenParameterSpec} instances. */ public final static class Builder { @@ -906,6 +921,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mUnlockedDeviceRequired = false; private boolean mCriticalToDeviceEncryption = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; + private String mAttestKeyAlias = null; /** * Creates a new instance of the {@code Builder}. @@ -975,6 +991,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired(); mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); mMaxUsageCount = sourceSpec.getMaxUsageCount(); + mAttestKeyAlias = sourceSpec.getAttestKeyAlias(); } /** @@ -1695,6 +1712,28 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Sets the alias of the attestation key that will be used to sign the attestation + * certificate for the generated key pair, if an attestation challenge is set with {@link + * #setAttestationChallenge}. If an attestKeyAlias is set but no challenge, {@link + * java.security.KeyPairGenerator#initialize} will throw {@link + * java.security.InvalidAlgorithmParameterException}. + * + * <p>If the attestKeyAlias is set to null (the default), Android Keystore will select an + * appropriate system-provided attestation signing key. If not null, the alias must + * reference an Android Keystore Key that was created with {@link + * android.security.keystore.KeyProperties#PURPOSE_ATTEST_KEY}, or key generation will throw + * {@link java.security.InvalidAlgorithmParameterException}. + * + * @param attestKeyAlias the alias of the attestation key to be used to sign the + * attestation certificate. + */ + @NonNull + public Builder setAttestKeyAlias(@Nullable String attestKeyAlias) { + mAttestKeyAlias = attestKeyAlias; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1731,7 +1770,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserConfirmationRequired, mUnlockedDeviceRequired, mCriticalToDeviceEncryption, - mMaxUsageCount); + mMaxUsageCount, + mAttestKeyAlias); } } } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 293ab05e0b10..7b0fa91380e1 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -70,6 +70,7 @@ public abstract class KeyProperties { PURPOSE_VERIFY, PURPOSE_WRAP_KEY, PURPOSE_AGREE_KEY, + PURPOSE_ATTEST_KEY, }) public @interface PurposeEnum {} @@ -113,6 +114,13 @@ public abstract class KeyProperties { public static final int PURPOSE_AGREE_KEY = 1 << 6; /** + * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning + * that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In + * addition, PURPOSE_ATTEST_KEY may not be specified for imported keys. + */ + public static final int PURPOSE_ATTEST_KEY = 1 << 7; + + /** * @hide */ public static abstract class Purpose { @@ -132,6 +140,8 @@ public abstract class KeyProperties { return KeymasterDefs.KM_PURPOSE_WRAP; case PURPOSE_AGREE_KEY: return KeymasterDefs.KM_PURPOSE_AGREE_KEY; + case PURPOSE_ATTEST_KEY: + return KeymasterDefs.KM_PURPOSE_ATTEST_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } @@ -151,6 +161,8 @@ public abstract class KeyProperties { return PURPOSE_WRAP_KEY; case KeymasterDefs.KM_PURPOSE_AGREE_KEY: return PURPOSE_AGREE_KEY; + case KeymasterDefs.KM_PURPOSE_ATTEST_KEY: + return PURPOSE_ATTEST_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 76ce23efd05b..673491ef523e 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -588,7 +588,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; private boolean mIsStrongBoxBacked = false; - private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; + private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; + private String mAttestKeyAlias = null; /** * Creates a new instance of the {@code Builder}. diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index 1f2f853b67a8..c20cf01a993e 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -110,6 +110,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeBoolean(mSpec.isUnlockedDeviceRequired()); out.writeBoolean(mSpec.isCriticalToDeviceEncryption()); out.writeInt(mSpec.getMaxUsageCount()); + out.writeString(mSpec.getAttestKeyAlias()); } private static Date readDateOrNull(Parcel in) { @@ -170,6 +171,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final boolean unlockedDeviceRequired = in.readBoolean(); final boolean criticalToDeviceEncryption = in.readBoolean(); final int maxUsageCount = in.readInt(); + final String attestKeyAlias = in.readString(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). @@ -205,7 +207,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { userConfirmationRequired, unlockedDeviceRequired, criticalToDeviceEncryption, - maxUsageCount); + maxUsageCount, + attestKeyAlias); } public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 4d27c3454a84..b3bfd6a3a97a 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -20,7 +20,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.KeyPurpose; import android.hardware.security.keymint.SecurityLevel; +import android.hardware.security.keymint.Tag; import android.os.Build; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; @@ -37,9 +39,11 @@ import android.security.keystore.KeyProperties; import android.security.keystore.KeymasterUtils; import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.StrongBoxUnavailableException; +import android.system.keystore2.Authorization; import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyEntryResponse; import android.system.keystore2.KeyMetadata; import android.system.keystore2.ResponseCode; import android.telephony.TelephonyManager; @@ -61,6 +65,7 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -69,6 +74,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; /** * Provides a way to create instances of a KeyPair which will be placed in the @@ -153,6 +159,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private int mKeymasterAlgorithm = -1; private int mKeySizeBits; private SecureRandom mRng; + private KeyDescriptor mAttestKeyDescriptor; private int[] mKeymasterPurposes; private int[] mKeymasterBlockModes; @@ -197,83 +204,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Legacy/deprecated spec KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params; try { - KeyGenParameterSpec.Builder specBuilder; - String specKeyAlgorithm = legacySpec.getKeyType(); - if (specKeyAlgorithm != null) { - // Spec overrides the generator's default key algorithm - try { - keymasterAlgorithm = - KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( - specKeyAlgorithm); - } catch (IllegalArgumentException e) { - throw new InvalidAlgorithmParameterException( - "Invalid key type in parameters", e); - } - } - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - specBuilder = new KeyGenParameterSpec.Builder( - legacySpec.getKeystoreAlias(), - KeyProperties.PURPOSE_SIGN - | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - // MD5 was never offered for Android Keystore for ECDSA. - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - break; - case KeymasterDefs.KM_ALGORITHM_RSA: - specBuilder = new KeyGenParameterSpec.Builder( - legacySpec.getKeystoreAlias(), - KeyProperties.PURPOSE_ENCRYPT - | KeyProperties.PURPOSE_DECRYPT - | KeyProperties.PURPOSE_SIGN - | KeyProperties.PURPOSE_VERIFY); - // Authorized to be used with any digest (including no digest). - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); - // Authorized to be used with any encryption and signature padding - // schemes (including no padding). - specBuilder.setEncryptionPaddings( - KeyProperties.ENCRYPTION_PADDING_NONE, - KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, - KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - specBuilder.setSignaturePaddings( - KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, - KeyProperties.SIGNATURE_PADDING_RSA_PSS); - // Disable randomized encryption requirement to support encryption - // padding NONE above. - specBuilder.setRandomizedEncryptionRequired(false); - break; - default: - throw new ProviderException( - "Unsupported algorithm: " + mKeymasterAlgorithm); - } - - if (legacySpec.getKeySize() != -1) { - specBuilder.setKeySize(legacySpec.getKeySize()); - } - if (legacySpec.getAlgorithmParameterSpec() != null) { - specBuilder.setAlgorithmParameterSpec( - legacySpec.getAlgorithmParameterSpec()); - } - specBuilder.setCertificateSubject(legacySpec.getSubjectDN()); - specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber()); - specBuilder.setCertificateNotBefore(legacySpec.getStartDate()); - specBuilder.setCertificateNotAfter(legacySpec.getEndDate()); - specBuilder.setUserAuthenticationRequired(false); - - spec = specBuilder.build(); + keymasterAlgorithm = getKeymasterAlgorithmFromLegacy(keymasterAlgorithm, + legacySpec); + spec = buildKeyGenParameterSpecFromLegacy(legacySpec, keymasterAlgorithm); } catch (NullPointerException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } @@ -342,6 +275,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mJcaKeyAlgorithm = jcaKeyAlgorithm; mRng = random; mKeyStore = KeyStore2.getInstance(); + + mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec); + checkAttestKeyPurpose(spec); + success = true; } finally { if (!success) { @@ -350,6 +287,156 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } } + private void checkAttestKeyPurpose(KeyGenParameterSpec spec) + throws InvalidAlgorithmParameterException { + if ((spec.getPurposes() & KeyProperties.PURPOSE_ATTEST_KEY) != 0 + && spec.getPurposes() != KeyProperties.PURPOSE_ATTEST_KEY) { + throw new InvalidAlgorithmParameterException( + "PURPOSE_ATTEST_KEY may not be specified with any other purposes"); + } + } + + private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec) + throws InvalidAlgorithmParameterException { + if (spec.getAttestKeyAlias() != null) { + KeyDescriptor attestKeyDescriptor = new KeyDescriptor(); + attestKeyDescriptor.domain = Domain.APP; + attestKeyDescriptor.alias = spec.getAttestKeyAlias(); + try { + KeyEntryResponse attestKey = mKeyStore.getKeyEntry(attestKeyDescriptor); + checkAttestKeyChallenge(spec); + checkAttestKeyPurpose(attestKey.metadata.authorizations); + checkAttestKeySecurityLevel(spec, attestKey); + } catch (KeyStoreException e) { + throw new InvalidAlgorithmParameterException("Invalid attestKeyAlias", e); + } + return attestKeyDescriptor; + } + return null; + } + + private void checkAttestKeyChallenge(KeyGenParameterSpec spec) + throws InvalidAlgorithmParameterException { + if (spec.getAttestationChallenge() == null) { + throw new InvalidAlgorithmParameterException( + "AttestKey specified but no attestation challenge provided"); + } + } + + private void checkAttestKeyPurpose(Authorization[] keyAuths) + throws InvalidAlgorithmParameterException { + Predicate<Authorization> isAttestKeyPurpose = x -> x.keyParameter.tag == Tag.PURPOSE + && x.keyParameter.value.getKeyPurpose() == KeyPurpose.ATTEST_KEY; + + if (Arrays.stream(keyAuths).noneMatch(isAttestKeyPurpose)) { + throw new InvalidAlgorithmParameterException( + ("Invalid attestKey, does not have PURPOSE_ATTEST_KEY")); + } + } + + private void checkAttestKeySecurityLevel(KeyGenParameterSpec spec, KeyEntryResponse key) + throws InvalidAlgorithmParameterException { + boolean attestKeyInStrongBox = key.metadata.keySecurityLevel == SecurityLevel.STRONGBOX; + if (spec.isStrongBoxBacked() != attestKeyInStrongBox) { + if (attestKeyInStrongBox) { + throw new InvalidAlgorithmParameterException( + "Invalid security level: Cannot sign non-StrongBox key with " + + "StrongBox attestKey"); + + } else { + throw new InvalidAlgorithmParameterException( + "Invalid security level: Cannot sign StrongBox key with " + + "non-StrongBox attestKey"); + } + } + } + + private int getKeymasterAlgorithmFromLegacy(int keymasterAlgorithm, + KeyPairGeneratorSpec legacySpec) throws InvalidAlgorithmParameterException { + String specKeyAlgorithm = legacySpec.getKeyType(); + if (specKeyAlgorithm != null) { + // Spec overrides the generator's default key algorithm + try { + keymasterAlgorithm = + KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( + specKeyAlgorithm); + } catch (IllegalArgumentException e) { + throw new InvalidAlgorithmParameterException( + "Invalid key type in parameters", e); + } + } + return keymasterAlgorithm; + } + + private KeyGenParameterSpec buildKeyGenParameterSpecFromLegacy(KeyPairGeneratorSpec legacySpec, + int keymasterAlgorithm) { + KeyGenParameterSpec.Builder specBuilder; + switch (keymasterAlgorithm) { + case KeymasterDefs.KM_ALGORITHM_EC: + specBuilder = new KeyGenParameterSpec.Builder( + legacySpec.getKeystoreAlias(), + KeyProperties.PURPOSE_SIGN + | KeyProperties.PURPOSE_VERIFY); + // Authorized to be used with any digest (including no digest). + // MD5 was never offered for Android Keystore for ECDSA. + specBuilder.setDigests( + KeyProperties.DIGEST_NONE, + KeyProperties.DIGEST_SHA1, + KeyProperties.DIGEST_SHA224, + KeyProperties.DIGEST_SHA256, + KeyProperties.DIGEST_SHA384, + KeyProperties.DIGEST_SHA512); + break; + case KeymasterDefs.KM_ALGORITHM_RSA: + specBuilder = new KeyGenParameterSpec.Builder( + legacySpec.getKeystoreAlias(), + KeyProperties.PURPOSE_ENCRYPT + | KeyProperties.PURPOSE_DECRYPT + | KeyProperties.PURPOSE_SIGN + | KeyProperties.PURPOSE_VERIFY); + // Authorized to be used with any digest (including no digest). + specBuilder.setDigests( + KeyProperties.DIGEST_NONE, + KeyProperties.DIGEST_MD5, + KeyProperties.DIGEST_SHA1, + KeyProperties.DIGEST_SHA224, + KeyProperties.DIGEST_SHA256, + KeyProperties.DIGEST_SHA384, + KeyProperties.DIGEST_SHA512); + // Authorized to be used with any encryption and signature padding + // schemes (including no padding). + specBuilder.setEncryptionPaddings( + KeyProperties.ENCRYPTION_PADDING_NONE, + KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, + KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); + specBuilder.setSignaturePaddings( + KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, + KeyProperties.SIGNATURE_PADDING_RSA_PSS); + // Disable randomized encryption requirement to support encryption + // padding NONE above. + specBuilder.setRandomizedEncryptionRequired(false); + break; + default: + throw new ProviderException( + "Unsupported algorithm: " + mKeymasterAlgorithm); + } + + if (legacySpec.getKeySize() != -1) { + specBuilder.setKeySize(legacySpec.getKeySize()); + } + if (legacySpec.getAlgorithmParameterSpec() != null) { + specBuilder.setAlgorithmParameterSpec( + legacySpec.getAlgorithmParameterSpec()); + } + specBuilder.setCertificateSubject(legacySpec.getSubjectDN()); + specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber()); + specBuilder.setCertificateNotBefore(legacySpec.getStartDate()); + specBuilder.setCertificateNotAfter(legacySpec.getEndDate()); + specBuilder.setUserAuthenticationRequired(false); + + return specBuilder.build(); + } + private void resetAll() { mEntryAlias = null; mEntryUid = KeyProperties.NAMESPACE_APPLICATION; @@ -464,7 +551,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato try { KeyStoreSecurityLevel iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel); - KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, null, + KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, mAttestKeyDescriptor, constructKeyGenerationArguments(), flags, additionalEntropy); AndroidKeyStorePublicKey publicKey = |