summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2015-06-05 15:51:06 -0700
committerAlex Klyubin <klyubin@google.com>2015-06-09 13:08:02 -0700
commit3ceb1a04b44539c2b2c3afec6df487fe128911f2 (patch)
tree142abcf7c31076ba1501159baaecc95a6a39c5c2 /keystore
parent3eb63dc35e7ac0335defe4f8e7b42f5dcc390b42 (diff)
downloadframeworks_base-3ceb1a04b44539c2b2c3afec6df487fe128911f2.zip
frameworks_base-3ceb1a04b44539c2b2c3afec6df487fe128911f2.tar.gz
frameworks_base-3ceb1a04b44539c2b2c3afec6df487fe128911f2.tar.bz2
Switch Android Keystore key gen and import to new KeyStore API.
This makes Android Keystore's asymmetric key generation and import use the new KeyStore API (similar to keymaster 1.0 API). Because the resulting private keys will be used through Conscrypt/keystore-engine which uses the old Keystore API, this CL implements a temporary workaround where all generated and imported keys are authorized for padding NONE and digest NONE, in addition to padding schemes and digests requested by the user of the Android Keystore API. This workaround is needed because keystore-engine uses digest NONE and padding NONE for all its crypto operations. Bug: 18088752 Bug: 20912868 Change-Id: Idc709039d091294265bd000160b5507f13825849
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/KeyStore.java8
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java8
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java6
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java829
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSpi.java343
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java9
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java47
-rw-r--r--keystore/java/android/security/keystore/KeymasterUtils.java17
8 files changed, 913 insertions, 354 deletions
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 class KeyStore {
}
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 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
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 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
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.SecureRandom;
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 abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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;
+
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterEncryptionPaddings;
+ private int[] mKeymasterSignaturePaddings;
+ private int[] mKeymasterDigests;
- protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
- mAlgorithm = algorithm;
+ 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");
+ }
+
+ @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);
+ }
}
- /**
- * 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 KeyPair generateKeyPair() {
if (mKeyStore == null || mSpec == null) {
@@ -134,18 +422,65 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
+ ", but the user has not yet entered the credential");
}
- final String alias = mSpec.getKeystoreAlias();
-
- byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
-
- final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+ 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);
+
+ // 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);
+ }
+ 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 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
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();
- }
- 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;
+ try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) {
+ tbsGenerator.setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
}
- return -1;
+ 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 void checkValidKeySize(String keyAlgorithm, int keyType, 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 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);
}
}
- 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());
+ private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
+ throws InvalidAlgorithmParameterException {
+ 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);
}
- } 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() };
+ 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");
- }
-
- @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());
+ /**
+ * 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.
+
+ if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
+ // Key not authorized for signing
+ return null;
}
-
- 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);
+ 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());
+
+ 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;
+ }
+ return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+ bestKeymasterDigest) + "WithECDSA";
+ }
+ 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 (legacySpec.getAlgorithmParameterSpec() != null) {
- specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
+ if (bestKeymasterDigest == -1) {
+ return null;
}
- 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) + "WithRSA";
}
- } else if (params instanceof KeyGenParameterSpec) {
- spec = (KeyGenParameterSpec) params;
- keyAlgorithm = getAlgorithm();
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported params class: " + params.getClass().getName()
- + ". Supported: " + KeyGenParameterSpec.class.getName()
- + ", " + KeyPairGeneratorSpec.class);
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
}
+ }
- int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
- if (keyType == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
+ 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);
}
- int keySize = spec.getKeySize();
- if (keySize == -1) {
- keySize = getDefaultKeySize(keyType);
- if (keySize == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
+ 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 {
+ // Key is authorized to be used only with specific digests.
+ Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
+ result.retainAll(authorizedKeymasterKeyDigests);
+ return result;
}
- 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 com.android.org.conscrypt.OpenSSLKeyHolder;
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.Key;
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 @@ import javax.crypto.SecretKey;
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
}
+ 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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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);
- }
-
- 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");
+ pkeyAlias = null;
+ }
+
+ 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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
}
}
- 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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
// Unfortunate name collision.
- mKeyStore = android.security.KeyStore.getInstance();
+ mKeyStore = KeyStore.getInstance();
}
@Override
@@ -852,8 +934,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
}
/**
- * 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
* 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 abstract class KeyProperties {
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 @@ public abstract class KeyProperties {
}
@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 abstract class KeymasterUtils {
}
}
- 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 abstract class KeymasterUtils {
}
}
+ 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.