Merge "Switch Android Keystore key gen and import to new KeyStore API." into mnc-dev
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ad348f8..893771a 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -181,11 +181,15 @@
}
public boolean put(String key, byte[] value, int uid, int flags) {
+ return insert(key, value, uid, flags) == NO_ERROR;
+ }
+
+ public int insert(String key, byte[] value, int uid, int flags) {
try {
- return mBinder.insert(key, value, uid, flags) == NO_ERROR;
+ return mBinder.insert(key, value, uid, flags);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
- return false;
+ return SYSTEM_ERROR;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index e555cc0..f37cf07 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -245,4 +245,12 @@
put("Signature." + algorithm + " SupportedKeyClasses",
KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
}
+
+ public static String[] getSupportedEcdsaSignatureDigests() {
+ return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+ }
+
+ public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() {
+ return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 4d6178f..688936c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -179,11 +179,15 @@
mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
+ if (spec.getSignaturePaddings().length > 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Signature paddings not supported for symmetric key algorithms");
+ }
mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : mKeymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
keymasterBlockMode)) {
throw new InvalidAlgorithmParameterException(
"Randomized encryption (IND-CPA) required but may be violated"
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index c5ea0f7..69155a8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -16,17 +16,39 @@
package android.security.keystore;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERInteger;
+import com.android.org.bouncycastle.asn1.DERNull;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.Certificate;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.org.bouncycastle.asn1.x509.Time;
+import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.org.bouncycastle.jce.X509Principal;
+import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
+import libcore.util.EmptyArray;
+
+import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -41,10 +63,19 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
/**
* Provides a way to create instances of a KeyPair which will be placed in the
@@ -63,13 +94,13 @@
public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
public RSA() {
- super(KeyProperties.KEY_ALGORITHM_RSA);
+ super(KeymasterDefs.KM_ALGORITHM_RSA);
}
}
public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
public EC() {
- super(KeyProperties.KEY_ALGORITHM_EC);
+ super(KeymasterDefs.KM_ALGORITHM_EC);
}
}
@@ -87,39 +118,296 @@
private static final int RSA_MIN_KEY_SIZE = 512;
private static final int RSA_MAX_KEY_SIZE = 8192;
- private final String mAlgorithm;
+ private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
+ new HashMap<String, Integer>();
+ private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
+ static {
+ // Aliases for NIST P-192
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-192", 192);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp192r1", 192);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime192v1", 192);
+
+ // Aliases for NIST P-224
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
+
+ // Aliases for NIST P-256
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+
+ // Aliases for NIST P-384
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
+
+ // Aliases for NIST P-521
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
+
+ SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
+ Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
+ }
+ private final int mOriginalKeymasterAlgorithm;
private KeyStore mKeyStore;
private KeyGenParameterSpec mSpec;
+
+ private String mEntryAlias;
private boolean mEncryptionAtRestRequired;
- private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
- private int mKeyType;
- private int mKeySize;
+ private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
+ private int mKeymasterAlgorithm = -1;
+ private int mKeySizeBits;
+ private SecureRandom mRng;
- protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
- mAlgorithm = algorithm;
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterEncryptionPaddings;
+ private int[] mKeymasterSignaturePaddings;
+ private int[] mKeymasterDigests;
+
+ private long mRSAPublicExponent;
+
+ protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
+ mOriginalKeymasterAlgorithm = keymasterAlgorithm;
}
- @KeyProperties.KeyAlgorithmEnum String getAlgorithm() {
- return mAlgorithm;
+ @Override
+ public void initialize(int keysize, SecureRandom random) {
+ throw new IllegalArgumentException(
+ KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName()
+ + " required to initialize this KeyPairGenerator");
}
- /**
- * Generate a KeyPair which is backed by the Android keystore service. You
- * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
- * with an {@link KeyPairGeneratorSpec} as the {@code params}
- * argument before calling this otherwise an {@code IllegalStateException}
- * will be thrown.
- * <p>
- * This will create an entry in the Android keystore service with a
- * self-signed certificate using the {@code params} specified in the
- * {@code initialize(params)} call.
- *
- * @throws IllegalStateException when called before calling
- * {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
- * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
- */
+ @Override
+ public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if (params == null) {
+ throw new InvalidAlgorithmParameterException(
+ "Must supply params of type " + KeyGenParameterSpec.class.getName()
+ + " or " + KeyPairGeneratorSpec.class.getName());
+ }
+
+ KeyGenParameterSpec spec;
+ boolean encryptionAtRestRequired = false;
+ int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
+ if (params instanceof KeyGenParameterSpec) {
+ spec = (KeyGenParameterSpec) params;
+ } else if (params instanceof KeyPairGeneratorSpec) {
+ // 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);
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_MD5,
+ 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);
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_MD5,
+ KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512);
+ specBuilder.setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ specBuilder.setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_NONE,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ // 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());
+ encryptionAtRestRequired = legacySpec.isEncryptionRequired();
+ specBuilder.setUserAuthenticationRequired(false);
+
+ spec = specBuilder.build();
+ } catch (NullPointerException | IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+ } else {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported params class: " + params.getClass().getName()
+ + ". Supported: " + KeyGenParameterSpec.class.getName()
+ + ", " + KeyPairGeneratorSpec.class.getName());
+ }
+
+ mEntryAlias = spec.getKeystoreAlias();
+ mSpec = spec;
+ mKeymasterAlgorithm = keymasterAlgorithm;
+ mEncryptionAtRestRequired = encryptionAtRestRequired;
+ mKeySizeBits = spec.getKeySize();
+ initAlgorithmSpecificParameters();
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
+ }
+ checkValidKeySize(keymasterAlgorithm, mKeySizeBits);
+
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+
+ String jcaKeyAlgorithm;
+ try {
+ jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
+ keymasterAlgorithm);
+ mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+ mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+ mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster(
+ spec.getSignaturePaddings());
+ if (spec.isDigestsSpecified()) {
+ mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+ } else {
+ mKeymasterDigests = EmptyArray.INT;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+
+ mJcaKeyAlgorithm = jcaKeyAlgorithm;
+ mRng = random;
+ mKeyStore = KeyStore.getInstance();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void resetAll() {
+ mEntryAlias = null;
+ mJcaKeyAlgorithm = null;
+ mKeymasterAlgorithm = -1;
+ mKeymasterPurposes = null;
+ mKeymasterBlockModes = null;
+ mKeymasterEncryptionPaddings = null;
+ mKeymasterSignaturePaddings = null;
+ mKeymasterDigests = null;
+ mKeySizeBits = 0;
+ mSpec = null;
+ mRSAPublicExponent = -1;
+ mEncryptionAtRestRequired = false;
+ mRng = null;
+ mKeyStore = null;
+ }
+
+ private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
+ AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec();
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ {
+ BigInteger publicExponent = null;
+ if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
+ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = rsaSpec.getKeysize();
+ } else if (mKeySizeBits != rsaSpec.getKeysize()) {
+ throw new InvalidAlgorithmParameterException("RSA key size must match "
+ + " between " + mSpec + " and " + algSpecificSpec
+ + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize());
+ }
+ publicExponent = rsaSpec.getPublicExponent();
+ } else if (algSpecificSpec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "RSA may only use RSAKeyGenParameterSpec");
+ }
+ if (publicExponent == null) {
+ publicExponent = RSAKeyGenParameterSpec.F4;
+ }
+ if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
+ throw new InvalidAlgorithmParameterException(
+ "RSA public exponent must be positive: " + publicExponent);
+ }
+ if (publicExponent.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported RSA public exponent: " + publicExponent
+ + ". Only exponents <= " + Long.MAX_VALUE + " supported");
+ }
+ mRSAPublicExponent = publicExponent.longValue();
+ break;
+ }
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ if (algSpecificSpec instanceof ECGenParameterSpec) {
+ ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
+ String curveName = ecSpec.getName();
+ Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
+ curveName.toLowerCase(Locale.US));
+ if (ecSpecKeySizeBits == null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported EC curve name: " + curveName
+ + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
+ }
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = ecSpecKeySizeBits;
+ } else if (mKeySizeBits != ecSpecKeySizeBits) {
+ throw new InvalidAlgorithmParameterException("EC key size must match "
+ + " between " + mSpec + " and " + algSpecificSpec
+ + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits);
+ }
+ } else if (algSpecificSpec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "EC may only use ECGenParameterSpec");
+ }
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+ }
+
@Override
public KeyPair generateKeyPair() {
if (mKeyStore == null || mSpec == null) {
@@ -134,18 +422,65 @@
+ ", but the user has not yet entered the credential");
}
- final String alias = mSpec.getKeystoreAlias();
+ KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
+ args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
+ args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
- byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
+ // TODO: Remove the digest and padding NONE workaround below once Android Keystore returns
+ // keys which are backed by AndroidKeyStoreBCWorkaround provider instead of Conscrypt. The
+ // workaround is needed because Conscrypt (via keystore-engine) uses old KeyStore API which
+ // translates into digest NONE and padding NONE in the new API. keystore-engine cannot be
+ // updated to pass in the correct padding and digest values because it uses
+ // OpenSSL/BoringSSL engine which performs digesting and padding prior before invoking
+ // KeyStore API.
+ if (!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterDigests, KeymasterDefs.KM_DIGEST_NONE)) {
+ args.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+ }
+ if ((!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterSignaturePaddings, KeymasterDefs.KM_PAD_NONE))
+ && (!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterEncryptionPaddings, KeymasterDefs.KM_PAD_NONE))) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ }
- final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+ KeymasterUtils.addUserAuthArgs(args,
+ mSpec.isUserAuthenticationRequired(),
+ mSpec.getUserAuthenticationValidityDurationSeconds());
+ args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+ (mSpec.getKeyValidityStart() != null)
+ ? mSpec.getKeyValidityStart() : new Date(0));
+ args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ (mSpec.getKeyValidityForOriginationEnd() != null)
+ ? mSpec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+ args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ (mSpec.getKeyValidityForConsumptionEnd() != null)
+ ? mSpec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+ addAlgorithmSpecificParameters(args);
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, (mKeySizeBits + 7) / 8);
+
+ final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
- flags, args)) {
- throw new IllegalStateException("could not generate key in keystore");
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+ int errorCode = mKeyStore.generateKey(
+ privateKeyAlias,
+ args,
+ additionalEntropy,
+ flags,
+ resultingKeyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new ProviderException(
+ "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
}
final PrivateKey privKey;
@@ -153,7 +488,7 @@
try {
privKey = engine.getPrivateKeyById(privateKeyAlias);
} catch (InvalidKeyException e) {
- throw new RuntimeException("Can't get key", e);
+ throw new ProviderException("Failed to obtain generated private key", e);
}
ExportResult exportResult =
@@ -163,39 +498,45 @@
throw new KeyStoreConnectException();
} else if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw new ProviderException(
- "Failed to obtain public key in X.509 format",
+ "Failed to obtain X.509 form of generated public key",
KeyStore.getKeyStoreException(exportResult.resultCode));
}
final byte[] pubKeyBytes = exportResult.exportData;
-
final PublicKey pubKey;
try {
- final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
+ final KeyFactory keyFact = KeyFactory.getInstance(mJcaKeyAlgorithm);
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
} catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("Can't instantiate key generator", e);
+ throw new ProviderException(
+ "Failed to obtain " + mJcaKeyAlgorithm + " KeyFactory", e);
} catch (InvalidKeySpecException e) {
- throw new IllegalStateException("keystore returned invalid key encoding", e);
+ throw new ProviderException("Invalid X.509 encoding of generated public key", e);
}
final X509Certificate cert;
try {
- cert = generateCertificate(privKey, pubKey);
+ cert = generateSelfSignedCertificate(privKey, pubKey);
} catch (Exception e) {
- throw new IllegalStateException("Can't generate certificate", e);
+ throw new ProviderException("Failed to generate self-signed certificate", e);
}
byte[] certBytes;
try {
certBytes = cert.getEncoded();
} catch (CertificateEncodingException e) {
- throw new IllegalStateException("Can't get encoding of certificate", e);
+ throw new ProviderException(
+ "Failed to obtain encoded form of self-signed certificate", e);
}
- if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
- flags)) {
- throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+ int insertErrorCode = mKeyStore.insert(
+ Credentials.USER_CERTIFICATE + mEntryAlias,
+ certBytes,
+ KeyStore.UID_SELF,
+ flags);
+ if (insertErrorCode != KeyStore.NO_ERROR) {
+ throw new ProviderException("Failed to store self-signed certificate",
+ KeyStore.getKeyStoreException(insertErrorCode));
}
KeyPair result = new KeyPair(pubKey, privKey);
@@ -203,14 +544,41 @@
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
}
}
}
+ private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ keymasterArgs.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
+ break;
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+ }
+
+ private X509Certificate generateSelfSignedCertificate(
+ PrivateKey privateKey, PublicKey publicKey) throws Exception {
+ String signatureAlgorithm =
+ getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
+ if (signatureAlgorithm == null) {
+ // Key cannot be used to sign a certificate
+ return generateSelfSignedCertificateWithFakeSignature(publicKey);
+ } else {
+ // Key can be used to sign a certificate
+ return generateSelfSignedCertificateWithValidSignature(
+ privateKey, publicKey, signatureAlgorithm);
+ }
+ }
+
@SuppressWarnings("deprecation")
- private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
- throws Exception {
+ private X509Certificate generateSelfSignedCertificateWithValidSignature(
+ PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm)
+ throws Exception {
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(publicKey);
certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
@@ -218,198 +586,223 @@
certGen.setIssuerDN(mSpec.getCertificateSubject());
certGen.setNotBefore(mSpec.getCertificateNotBefore());
certGen.setNotAfter(mSpec.getCertificateNotAfter());
- certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
+ certGen.setSignatureAlgorithm(signatureAlgorithm);
return certGen.generate(privateKey);
}
- @NonNull
- private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
- String result = spec.getKeyType();
- if (result != null) {
- return result;
+ @SuppressWarnings("deprecation")
+ private X509Certificate generateSelfSignedCertificateWithFakeSignature(
+ PublicKey publicKey) throws Exception {
+ V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
+ ASN1ObjectIdentifier sigAlgOid;
+ AlgorithmIdentifier sigAlgId;
+ byte[] signature;
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
+ sigAlgId = new AlgorithmIdentifier(sigAlgOid);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERInteger(0));
+ v.add(new DERInteger(0));
+ signature = new DERSequence().getEncoded();
+ break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption;
+ sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE);
+ signature = new byte[1];
+ break;
+ default:
+ throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm);
}
- return getAlgorithm();
+
+ try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) {
+ tbsGenerator.setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
+ }
+ tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber()));
+ X509Principal subject =
+ new X509Principal(mSpec.getCertificateSubject().getEncoded());
+ tbsGenerator.setSubject(subject);
+ tbsGenerator.setIssuer(subject);
+ tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore()));
+ tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter()));
+ tbsGenerator.setSignature(sigAlgId);
+ TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate();
+
+ ASN1EncodableVector result = new ASN1EncodableVector();
+ result.add(tbsCertificate);
+ result.add(sigAlgId);
+ result.add(new DERBitString(signature));
+ return new X509CertificateObject(Certificate.getInstance(new DERSequence(result)));
}
- private static int getDefaultKeySize(int keyType) {
- if (keyType == NativeConstants.EVP_PKEY_EC) {
- return EC_DEFAULT_KEY_SIZE;
- } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
- return RSA_DEFAULT_KEY_SIZE;
+ private static int getDefaultKeySize(int keymasterAlgorithm) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ return EC_DEFAULT_KEY_SIZE;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ return RSA_DEFAULT_KEY_SIZE;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
}
- return -1;
}
- private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
+ private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
throws InvalidAlgorithmParameterException {
- if (keyType == NativeConstants.EVP_PKEY_EC) {
- if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
- throw new InvalidAlgorithmParameterException("EC keys must be >= "
- + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
- }
- } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
- if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
- throw new InvalidAlgorithmParameterException("RSA keys must be >= "
- + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
- }
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- }
-
- private static void checkCorrectParametersSpec(int keyType, int keySize,
- AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
- if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) {
- if (spec instanceof RSAKeyGenParameterSpec) {
- RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
- if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
- throw new InvalidAlgorithmParameterException("RSA key size must match: "
- + keySize + " vs " + rsaSpec.getKeysize());
- }
- } else {
- throw new InvalidAlgorithmParameterException(
- "RSA may only use RSAKeyGenParameterSpec");
- }
- }
- }
-
- private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
- @KeyProperties.KeyAlgorithmEnum String algorithm) {
- if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
- return "sha256WithRSA";
- } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
- return "sha256WithECDSA";
- } else {
- throw new IllegalArgumentException("Unsupported key type " + algorithm);
- }
- }
-
- private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
- switch (keyType) {
- case NativeConstants.EVP_PKEY_RSA:
- if (spec instanceof RSAKeyGenParameterSpec) {
- RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
- return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+ throw new InvalidAlgorithmParameterException("EC key size must be >= "
+ + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
}
break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+ throw new InvalidAlgorithmParameterException("RSA key size must be >= "
+ + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
+ }
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
}
- return null;
}
- @Override
- public void initialize(int keysize, SecureRandom random) {
- throw new IllegalArgumentException(
- "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
- }
+ /**
+ * Returns the {@code Signature} algorithm to be used for signing a certificate using the
+ * specified key or {@code null} if the key cannot be used for signing a certificate.
+ */
+ @Nullable
+ private static String getCertificateSignatureAlgorithm(
+ int keymasterAlgorithm,
+ int keySizeBits,
+ KeyGenParameterSpec spec) {
+ // Constraints:
+ // 1. Key must be authorized for signing.
+ // 2. Signature digest must be one of key's authorized digests.
+ // 3. For RSA keys, the digest output size must not exceed modulus size minus space needed
+ // for RSA PKCS#1 signature padding (about 29 bytes: minimum 10 bytes of padding + 15--19
+ // bytes overhead for encoding digest OID and digest value in DER).
+ // 4. For EC keys, the there is no point in using a digest whose output size is longer than
+ // key/field size because the digest will be truncated to that size.
- @Override
- public void initialize(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- if (params == null) {
- throw new InvalidAlgorithmParameterException(
- "Must supply params of type " + KeyGenParameterSpec.class.getName()
- + " or " + KeyPairGeneratorSpec.class.getName());
+ if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
+ // Key not authorized for signing
+ return null;
}
+ if (!spec.isDigestsSpecified()) {
+ // Key not authorized for any digests -- can't sign
+ return null;
+ }
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ {
+ Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+ spec.getDigests(),
+ AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
- String keyAlgorithm;
- KeyGenParameterSpec spec;
- boolean encryptionAtRestRequired = false;
- if (params instanceof KeyPairGeneratorSpec) {
- KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
- try {
- KeyGenParameterSpec.Builder specBuilder;
- keyAlgorithm = getKeyAlgorithm(legacySpec).toUpperCase(Locale.US);
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT
- | KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
- specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
- // Disable randomized encryption requirement to support encryption padding NONE
- // above.
- specBuilder.setRandomizedEncryptionRequired(false);
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
+ int bestKeymasterDigest = -1;
+ int bestDigestOutputSizeBits = -1;
+ for (int keymasterDigest : availableKeymasterDigests) {
+ int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+ if (outputSizeBits == keySizeBits) {
+ // Perfect match -- use this digest
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ break;
+ }
+ // Not a perfect match -- check against the best digest so far
+ if (bestKeymasterDigest == -1) {
+ // First digest tested -- definitely the best so far
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ } else {
+ // Prefer output size to be as close to key size as possible, with output
+ // sizes larger than key size preferred to those smaller than key size.
+ if (bestDigestOutputSizeBits < keySizeBits) {
+ // Output size of the best digest so far is smaller than key size.
+ // Anything larger is a win.
+ if (outputSizeBits > bestDigestOutputSizeBits) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ } else {
+ // Output size of the best digest so far is larger than key size.
+ // Anything smaller is a win, as long as it's not smaller than key size.
+ if ((outputSizeBits < bestDigestOutputSizeBits)
+ && (outputSizeBits >= keySizeBits)) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ }
+ }
}
-
- if (legacySpec.getKeySize() != -1) {
- specBuilder.setKeySize(legacySpec.getKeySize());
+ if (bestKeymasterDigest == -1) {
+ return null;
}
- if (legacySpec.getAlgorithmParameterSpec() != null) {
- specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
- }
- specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
- specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
- specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
- specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
- encryptionAtRestRequired = legacySpec.isEncryptionRequired();
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(e);
+ return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+ bestKeymasterDigest) + "WithECDSA";
}
- } else if (params instanceof KeyGenParameterSpec) {
- spec = (KeyGenParameterSpec) params;
- keyAlgorithm = getAlgorithm();
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ {
+ Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+ spec.getDigests(),
+ AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
+
+ // The amount of space available for the digest is less than modulus size because
+ // padding must be at least 10 bytes long, and then there's also the 15--19
+ // bytes overhead for encoding digest OID and digest value in DER.
+ int maxDigestOutputSizeBits = keySizeBits - 29 * 8;
+ int bestKeymasterDigest = -1;
+ int bestDigestOutputSizeBits = -1;
+ for (int keymasterDigest : availableKeymasterDigests) {
+ int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+ if (outputSizeBits > maxDigestOutputSizeBits) {
+ // Digest too long (signature generation will fail) -- skip
+ continue;
+ }
+ if (bestKeymasterDigest == -1) {
+ // First digest tested -- definitely the best so far
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ } else {
+ // The longer the better
+ if (outputSizeBits > bestDigestOutputSizeBits) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ }
+ }
+ if (bestKeymasterDigest == -1) {
+ return null;
+ }
+ return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+ bestKeymasterDigest) + "WithRSA";
+ }
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+ }
+ }
+
+ private static Set<Integer> getAvailableKeymasterSignatureDigests(
+ @KeyProperties.DigestEnum String[] authorizedKeyDigests,
+ @KeyProperties.DigestEnum String[] supportedSignatureDigests) {
+ Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>();
+ for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) {
+ authorizedKeymasterKeyDigests.add(keymasterDigest);
+ }
+ Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>();
+ for (int keymasterDigest
+ : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) {
+ supportedKeymasterSignatureDigests.add(keymasterDigest);
+ }
+ if (authorizedKeymasterKeyDigests.contains(KeymasterDefs.KM_DIGEST_NONE)) {
+ // Key is authorized to be used with any digest
+ return supportedKeymasterSignatureDigests;
} else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported params class: " + params.getClass().getName()
- + ". Supported: " + KeyGenParameterSpec.class.getName()
- + ", " + KeyPairGeneratorSpec.class);
+ // Key is authorized to be used only with specific digests.
+ Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
+ result.retainAll(authorizedKeymasterKeyDigests);
+ return result;
}
-
- int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
- if (keyType == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- int keySize = spec.getKeySize();
- if (keySize == -1) {
- keySize = getDefaultKeySize(keyType);
- if (keySize == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- }
- checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
- checkValidKeySize(keyAlgorithm, keyType, keySize);
-
- mKeyAlgorithm = keyAlgorithm;
- mKeyType = keyType;
- mKeySize = keySize;
- mSpec = spec;
- mEncryptionAtRestRequired = encryptionAtRestRequired;
- mKeyStore = KeyStore.getInstance();
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 7c9c0cf..c03be63 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -22,6 +22,7 @@
import libcore.util.EmptyArray;
import android.security.Credentials;
+import android.security.KeyStore;
import android.security.KeyStoreParameter;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
@@ -39,7 +40,6 @@
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
-import java.security.KeyStore;
import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
@@ -86,7 +86,7 @@
public class AndroidKeyStoreSpi extends KeyStoreSpi {
public static final String NAME = "AndroidKeyStore";
- private android.security.KeyStore mKeyStore;
+ private KeyStore mKeyStore;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
@@ -105,8 +105,7 @@
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
int errorCode = mKeyStore.getKeyCharacteristics(
keyAliasInKeystore, null, null, keyCharacteristics);
- if ((errorCode != KeymasterDefs.KM_ERROR_OK)
- && (errorCode != android.security.KeyStore.NO_ERROR)) {
+ if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to load information about key")
.initCause(mKeyStore.getInvalidKeyException(alias, errorCode));
@@ -272,107 +271,72 @@
}
}
+ private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
+ throws KeyStoreException {
+ String keyAlgorithm = key.getAlgorithm();
+ KeyProtection.Builder specBuilder;
+ if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
+ specBuilder =
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_MD5,
+ KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512);
+ } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
+ specBuilder =
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT
+ | KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY);
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_MD5,
+ KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512);
+ specBuilder.setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ specBuilder.setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_NONE,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ // Disable randomized encryption requirement to support encryption padding NONE
+ // above.
+ specBuilder.setRandomizedEncryptionRequired(false);
+ } else {
+ throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
+ }
+ specBuilder.setUserAuthenticationRequired(false);
+
+ return specBuilder.build();
+ }
+
private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
int flags = 0;
KeyProtection spec;
- if (param instanceof KeyStoreParameter) {
+ if (param == null) {
+ spec = getLegacyKeyProtectionParameter(key);
+ } else if (param instanceof KeyStoreParameter) {
+ spec = getLegacyKeyProtectionParameter(key);
KeyStoreParameter legacySpec = (KeyStoreParameter) param;
- try {
- String keyAlgorithm = key.getAlgorithm();
- KeyProtection.Builder specBuilder;
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder =
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder =
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT
- | KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
- specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
- // Disable randomized encryption requirement to support encryption padding NONE
- // above.
- specBuilder.setRandomizedEncryptionRequired(false);
- } else {
- throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
- }
- if (legacySpec.isEncryptionRequired()) {
- flags = android.security.KeyStore.FLAG_ENCRYPTED;
- }
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new KeyStoreException("Unsupported protection parameter", e);
+ if (legacySpec.isEncryptionRequired()) {
+ flags = KeyStore.FLAG_ENCRYPTED;
}
} else if (param instanceof KeyProtection) {
spec = (KeyProtection) param;
- } else if (param != null) {
+ } else {
throw new KeyStoreException(
"Unsupported protection parameter class:" + param.getClass().getName()
- + ". Supported: " + KeyStoreParameter.class.getName() + ", "
- + KeyProtection.class.getName());
- } else {
- spec = null;
- }
-
- byte[] keyBytes = null;
-
- final String pkeyAlias;
- if (key instanceof OpenSSLKeyHolder) {
- pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
- } else {
- pkeyAlias = null;
- }
-
- final boolean shouldReplacePrivateKey;
- if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
- final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
- if (!alias.equals(keySubalias)) {
- throw new KeyStoreException("Can only replace keys with same alias: " + alias
- + " != " + keySubalias);
- }
-
- shouldReplacePrivateKey = false;
- } else {
- // Make sure the PrivateKey format is the one we support.
- final String keyFormat = key.getFormat();
- if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
- throw new KeyStoreException(
- "Only PrivateKeys that can be encoded into PKCS#8 are supported");
- }
-
- // Make sure we can actually encode the key.
- keyBytes = key.getEncoded();
- if (keyBytes == null) {
- throw new KeyStoreException("PrivateKey has no encoding");
- }
-
- shouldReplacePrivateKey = true;
+ + ". Supported: " + KeyProtection.class.getName() + ", "
+ + KeyStoreParameter.class.getName());
}
// Make sure the chain exists since this is a PrivateKey
@@ -400,7 +364,7 @@
try {
userCertBytes = x509chain[0].getEncoded();
} catch (CertificateEncodingException e) {
- throw new KeyStoreException("Couldn't encode certificate #1", e);
+ throw new KeyStoreException("Failed to encode certificate #0", e);
}
/*
@@ -421,7 +385,7 @@
certsBytes[i] = x509chain[i + 1].getEncoded();
totalCertLength += certsBytes[i].length;
} catch (CertificateEncodingException e) {
- throw new KeyStoreException("Can't encode Certificate #" + i, e);
+ throw new KeyStoreException("Failed to encode certificate #" + i, e);
}
}
@@ -441,31 +405,150 @@
chainBytes = null;
}
- /*
- * Make sure we clear out all the appropriate types before trying to
- * write.
- */
- if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ final String pkeyAlias;
+ if (key instanceof OpenSSLKeyHolder) {
+ pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
+ } else if (key instanceof AndroidKeyStorePrivateKey) {
+ pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
} else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ pkeyAlias = null;
}
- if (shouldReplacePrivateKey
- && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put private key in keystore");
- } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put certificate #1 in keystore");
- } else if (chainBytes != null
- && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put certificate chain in keystore");
+ byte[] pkcs8EncodedPrivateKeyBytes;
+ KeymasterArguments importArgs;
+ final boolean shouldReplacePrivateKey;
+ if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
+ final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
+ if (!alias.equals(keySubalias)) {
+ throw new KeyStoreException("Can only replace keys with same alias: " + alias
+ + " != " + keySubalias);
+ }
+ shouldReplacePrivateKey = false;
+ importArgs = null;
+ pkcs8EncodedPrivateKeyBytes = null;
+ } else {
+ shouldReplacePrivateKey = true;
+ // Make sure the PrivateKey format is the one we support.
+ final String keyFormat = key.getFormat();
+ if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+ throw new KeyStoreException(
+ "Unsupported private key export format: " + keyFormat
+ + ". Only private keys which export their key material in PKCS#8 format are"
+ + " supported.");
+ }
+
+ // Make sure we can actually encode the key.
+ pkcs8EncodedPrivateKeyBytes = key.getEncoded();
+ if (pkcs8EncodedPrivateKeyBytes == null) {
+ throw new KeyStoreException("Private key did not export any key material");
+ }
+
+ importArgs = new KeymasterArguments();
+ try {
+ importArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+ KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+ key.getAlgorithm()));
+ @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
+ importArgs.addInts(KeymasterDefs.KM_TAG_PURPOSE,
+ KeyProperties.Purpose.allToKeymaster(purposes));
+ if (spec.isDigestsSpecified()) {
+ importArgs.addInts(KeymasterDefs.KM_TAG_DIGEST,
+ KeyProperties.Digest.allToKeymaster(spec.getDigests()));
+ }
+
+ importArgs.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
+ int[] keymasterEncryptionPaddings =
+ KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
+ && (spec.isRandomizedEncryptionRequired())) {
+ for (int keymasterPadding : keymasterEncryptionPaddings) {
+ if (!KeymasterUtils
+ .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ keymasterPadding)) {
+ throw new KeyStoreException(
+ "Randomized encryption (IND-CPA) required but is violated by"
+ + " encryption padding mode: "
+ + KeyProperties.EncryptionPadding.fromKeymaster(
+ keymasterPadding)
+ + ". See KeyProtection documentation.");
+ }
+ }
+ }
+ importArgs.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
+ importArgs.addInts(KeymasterDefs.KM_TAG_PADDING,
+ KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
+ KeymasterUtils.addUserAuthArgs(importArgs,
+ spec.isUserAuthenticationRequired(),
+ spec.getUserAuthenticationValidityDurationSeconds());
+ importArgs.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+ (spec.getKeyValidityStart() != null)
+ ? spec.getKeyValidityStart() : new Date(0));
+ importArgs.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ (spec.getKeyValidityForOriginationEnd() != null)
+ ? spec.getKeyValidityForOriginationEnd()
+ : new Date(Long.MAX_VALUE));
+ importArgs.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ (spec.getKeyValidityForConsumptionEnd() != null)
+ ? spec.getKeyValidityForConsumptionEnd()
+ : new Date(Long.MAX_VALUE));
+ } catch (IllegalArgumentException e) {
+ throw new KeyStoreException("Invalid parameter", e);
+ }
+ }
+
+
+ boolean success = false;
+ try {
+ // Store the private key, if necessary
+ if (shouldReplacePrivateKey) {
+ // Delete the stored private key and any related entries before importing the
+ // provided key
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+ int errorCode = mKeyStore.importKey(
+ Credentials.USER_PRIVATE_KEY + alias,
+ importArgs,
+ KeymasterDefs.KM_KEY_FORMAT_PKCS8,
+ pkcs8EncodedPrivateKeyBytes,
+ flags,
+ resultingKeyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store private key",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+ } else {
+ // Keep the stored private key around -- delete all other entry types
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ }
+
+ // Store the leaf certificate
+ int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
+ KeyStore.UID_SELF, flags);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store certificate #0",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+
+ // Store the certificate chain
+ errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
+ KeyStore.UID_SELF, flags);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store certificate chain",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ if (shouldReplacePrivateKey) {
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ } else {
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ }
+ }
}
}
@@ -589,7 +672,8 @@
if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ keymasterBlockMode)) {
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
@@ -598,9 +682,7 @@
}
}
}
- for (int keymasterPurpose : KeyProperties.Purpose.allToKeymaster(purposes)) {
- args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
- }
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
if (params.getSignaturePaddings().length > 0) {
throw new KeyStoreException("Signature paddings not supported for symmetric keys");
@@ -636,7 +718,7 @@
keyMaterial,
0, // flags
new KeyCharacteristics());
- if (errorCode != android.security.KeyStore.NO_ERROR) {
+ if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to import secret key. Keystore error code: "
+ errorCode);
}
@@ -667,7 +749,7 @@
}
if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
- android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
@@ -840,7 +922,7 @@
}
// Unfortunate name collision.
- mKeyStore = android.security.KeyStore.getInstance();
+ mKeyStore = KeyStore.getInstance();
}
@Override
@@ -852,8 +934,9 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- if (entry instanceof KeyStore.TrustedCertificateEntry) {
- KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
+ if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
+ java.security.KeyStore.TrustedCertificateEntry trE =
+ (java.security.KeyStore.TrustedCertificateEntry) entry;
engineSetCertificateEntry(alias, trE.getTrustedCertificate());
return;
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d861302..19ff9c7 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -214,7 +214,9 @@
}
/**
- * Returns the requested key size or {@code -1} if default size should be used.
+ * Returns the requested key size. If {@code -1}, the size should be looked up from
+ * {@link #getAlgorithmParameterSpec()}, if provided, otherwise an algorithm-specific default
+ * size should be used.
*/
public int getKeySize() {
return mKeySize;
@@ -465,7 +467,10 @@
* the modulus size, for EC keys this selects a curve with a matching field size, and for
* symmetric keys this sets the size of the bitstring which is their key material.
*
- * <p>The default key size is specific to each key algorithm.
+ * <p>The default key size is specific to each key algorithm. If key size is not set
+ * via this method, it should be looked up from the algorithm-specific parameters (if any)
+ * provided via
+ * {@link #setAlgorithmParameterSpec(AlgorithmParameterSpec) setAlgorithmParameterSpec}.
*/
@NonNull
public Builder setKeySize(int keySize) {
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index e3c2d1d..5af4181 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -168,6 +168,31 @@
public static abstract class KeyAlgorithm {
private KeyAlgorithm() {}
+ public static int toKeymasterAsymmetricKeyAlgorithm(
+ @NonNull @KeyAlgorithmEnum String algorithm) {
+ if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_EC;
+ } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_RSA;
+ } else {
+ throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+ }
+ }
+
+ @NonNull
+ public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
+ int keymasterAlgorithm) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ return KEY_ALGORITHM_EC;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ return KEY_ALGORITHM_RSA;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported key algorithm: " + keymasterAlgorithm);
+ }
+ }
+
public static int toKeymasterSecretKeyAlgorithm(
@NonNull @KeyAlgorithmEnum String algorithm) {
if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
@@ -572,6 +597,28 @@
}
@NonNull
+ public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return "NONE";
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return "MD5";
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return "SHA1";
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return "SHA224";
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return "SHA256";
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return "SHA384";
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return "SHA512";
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ @NonNull
public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
if (digests.isEmpty()) {
return EmptyArray.STRING;
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index e7529e1..0639d49 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -50,7 +50,8 @@
}
}
- public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
+ public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ int keymasterBlockMode) {
switch (keymasterBlockMode) {
case KeymasterDefs.KM_MODE_ECB:
return false;
@@ -63,6 +64,20 @@
}
}
+ public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ int keymasterPadding) {
+ switch (keymasterPadding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return false;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return true;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + keymasterPadding);
+ }
+ }
+
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.