Merge "Add support for app-generated attestation keys."
This commit is contained in:
@@ -12135,6 +12135,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";
|
||||
@@ -36290,6 +36291,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();
|
||||
@@ -36324,6 +36326,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);
|
||||
@@ -36421,6 +36424,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
|
||||
|
||||
@@ -3272,6 +3272,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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -868,6 +871,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
|
||||
return mMaxUsageCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1694,6 +1711,28 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
|
||||
throw new IllegalArgumentException("maxUsageCount is not valid");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
@@ -1731,7 +1770,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
|
||||
mUserConfirmationRequired,
|
||||
mUnlockedDeviceRequired,
|
||||
mCriticalToDeviceEncryption,
|
||||
mMaxUsageCount);
|
||||
mMaxUsageCount,
|
||||
mAttestKeyAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public abstract class KeyProperties {
|
||||
PURPOSE_VERIFY,
|
||||
PURPOSE_WRAP_KEY,
|
||||
PURPOSE_AGREE_KEY,
|
||||
PURPOSE_ATTEST_KEY,
|
||||
})
|
||||
public @interface PurposeEnum {}
|
||||
|
||||
@@ -112,6 +113,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
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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>() {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user