summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2015-05-13 09:15:00 -0700
committerAlex Klyubin <klyubin@google.com>2015-05-13 12:49:58 -0700
commit3f8d4d840894468f2be8a5b56ff266cef2d71c50 (patch)
treea6c2457700b1ab3b9bf5207b79c44c8ee315c209 /keystore
parentb33455511b7b704e848c79c9e76e28abe2509178 (diff)
downloadframeworks_base-3f8d4d840894468f2be8a5b56ff266cef2d71c50.zip
frameworks_base-3f8d4d840894468f2be8a5b56ff266cef2d71c50.tar.gz
frameworks_base-3f8d4d840894468f2be8a5b56ff266cef2d71c50.tar.bz2
New AndroidKeyStore API in android.security.keystore.
This CL addresses the comments from API Council about Android KeyStore KeyPairGeneratorSpec, KeyGeneratorSpec and KeyStoreParameter: 1. These abstractions should not take or hold references to Context. 2. The Builders of these abstractions should take all mandatory parameters in their constructors rather than expose them as setters -- only optional paratemers should be exposed via setters. These comments cannot be addressed without deprecation in the already launched KeyPairGeneratorSpec and KeyStoreParameter. Instead of deprecating just the getContext methods and Builder constructors, this CL goes for the nuclear option of deprecating KeyPairGeneratorSpec and KeyStoreParameter as a whole and exposing all of the AndroidKeyStore API in the new package android.security.keystore. This enables this CL to correct all of the accrued design issues with KeyPairGeneratorSpec (e.g., naming of certificate-related methods) and KeyStoreParameter. This also makes the transition to API Level M more clear for existing users of the AndroidKeyStore API. These users will only have to deal with the new always-mandatory parameters (e.g., purposes) and sometimes-mandatory (e.g., digests, block modes, paddings) if they switch to the new API. Prior to this CL they would've had to deal with this if they invoked any of the new methods of KeyPairGeneratorSpec or KeyStoreParameter introduced in API Level M. This CL rips out all the new API introduced into KeyPairGeneratorSpec and KeyStoreParameter classes for Android M, thus reverting these classes to the API launched in L MR1. This is because the new API is now in android.security.keystore.KeyGenParameterSpec and KeyProtection respectively. Bug: 21039983 Change-Id: I59672b3c6ef7bc25c40aa85f1c47d9d8a05d627c
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/AndroidKeyPairGenerator.java141
-rw-r--r--keystore/java/android/security/AndroidKeyStore.java123
-rw-r--r--keystore/java/android/security/ArrayUtils.java2
-rw-r--r--keystore/java/android/security/KeyChain.java14
-rw-r--r--keystore/java/android/security/KeyPairGeneratorSpec.java630
-rw-r--r--keystore/java/android/security/KeyStore.java15
-rw-r--r--keystore/java/android/security/KeyStoreCipherSpi.java3
-rw-r--r--keystore/java/android/security/KeyStoreCryptoOperationUtils.java1
-rw-r--r--keystore/java/android/security/KeyStoreKeyGeneratorSpi.java100
-rw-r--r--keystore/java/android/security/KeyStoreParameter.java556
-rw-r--r--keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java29
-rw-r--r--keystore/java/android/security/KeymasterUtils.java4
-rw-r--r--keystore/java/android/security/keystore/KeyExpiredException.java (renamed from keystore/java/android/security/KeyExpiredException.java)2
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java857
-rw-r--r--keystore/java/android/security/keystore/KeyInfo.java (renamed from keystore/java/android/security/KeyStoreKeySpec.java)73
-rw-r--r--keystore/java/android/security/keystore/KeyNotYetValidException.java (renamed from keystore/java/android/security/KeyNotYetValidException.java)2
-rw-r--r--keystore/java/android/security/keystore/KeyPermanentlyInvalidatedException.java (renamed from keystore/java/android/security/KeyPermanentlyInvalidatedException.java)2
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java (renamed from keystore/java/android/security/KeyStoreKeyProperties.java)78
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java (renamed from keystore/java/android/security/KeyGeneratorSpec.java)409
-rw-r--r--keystore/java/android/security/keystore/UserNotAuthenticatedException.java (renamed from keystore/java/android/security/UserNotAuthenticatedException.java)2
-rw-r--r--keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java56
-rw-r--r--keystore/tests/src/android/security/KeyStoreTest.java1
22 files changed, 1616 insertions, 1484 deletions
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index ea90ca3..e9f8320 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -16,6 +16,10 @@
package android.security;
+import android.annotation.NonNull;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
@@ -36,6 +40,7 @@ import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.Locale;
/**
* Provides a way to create instances of a KeyPair which will be placed in the
@@ -54,13 +59,13 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
public static class RSA extends AndroidKeyPairGenerator {
public RSA() {
- super(KeyStoreKeyProperties.KEY_ALGORITHM_RSA);
+ super(KeyProperties.KEY_ALGORITHM_RSA);
}
}
public static class EC extends AndroidKeyPairGenerator {
public EC() {
- super(KeyStoreKeyProperties.KEY_ALGORITHM_EC);
+ super(KeyProperties.KEY_ALGORITHM_EC);
}
}
@@ -80,18 +85,18 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
private final String mAlgorithm;
- private android.security.KeyStore mKeyStore;
+ private KeyStore mKeyStore;
- private KeyPairGeneratorSpec mSpec;
- private @KeyStoreKeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
+ private KeyGenParameterSpec mSpec;
+ private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.KeyAlgorithmEnum String algorithm) {
+ protected AndroidKeyPairGenerator(@KeyProperties.KeyAlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
- @KeyStoreKeyProperties.KeyAlgorithmEnum String getAlgorithm() {
+ @KeyProperties.KeyAlgorithmEnum String getAlgorithm() {
return mAlgorithm;
}
@@ -113,15 +118,16 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
@Override
public KeyPair generateKeyPair() {
if (mKeyStore == null || mSpec == null) {
- throw new IllegalStateException(
- "Must call initialize with an android.security.KeyPairGeneratorSpec first");
+ throw new IllegalStateException("Not initialized");
+
}
- if (((mSpec.getFlags() & KeyStore.FLAG_ENCRYPTED) != 0)
+ final int flags = mSpec.getFlags();
+ if (((flags & KeyStore.FLAG_ENCRYPTED) != 0)
&& (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
throw new IllegalStateException(
- "Android keystore must be in initialized and unlocked state "
- + "if encryption is required");
+ "Encryption at rest using secure lock screen credential requested for key pair"
+ + ", but the user has not yet entered the credential");
}
final String alias = mSpec.getKeystoreAlias();
@@ -131,8 +137,9 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+
if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
- mSpec.getFlags(), args)) {
+ flags, args)) {
throw new IllegalStateException("could not generate key in keystore");
}
@@ -175,7 +182,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
}
if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
- mSpec.getFlags())) {
+ flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
}
@@ -188,17 +195,17 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
throws Exception {
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(publicKey);
- certGen.setSerialNumber(mSpec.getSerialNumber());
- certGen.setSubjectDN(mSpec.getSubjectDN());
- certGen.setIssuerDN(mSpec.getSubjectDN());
- certGen.setNotBefore(mSpec.getStartDate());
- certGen.setNotAfter(mSpec.getEndDate());
+ certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
+ certGen.setSubjectDN(mSpec.getCertificateSubject());
+ certGen.setIssuerDN(mSpec.getCertificateSubject());
+ certGen.setNotBefore(mSpec.getCertificateNotBefore());
+ certGen.setNotAfter(mSpec.getCertificateNotAfter());
certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
return certGen.generate(privateKey);
}
- private @KeyStoreKeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(
- KeyPairGeneratorSpec spec) {
+ @NonNull
+ private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
String result = spec.getKeyType();
if (result != null) {
return result;
@@ -250,10 +257,10 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
}
private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
- @KeyStoreKeyProperties.KeyAlgorithmEnum String algorithm) {
- if (KeyStoreKeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
+ @KeyProperties.KeyAlgorithmEnum String algorithm) {
+ if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
return "sha256WithRSA";
- } else if (KeyStoreKeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
+ } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
return "sha256WithECDSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + algorithm);
@@ -282,14 +289,86 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
throws InvalidAlgorithmParameterException {
if (params == null) {
throw new InvalidAlgorithmParameterException(
- "must supply params of type android.security.KeyPairGeneratorSpec");
- } else if (!(params instanceof KeyPairGeneratorSpec)) {
+ "Must supply params of type " + KeyGenParameterSpec.class.getName()
+ + " or " + KeyPairGeneratorSpec.class.getName());
+ }
+
+ String keyAlgorithm;
+ KeyGenParameterSpec spec;
+ 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 (legacySpec.getKeySize() != -1) {
+ specBuilder.setKeySize(legacySpec.getKeySize());
+ }
+ if (legacySpec.getAlgorithmParameterSpec() != null) {
+ specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
+ }
+ specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
+ specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
+ specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
+ specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
+ specBuilder.setEncryptionAtRestRequired(legacySpec.isEncryptionRequired());
+ specBuilder.setUserAuthenticationRequired(false);
+
+ spec = specBuilder.build();
+ } catch (NullPointerException | IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+ } else if (params instanceof KeyGenParameterSpec) {
+ spec = (KeyGenParameterSpec) params;
+ keyAlgorithm = getAlgorithm();
+ } else {
throw new InvalidAlgorithmParameterException(
- "params must be of type android.security.KeyPairGeneratorSpec");
+ "Unsupported params class: " + params.getClass().getName()
+ + ". Supported: " + KeyGenParameterSpec.class.getName()
+ + ", " + KeyPairGeneratorSpec.class);
}
- KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
- @KeyStoreKeyProperties.KeyAlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec);
int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
if (keyType == -1) {
throw new InvalidAlgorithmParameterException(
@@ -300,7 +379,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
keySize = getDefaultKeySize(keyType);
if (keySize == -1) {
throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
+ "Unsupported key algorithm: " + keyAlgorithm);
}
}
checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
@@ -310,6 +389,6 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
mKeyType = keyType;
mKeySize = keySize;
mSpec = spec;
- mKeyStore = android.security.KeyStore.getInstance();
+ mKeyStore = KeyStore.getInstance();
}
}
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index b834641..69bf877 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -24,6 +24,8 @@ import libcore.util.EmptyArray;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
import android.util.Log;
import java.io.ByteArrayInputStream;
@@ -129,11 +131,10 @@ public class AndroidKeyStore extends KeyStoreSpi {
keymasterDigest = keymasterDigests.get(0);
}
- @KeyStoreKeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
+ @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
try {
- keyAlgorithmString =
- KeyStoreKeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
- keymasterAlgorithm, keymasterDigest);
+ keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+ keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -270,7 +271,70 @@ public class AndroidKeyStore extends KeyStoreSpi {
}
private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
- KeyStoreParameter params) throws KeyStoreException {
+ java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
+ KeyProtection spec;
+ if (param instanceof KeyStoreParameter) {
+ 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);
+ }
+ specBuilder.setEncryptionAtRestRequired(legacySpec.isEncryptionRequired());
+ specBuilder.setUserAuthenticationRequired(false);
+
+ spec = specBuilder.build();
+ } catch (NullPointerException | IllegalArgumentException e) {
+ throw new KeyStoreException("Unsupported protection parameter", e);
+ }
+ } else if (param instanceof KeyProtection) {
+ spec = (KeyProtection) param;
+ } else if (param != null) {
+ 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;
@@ -383,7 +447,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
}
- final int flags = (params == null) ? 0 : params.getFlags();
+ final int flags = (spec == null) ? 0 : spec.getFlags();
if (shouldReplacePrivateKey
&& !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
@@ -402,8 +466,16 @@ public class AndroidKeyStore extends KeyStoreSpi {
}
}
- private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params)
+ private void setSecretKeyEntry(String entryAlias, SecretKey key,
+ java.security.KeyStore.ProtectionParameter param)
throws KeyStoreException {
+ if ((param != null) && (!(param instanceof KeyProtection))) {
+ throw new KeyStoreException(
+ "Unsupported protection parameter class: " + param.getClass().getName()
+ + ". Supported: " + KeyProtection.class.getName());
+ }
+ KeyProtection params = (KeyProtection) param;
+
if (key instanceof KeyStoreSecretKey) {
// KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
// overwrite its own entry.
@@ -453,10 +525,9 @@ public class AndroidKeyStore extends KeyStoreSpi {
int keymasterAlgorithm;
int keymasterDigest;
try {
- keymasterAlgorithm = KeyStoreKeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
- keyAlgorithmString);
- keymasterDigest =
- KeyStoreKeyProperties.KeyAlgorithm.toKeymasterDigest(keyAlgorithmString);
+ keymasterAlgorithm =
+ KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(keyAlgorithmString);
+ keymasterDigest = KeyProperties.KeyAlgorithm.toKeymasterDigest(keyAlgorithmString);
} catch (IllegalArgumentException e) {
throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
}
@@ -467,7 +538,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
int[] keymasterDigests;
if (params.isDigestsSpecified()) {
// Digest(s) specified in parameters
- keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests());
+ keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
if (keymasterDigest != -1) {
// Digest also specified in the JCA key algorithm name.
if (!com.android.internal.util.ArrayUtils.contains(
@@ -509,33 +580,32 @@ public class AndroidKeyStore extends KeyStoreSpi {
}
}
- @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
+ @KeyProperties.PurposeEnum int purposes = params.getPurposes();
int[] keymasterBlockModes =
- KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
- if (((purposes & KeyStoreKeyProperties.PURPOSE_ENCRYPT) != 0)
+ KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
+ if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
- + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
- + ". See KeyStoreParameter documentation.");
+ + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ + ". See KeyProtection documentation.");
}
}
}
- for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
+ for (int keymasterPurpose : KeyProperties.Purpose.allToKeymaster(purposes)) {
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
if (params.getSignaturePaddings().length > 0) {
throw new KeyStoreException("Signature paddings not supported for symmetric keys");
}
- int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
params.getEncryptionPaddings());
args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
- params.getContext(),
params.isUserAuthenticationRequired(),
params.getUserAuthenticationValidityDurationSeconds());
args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
@@ -551,7 +621,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
// TODO: Remove this once keymaster does not require us to specify the size of imported key.
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
- if (((purposes & KeyStoreKeyProperties.PURPOSE_ENCRYPT) != 0)
+ if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (!params.isRandomizedEncryptionRequired())) {
// Permit caller-provided IV when encrypting with this key
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
@@ -789,19 +859,12 @@ public class AndroidKeyStore extends KeyStoreSpi {
return;
}
- if (param != null && !(param instanceof KeyStoreParameter)) {
- throw new KeyStoreException(
- "protParam should be android.security.KeyStoreParameter; was: "
- + param.getClass().getName());
- }
-
if (entry instanceof PrivateKeyEntry) {
PrivateKeyEntry prE = (PrivateKeyEntry) entry;
- setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
- (KeyStoreParameter) param);
+ setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
} else if (entry instanceof SecretKeyEntry) {
SecretKeyEntry secE = (SecretKeyEntry) entry;
- setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param);
+ setSecretKeyEntry(alias, secE.getSecretKey(), param);
} else {
throw new KeyStoreException(
"Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
diff --git a/keystore/java/android/security/ArrayUtils.java b/keystore/java/android/security/ArrayUtils.java
index 2047d3f..71b99d0 100644
--- a/keystore/java/android/security/ArrayUtils.java
+++ b/keystore/java/android/security/ArrayUtils.java
@@ -5,7 +5,7 @@ import libcore.util.EmptyArray;
/**
* @hide
*/
-abstract class ArrayUtils {
+public abstract class ArrayUtils {
private ArrayUtils() {}
public static String[] nullToEmpty(String[] array) {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 3853eca..19b62a6 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -28,6 +28,8 @@ import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.security.keystore.KeyProperties;
+
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.security.InvalidKeyException;
@@ -266,7 +268,7 @@ public final class KeyChain {
*/
public static void choosePrivateKeyAlias(@NonNull Activity activity,
@NonNull KeyChainAliasCallback response,
- @KeyStoreKeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
+ @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
@Nullable String host, int port, @Nullable String alias) {
choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
}
@@ -312,7 +314,7 @@ public final class KeyChain {
*/
public static void choosePrivateKeyAlias(@NonNull Activity activity,
@NonNull KeyChainAliasCallback response,
- @KeyStoreKeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
+ @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
@Nullable String host, int port, @Nullable String url, @Nullable String alias) {
/*
* TODO currently keyTypes, issuers are unused. They are meant
@@ -439,10 +441,10 @@ public final class KeyChain {
* "RSA").
*/
public static boolean isKeyAlgorithmSupported(
- @NonNull @KeyStoreKeyProperties.KeyAlgorithmEnum String algorithm) {
+ @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
final String algUpper = algorithm.toUpperCase(Locale.US);
- return KeyStoreKeyProperties.KEY_ALGORITHM_EC.equals(algUpper)
- || KeyStoreKeyProperties.KEY_ALGORITHM_RSA.equals(algUpper);
+ return KeyProperties.KEY_ALGORITHM_EC.equals(algUpper)
+ || KeyProperties.KEY_ALGORITHM_RSA.equals(algUpper);
}
/**
@@ -453,7 +455,7 @@ public final class KeyChain {
* that makes it non-exportable.
*/
public static boolean isBoundKeyAlgorithm(
- @NonNull @KeyStoreKeyProperties.KeyAlgorithmEnum String algorithm) {
+ @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
if (!isKeyAlgorithmSupported(algorithm)) {
return false;
}
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index b07c052..efbce41 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -17,14 +17,14 @@
package android.security;
import android.app.KeyguardManager;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import java.math.BigInteger;
-import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@@ -34,72 +34,32 @@ import java.util.Date;
import javax.security.auth.x500.X500Principal;
/**
- * {@link AlgorithmParameterSpec} for initializing a {@link KeyPairGenerator} of the
- * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. This class
- * specifies whether user authentication is required for using the private key, what uses the
- * private key is authorized for (e.g., only for signing -- decryption not permitted), whether the
- * private key should be encrypted at rest, the private key's and validity start and end dates.
+ * This provides the required parameters needed for initializing the
+ * {@code KeyPairGenerator} that works with
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
+ * facility</a>. The Android KeyStore facility is accessed through a
+ * {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore}
+ * provider. The {@code context} passed in may be used to pop up some UI to ask
+ * the user to unlock or initialize the Android KeyStore facility.
+ * <p>
+ * After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link PrivateKey} and its associated
+ * {@link Certificate} chain.
+ * <p>
+ * The KeyPair generator will create a self-signed certificate with the subject
+ * as its X.509v3 Subject Distinguished Name and as its X.509v3 Issuer
+ * Distinguished Name along with the other parameters specified with the
+ * {@link Builder}.
+ * <p>
+ * The self-signed X.509 certificate may be replaced at a later time by a
+ * certificate signed by a real Certificate Authority.
*
- * <p>To generate a key pair, create an instance of this class using the {@link Builder}, initialize
- * a {@code KeyPairGenerator} of the desired key type (e.g., {@code EC} or {@code RSA}) from the
- * {@code AndroidKeyStore} provider with the {@code KeyPairGeneratorSpec} instance, and then
- * generate a key pair using {@link KeyPairGenerator#generateKeyPair()}.
- *
- * <p>The generated key pair will be returned by the {@code KeyPairGenerator} and also stored in the
- * Android KeyStore under the alias specified in this {@code KeyPairGeneratorSpec}. To obtain the
- * private key from the Android KeyStore use
- * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or
- * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
- * To obtain the public key from the Android KeyStore use
- * {@link java.security.KeyStore#getCertificate(String)} and then
- * {@link Certificate#getPublicKey()}.
- *
- * <p>A self-signed X.509 certificate will be also generated and stored in the Android KeyStore.
- * This is because the {@link java.security.KeyStore} abstraction does not support storing key pairs
- * without a certificate. The subject, serial number, and validity dates of the certificate can be
- * specified in this {@code KeyPairGeneratorSpec}. The self-signed certificate may be replaced at a
- * later time by a certificate signed by a Certificate Authority (CA).
- *
- * <p>NOTE: The key material of the private keys generating using the {@code KeyPairGeneratorSpec}
- * is not accessible. The key material of the public keys is accessible.
- *
- * <p><h3>Example</h3>
- * The following example illustrates how to generate an EC key pair in the Android KeyStore under
- * alias {@code key2} authorized to be used only for signing using SHA-256, SHA-384, or SHA-512
- * digest and only if the user has been authenticated within the last five minutes.
- * <pre> {@code
- * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
- * KeyStoreKeyProperties.KEY_ALGORITHM_EC,
- * "AndroidKeyStore");
- * keyPairGenerator.initialize(
- * new KeyGeneratorSpec.Builder(context)
- * .setAlias("key2")
- * .setPurposes(KeyStoreKeyProperties.PURPOSE_SIGN
- * | KeyStoreKeyProperties.PURPOSE_VERIFY)
- * .setDigests(KeyStoreKeyProperties.DIGEST_SHA256
- * | KeyStoreKeyProperties.DIGEST_SHA384
- * | KeyStoreKeyProperties.DIGEST_SHA512)
- * // Only permit this key to be used if the user authenticated
- * // within the last five minutes.
- * .setUserAuthenticationRequired(true)
- * .setUserAuthenticationValidityDurationSeconds(5 * 60)
- * .build());
- * KeyPair keyPair = keyPairGenerator.generateKey();
- *
- * // The key pair can also be obtained from the Android KeyStore any time as follows:
- * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- * keyStore.load(null);
- * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key2", null);
- * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
- * }</pre>
+ * @deprecated Use {@link KeyGenParameterSpec} instead.
*/
+@Deprecated
public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
- private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
- private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
- private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
- private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
-
private final Context mContext;
private final String mKeystoreAlias;
@@ -120,28 +80,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
private final int mFlags;
- private final Date mKeyValidityStart;
-
- private final Date mKeyValidityForOriginationEnd;
-
- private final Date mKeyValidityForConsumptionEnd;
-
- private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-
- private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
-
- private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
-
- private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
-
- private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
-
- private final boolean mRandomizedEncryptionRequired;
-
- private final boolean mUserAuthenticationRequired;
-
- private final int mUserAuthenticationValidityDurationSeconds;
-
/**
* Parameter specification for the "{@code AndroidKeyPairGenerator}"
* instance of the {@link java.security.KeyPairGenerator} API. The
@@ -162,7 +100,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
* @param context Android context for the activity
* @param keyStoreAlias name to use for the generated key in the Android
* keystore
- * @param keyType key algorithm to use (EC, RSA)
+ * @param keyType key algorithm to use (RSA, DSA, EC)
* @param keySize size of key to generate
* @param spec the underlying key type parameters
* @param subjectDN X.509 v3 Subject Distinguished Name
@@ -176,39 +114,21 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
*/
public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
- Date startDate, Date endDate, int flags,
- Date keyValidityStart,
- Date keyValidityForOriginationEnd,
- Date keyValidityForConsumptionEnd,
- @KeyStoreKeyProperties.PurposeEnum int purposes,
- @KeyStoreKeyProperties.DigestEnum String[] digests,
- @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
- @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
- @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
- boolean randomizedEncryptionRequired,
- boolean userAuthenticationRequired,
- int userAuthenticationValidityDurationSeconds) {
+ Date startDate, Date endDate, int flags) {
if (context == null) {
throw new IllegalArgumentException("context == null");
} else if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
- } else if ((userAuthenticationValidityDurationSeconds < 0)
- && (userAuthenticationValidityDurationSeconds != -1)) {
- throw new IllegalArgumentException(
- "userAuthenticationValidityDurationSeconds must not be negative");
- }
-
- if (subjectDN == null) {
- subjectDN = DEFAULT_CERT_SUBJECT;
- }
- if (startDate == null) {
- startDate = DEFAULT_CERT_NOT_BEFORE;
- }
- if (endDate == null) {
- endDate = DEFAULT_CERT_NOT_AFTER;
- }
- if (serialNumber == null) {
- serialNumber = DEFAULT_CERT_SERIAL_NUMBER;
+ } else if (subjectDN == null) {
+ throw new IllegalArgumentException("subjectDN == null");
+ } else if (serialNumber == null) {
+ throw new IllegalArgumentException("serialNumber == null");
+ } else if (startDate == null) {
+ throw new IllegalArgumentException("startDate == null");
+ } else if (endDate == null) {
+ throw new IllegalArgumentException("endDate == null");
+ } else if (endDate.before(startDate)) {
+ throw new IllegalArgumentException("endDate < startDate");
}
if (endDate.before(startDate)) {
@@ -225,50 +145,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
mStartDate = startDate;
mEndDate = endDate;
mFlags = flags;
- mKeyValidityStart = keyValidityStart;
- mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
- mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
- mPurposes = purposes;
- mDigests = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(digests));
- mEncryptionPaddings =
- ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
- mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
- mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
- mRandomizedEncryptionRequired = randomizedEncryptionRequired;
- mUserAuthenticationRequired = userAuthenticationRequired;
- mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
- }
-
- /**
- * TODO: Remove this constructor once tests are switched over to the new one above.
- * @hide
- */
- public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
- AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
- Date startDate, Date endDate, int flags) {
-
- this(context,
- keyStoreAlias,
- keyType,
- keySize,
- spec,
- subjectDN,
- serialNumber,
- startDate,
- endDate,
- flags,
- startDate,
- endDate,
- endDate,
- 0, // purposes
- null, // digests
- null, // encryption paddings
- null, // signature paddings
- null, // block modes
- false, // randomized encryption required
- false, // user authentication required
- -1 // user authentication validity duration (seconds)
- );
}
/**
@@ -288,10 +164,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Returns the type of key pair (e.g., {@code EC}, {@code RSA}) to be generated. See
- * {@link KeyStoreKeyProperties}.{@code KEY_ALGORITHM} constants.
+ * {@link KeyProperties}.{@code KEY_ALGORITHM} constants.
*/
@Nullable
- public @KeyStoreKeyProperties.KeyAlgorithmEnum String getKeyType() {
+ public @KeyProperties.KeyAlgorithmEnum String getKeyType() {
return mKeyType;
}
@@ -352,145 +228,24 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* @hide
*/
- int getFlags() {
+ public int getFlags() {
return mFlags;
}
/**
* Returns {@code true} if the key must be encrypted at rest. This will protect the key pair
* with the secure lock screen credential (e.g., password, PIN, or pattern).
- */
- public boolean isEncryptionRequired() {
- return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
- }
-
- /**
- * Gets the time instant before which the key pair is not yet valid.
*
- * @return instant or {@code null} if not restricted.
- */
- @Nullable
- public Date getKeyValidityStart() {
- return mKeyValidityStart;
- }
-
- /**
- * Gets the time instant after which the key pair is no longer valid for decryption and
- * verification.
+ * <p>Note that encrypting the key at rest requires that the secure lock screen (e.g., password,
+ * PIN, pattern) is set up, otherwise key generation will fail. Moreover, this key will be
+ * deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device
+ * Administrator). Finally, this key cannot be used until the user unlocks the secure lock
+ * screen after boot.
*
- * @return instant or {@code null} if not restricted.
+ * @see KeyguardManager#isDeviceSecure()
*/
- @Nullable
- public Date getKeyValidityForConsumptionEnd() {
- return mKeyValidityForConsumptionEnd;
- }
-
- /**
- * Gets the time instant after which the key pair is no longer valid for encryption and signing.
- *
- * @return instant or {@code null} if not restricted.
- */
- @Nullable
- public Date getKeyValidityForOriginationEnd() {
- return mKeyValidityForOriginationEnd;
- }
-
- /**
- * Gets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
- * Attempts to use the key for any other purpose will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
- */
- public @KeyStoreKeyProperties.PurposeEnum int getPurposes() {
- return mPurposes;
- }
-
- /**
- * Gets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the key
- * can be used.
- *
- * @see KeyStoreKeyProperties.Digest
- */
- @NonNull
- public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
- return ArrayUtils.cloneIfNotEmpty(mDigests);
- }
-
- /**
- * Gets the set of padding schemes (e.g., {@code OEAPPadding}, {@code PKCS1Padding},
- * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to use
- * the key with any other padding scheme will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
- return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
- }
-
- /**
- * Gets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
- * can be used when signing/verifying. Attempts to use the key with any other padding scheme
- * will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code SIGNATURE_PADDING} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
- return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
- }
-
- /**
- * Gets the set of block modes (e.g., {@code CBC}, {@code CTR}) with which the key can be used
- * when encrypting/decrypting. Attempts to use the key with any other block modes will be
- * rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
- return ArrayUtils.cloneIfNotEmpty(mBlockModes);
- }
-
- /**
- * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
- * different ciphertexts for the same plaintext every time. The formal cryptographic property
- * being required is <em>indistinguishability under chosen-plaintext attack ({@code
- * IND-CPA})</em>. This property is important because it mitigates several classes of
- * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
- * given plaintext always produces the same ciphertext, an attacker may see the repeated
- * ciphertexts and be able to deduce something about the plaintext.
- */
- public boolean isRandomizedEncryptionRequired() {
- return mRandomizedEncryptionRequired;
- }
-
- /**
- * Returns {@code true} if user authentication is required for this key to be used.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @see #getUserAuthenticationValidityDurationSeconds()
- */
- public boolean isUserAuthenticationRequired() {
- return mUserAuthenticationRequired;
- }
-
- /**
- * Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated. This has effect only if user authentication is required.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @return duration in seconds or {@code -1} if authentication is required for every use of the
- * key.
- *
- * @see #isUserAuthenticationRequired()
- */
- public int getUserAuthenticationValidityDurationSeconds() {
- return mUserAuthenticationValidityDurationSeconds;
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
}
/**
@@ -513,7 +268,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
* .setSubject(new X500Principal(&quot;CN=myKey&quot;)).setSerial(BigInteger.valueOf(1337))
* .setStartDate(start.getTime()).setEndDate(end.getTime()).build();
* </pre>
+ *
+ * @deprecated Use {@link KeyGenParameterSpec.Builder} instead.
*/
+ @Deprecated
public final static class Builder {
private final Context mContext;
@@ -535,28 +293,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
private int mFlags;
- private Date mKeyValidityStart;
-
- private Date mKeyValidityForOriginationEnd;
-
- private Date mKeyValidityForConsumptionEnd;
-
- private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-
- private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
-
- private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
-
- private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
-
- private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
-
- private boolean mRandomizedEncryptionRequired = true;
-
- private boolean mUserAuthenticationRequired;
-
- private int mUserAuthenticationValidityDurationSeconds = -1;
-
/**
* Creates a new instance of the {@code Builder} with the given
* {@code context}. The {@code context} passed in may be used to pop up
@@ -586,11 +322,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the type of key pair (e.g., {@code EC}, {@code RSA}) of the key pair to be
- * generated. See {@link KeyStoreKeyProperties}.{@code KEY_ALGORITHM} constants.
+ * generated. See {@link KeyProperties}.{@code KEY_ALGORITHM} constants.
*
*/
@NonNull
- public Builder setKeyType(@NonNull @KeyStoreKeyProperties.KeyAlgorithmEnum String keyType)
+ public Builder setKeyType(@NonNull @KeyProperties.KeyAlgorithmEnum String keyType)
throws NoSuchAlgorithmException {
if (keyType == null) {
throw new NullPointerException("keyType == null");
@@ -632,10 +368,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the subject used for the self-signed certificate of the
* generated key pair.
- *
- * <p>The subject must be specified on API Level
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
- * newer platforms the subject defaults to {@code CN=fake} if not specified.
*/
@NonNull
public Builder setSubject(@NonNull X500Principal subject) {
@@ -649,10 +381,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the serial number used for the self-signed certificate of the
* generated key pair.
- *
- * <p>The serial number must be specified on API Level
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
- * newer platforms the serial number defaults to {@code 1} if not specified.
*/
@NonNull
public Builder setSerialNumber(@NonNull BigInteger serialNumber) {
@@ -666,10 +394,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the start of the validity period for the self-signed certificate
* of the generated key pair.
- *
- * <p>The date must be specified on API Level
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
- * newer platforms the date defaults to {@code Jan 1 1970} if not specified.
*/
@NonNull
public Builder setStartDate(@NonNull Date startDate) {
@@ -683,10 +407,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the end of the validity period for the self-signed certificate
* of the generated key pair.
- *
- * <p>The date must be specified on API Level
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
- * newer platforms the date defaults to {@code Jan 1 2048} if not specified.
*/
@NonNull
public Builder setEndDate(@NonNull Date endDate) {
@@ -716,239 +436,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Sets the time instant before which the key is not yet valid.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see #setKeyValidityEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityStart(Date startDate) {
- mKeyValidityStart = startDate;
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see #setKeyValidityStart(Date)
- * @see #setKeyValidityForConsumptionEnd(Date)
- * @see #setKeyValidityForOriginationEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityEnd(Date endDate) {
- setKeyValidityForOriginationEnd(endDate);
- setKeyValidityForConsumptionEnd(endDate);
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid for encryption and signing.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see #setKeyValidityForConsumptionEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityForOriginationEnd(Date endDate) {
- mKeyValidityForOriginationEnd = endDate;
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid for decryption and
- * verification.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see #setKeyValidityForOriginationEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityForConsumptionEnd(Date endDate) {
- mKeyValidityForConsumptionEnd = endDate;
- return this;
- }
-
- /**
- * Sets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
- * Attempts to use the key for any other purpose will be rejected.
- *
- * <p>This must be specified for all keys. There is no default.
- *
- * <p>If the set of purposes for which the key can be used does not contain
- * {@link KeyStoreKeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
- * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an invalid
- * signature. This is OK if the certificate is only used for obtaining the public key from
- * Android KeyStore.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
- */
- @NonNull
- public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
- mPurposes = purposes;
- return this;
- }
-
- /**
- * Sets the set of digests algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which
- * the key can be used when signing/verifying. Attempts to use the key with any other digest
- * algorithm will be rejected.
- *
- * <p>This must be specified for keys which are used for signing/verification.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see KeyStoreKeyProperties.Digest
- */
- @NonNull
- public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
- mDigests = ArrayUtils.cloneIfNotEmpty(digests);
- return this;
- }
-
- /**
- * Sets the set of padding schemes (e.g., {@code OAEPPadding}, {@code PKCS1Padding},
- * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to
- * use the key with any other padding scheme will be rejected.
- *
- * <p>This must be specified for keys which are used for encryption/decryption.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
- */
- @NonNull
- public Builder setEncryptionPaddings(
- @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
- mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
- return this;
- }
-
- /**
- * Sets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
- * can be used when signing/verifying. Attempts to use the key with any other padding scheme
- * will be rejected.
- *
- * <p>This must be specified for RSA keys which are used for signing/verification.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code SIGNATURE_PADDING} constants.
- */
- @NonNull
- public Builder setSignaturePaddings(
- @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
- mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
- return this;
- }
-
- /**
- * Sets the set of block modes (e.g., {@code ECB}, {@code CBC}, {@code CTR}) with which the
- * key can be used when encrypting/decrypting. Attempts to use the key with any other block
- * modes will be rejected.
- *
- * <p>This must be specified for encryption/decryption keys.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
- */
- @NonNull
- public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
- mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
- return this;
- }
-
- /**
- * Sets whether encryption using this key must be sufficiently randomized to produce
- * different ciphertexts for the same plaintext every time. The formal cryptographic
- * property being required is <em>indistinguishability under chosen-plaintext attack
- * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
- * of weaknesses due to which ciphertext may leak information about plaintext. For example,
- * if a given plaintext always produces the same ciphertext, an attacker may see the
- * repeated ciphertexts and be able to deduce something about the plaintext.
- *
- * <p>By default, {@code IND-CPA} is required.
- *
- * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not
- * offer {@code IND-CPA}, such as RSA without padding, are prohibited.
- *
- * <p>Before disabling this requirement, consider the following approaches instead:
- * <ul>
- * <li>If you are using RSA encryption without padding, consider switching to padding
- * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
- * </ul>
- *
- * <p><b>NOTE: This has currently no effect.</b>
- */
- @NonNull
- public Builder setRandomizedEncryptionRequired(boolean required) {
- mRandomizedEncryptionRequired = required;
- return this;
- }
-
- /**
- * Sets whether user authentication is required to use this key.
- *
- * <p>By default, the key can be used without user authentication.
- *
- * <p>When user authentication is required, the user authorizes the use of the key by
- * authenticating to this Android device using a subset of their secure lock screen
- * credentials. Different authentication methods are used depending on whether the every
- * use of the key must be authenticated (as specified by
- * {@link #setUserAuthenticationValidityDurationSeconds(int)}).
- * <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
- * information</a>.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @see #setUserAuthenticationValidityDurationSeconds(int)
- */
- @NonNull
- public Builder setUserAuthenticationRequired(boolean required) {
- mUserAuthenticationRequired = required;
- return this;
- }
-
- /**
- * Sets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated. This has effect only if user authentication is required.
- *
- * <p>By default, the user needs to authenticate for every use of the key.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * <p><b>NOTE: This has currently no effect.</b>
- *
- * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
- * every use of the key.
- *
- * @see #setUserAuthenticationRequired(boolean)
- */
- @NonNull
- public Builder setUserAuthenticationValidityDurationSeconds(
- @IntRange(from = -1) int seconds) {
- mUserAuthenticationValidityDurationSeconds = seconds;
- return this;
- }
-
- /**
* Builds the instance of the {@code KeyPairGeneratorSpec}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -965,18 +452,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
mSerialNumber,
mStartDate,
mEndDate,
- mFlags,
- mKeyValidityStart,
- mKeyValidityForOriginationEnd,
- mKeyValidityForConsumptionEnd,
- mPurposes,
- mDigests,
- mEncryptionPaddings,
- mSignaturePaddings,
- mBlockModes,
- mRandomizedEncryptionRequired,
- mUserAuthenticationRequired,
- mUserAuthenticationValidityDurationSeconds);
+ mFlags);
}
}
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index caa4fec..72c74df 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -35,6 +35,11 @@ import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterBlob;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
+import android.security.keystore.KeyExpiredException;
+import android.security.keystore.KeyNotYetValidException;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
import java.security.InvalidKeyException;
@@ -101,10 +106,10 @@ public class KeyStore {
private KeyStore(IKeystoreService binder) {
mBinder = binder;
- mContext = getContext();
+ mContext = getApplicationContext();
}
- private static Context getContext() {
+ static Context getApplicationContext() {
ActivityThread activityThread = ActivityThread.currentActivityThread();
if (activityThread == null) {
throw new IllegalStateException(
@@ -131,10 +136,10 @@ public class KeyStore {
return mToken;
}
- static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.KeyAlgorithmEnum String keyType) {
- if (KeyStoreKeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyType)) {
+ static int getKeyTypeForAlgorithm(@KeyProperties.KeyAlgorithmEnum String keyType) {
+ if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_RSA;
- } else if (KeyStoreKeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyType)) {
+ } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 4eeca47..b0f1695 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -20,6 +20,7 @@ import android.os.IBinder;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
+import android.security.keystore.KeyProperties;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
@@ -496,7 +497,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
if ((mIv != null) && (mIv.length > 0)) {
try {
AlgorithmParameters params =
- AlgorithmParameters.getInstance(KeyStoreKeyProperties.KEY_ALGORITHM_AES);
+ AlgorithmParameters.getInstance(KeyProperties.KEY_ALGORITHM_AES);
params.init(new IvParameterSpec(mIv));
return params;
} catch (NoSuchAlgorithmException e) {
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
index 311278b..c9bdd41 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
@@ -17,6 +17,7 @@
package android.security;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.UserNotAuthenticatedException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index d734d66..feec00f 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -19,11 +19,16 @@ package android.security;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import libcore.util.EmptyArray;
import java.security.InvalidAlgorithmParameterException;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
import java.util.Date;
import javax.crypto.KeyGeneratorSpi;
@@ -96,13 +101,14 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
private final int mKeymasterDigest;
private final int mDefaultKeySizeBits;
- private KeyGeneratorSpec mSpec;
+ private KeyGenParameterSpec mSpec;
private SecureRandom mRng;
protected int mKeySizeBits;
private int[] mKeymasterPurposes;
private int[] mKeymasterBlockModes;
private int[] mKeymasterPaddings;
+ private int[] mKeymasterDigests;
protected KeyStoreKeyGeneratorSpi(
int keymasterAlgorithm,
@@ -129,14 +135,14 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
@Override
protected void engineInit(SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
+ throw new UnsupportedOperationException("Cannot initialize without a "
+ + KeyGenParameterSpec.class.getName() + " parameter");
}
@Override
protected void engineInit(int keySize, SecureRandom random) {
throw new UnsupportedOperationException("Cannot initialize without a "
- + KeyGeneratorSpec.class.getName() + " parameter");
+ + KeyGenParameterSpec.class.getName() + " parameter");
}
@Override
@@ -146,11 +152,11 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
boolean success = false;
try {
- if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
- throw new InvalidAlgorithmParameterException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
+ if ((params == null) || (!(params instanceof KeyGenParameterSpec))) {
+ throw new InvalidAlgorithmParameterException("Cannot initialize without a "
+ + KeyGenParameterSpec.class.getName() + " parameter");
}
- KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+ KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
if (spec.getKeystoreAlias() == null) {
throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
}
@@ -168,13 +174,11 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
}
try {
- mKeymasterPurposes =
- KeyStoreKeyProperties.Purpose.allToKeymaster(spec.getPurposes());
- mKeymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+ mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
- mKeymasterBlockModes =
- KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
- if (((spec.getPurposes() & KeyStoreKeyProperties.PURPOSE_ENCRYPT) != 0)
+ mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : mKeymasterBlockModes) {
if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
@@ -182,14 +186,55 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
throw new InvalidAlgorithmParameterException(
"Randomized encryption (IND-CPA) required but may be violated"
+ " by block mode: "
- + KeyStoreKeyProperties.BlockMode.fromKeymaster(
- keymasterBlockMode)
- + ". See " + KeyGeneratorSpec.class.getName()
+ + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ + ". See " + KeyGenParameterSpec.class.getName()
+ " documentation.");
}
}
}
- } catch (IllegalArgumentException e) {
+ if (spec.isDigestsSpecified()) {
+ // Digest(s) explicitly specified in the spec
+ mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+ if (mKeymasterDigest != -1) {
+ // Key algorithm implies a digest -- ensure it's specified in the spec as
+ // first digest.
+ if (!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterDigests, mKeymasterDigest)) {
+ throw new InvalidAlgorithmParameterException(
+ "Digests specified in algorithm parameters ("
+ + Arrays.asList(spec.getDigests()) + ") must include "
+ + " the digest "
+ + KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
+ + " implied by key algorithm");
+ }
+ if (mKeymasterDigests[0] != mKeymasterDigest) {
+ // The first digest is not the one implied by the key algorithm.
+ // Swap the implied digest with the first one.
+ for (int i = 0; i < mKeymasterDigests.length; i++) {
+ if (mKeymasterDigests[i] == mKeymasterDigest) {
+ mKeymasterDigests[i] = mKeymasterDigests[0];
+ mKeymasterDigests[0] = mKeymasterDigest;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ // No digest specified in the spec
+ if (mKeymasterDigest != -1) {
+ // Key algorithm implies a digest -- use that digest
+ mKeymasterDigests = new int[] {mKeymasterDigest};
+ } else {
+ mKeymasterDigests = EmptyArray.INT;
+ }
+ }
+ if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
+ if (mKeymasterDigests.length == 0) {
+ throw new InvalidAlgorithmParameterException(
+ "At least one digest algorithm must be specified");
+ }
+ }
+ } catch (IllegalStateException | IllegalArgumentException e) {
throw new InvalidAlgorithmParameterException(e);
}
@@ -212,29 +257,26 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
@Override
protected SecretKey engineGenerateKey() {
- KeyGeneratorSpec spec = mSpec;
+ KeyGenParameterSpec spec = mSpec;
if (spec == null) {
throw new IllegalStateException("Not initialized");
}
- if ((spec.isEncryptionRequired())
+ if ((spec.isEncryptionAtRestRequired())
&& (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
throw new IllegalStateException(
- "Android KeyStore must be in initialized and unlocked state if encryption is"
- + " required");
+ "Requested to import a key which must be encrypted at rest using secure lock"
+ + " screen credential, but the credential hasn't yet been entered by the user");
}
KeymasterArguments args = new KeymasterArguments();
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
- if (mKeymasterDigest != -1) {
- args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- }
args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
+ args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
KeymasterUtils.addUserAuthArgs(args,
- spec.getContext(),
spec.isUserAuthenticationRequired(),
spec.getUserAuthenticationValidityDurationSeconds());
args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
@@ -247,7 +289,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
(spec.getKeyValidityForConsumptionEnd() != null)
? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
- if (((spec.getPurposes() & KeyStoreKeyProperties.PURPOSE_ENCRYPT) != 0)
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (!spec.isRandomizedEncryptionRequired())) {
// Permit caller-provided IV when encrypting with this key
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
@@ -265,9 +307,9 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
throw new ProviderException(
"Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
- @KeyStoreKeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+ @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
try {
- keyAlgorithmJCA = KeyStoreKeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+ keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
mKeymasterAlgorithm, mKeymasterDigest);
} catch (IllegalArgumentException e) {
throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index a7fab80..174e03f 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -16,145 +16,51 @@
package android.security;
-import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProtection;
-import java.security.Key;
+import java.security.KeyPairGenerator;
import java.security.KeyStore.ProtectionParameter;
-import java.security.cert.Certificate;
-import java.util.Date;
-
-import javax.crypto.Cipher;
/**
- * Parameters specifying how to secure and restrict the use of a key or key pair being imported into
- * the <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. This class
- * specifies whether user authentication is required for using the key, what uses the key is
- * authorized for (e.g., only in {@code CTR} mode, or only for signing -- decryption not permitted),
- * whether the key should be encrypted at rest, the key's and validity start and end dates.
- *
- * <p>To import a key or key pair into the Android KeyStore, create an instance of this class using
- * the {@link Builder} and pass the instance into {@link java.security.KeyStore#setEntry(String, java.security.KeyStore.Entry, ProtectionParameter) KeyStore.setEntry}
- * with the key or key pair being imported.
- *
- * <p>To obtain the secret/symmetric or private key from the Android KeyStore use
- * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or
- * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
- * To obtain the public key from the Android KeyStore use
- * {@link java.security.KeyStore#getCertificate(String)} and then
- * {@link Certificate#getPublicKey()}.
- *
- * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible.
- *
- * <p><h3>Example: Symmetric Key</h3>
- * The following example illustrates how to import an AES key into the Android KeyStore under alias
- * {@code key1} authorized to be used only for encryption/decryption in CBC mode with PKCS#7
- * padding. The key must export its key material via {@link Key#getEncoded()} in {@code RAW} format.
- * <pre> {@code
- * SecretKey key = ...; // AES key
- *
- * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- * keyStore.load(null);
- * keyStore.setEntry(
- * "key1",
- * new KeyStore.SecretKeyEntry(key),
- * new KeyStoreParameter.Builder(context)
- * .setPurposes(KeyStoreKeyProperties.PURPOSE_ENCRYPT
- * | KeyStoreKeyProperties.PURPOSE_DECRYPT)
- * .setBlockMode(KeyStoreKeyProperties.BLOCK_MODE_CBC)
- * .setEncryptionPaddings(
- * KeyStoreKeyProperties.ENCRYPTION_PADDING_PKCS7)
- * .build());
- * // Key imported, obtain a reference to it.
- * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
- * // The original key can now be thrown away.
- * }</pre>
+ * This provides the optional parameters that can be specified for
+ * {@code KeyStore} entries that work with
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
+ * facility</a>. The Android KeyStore facility is accessed through a
+ * {@link java.security.KeyStore} API using the {@code AndroidKeyStore}
+ * provider. The {@code context} passed in may be used to pop up some UI to ask
+ * the user to unlock or initialize the Android KeyStore facility.
+ * <p>
+ * Any entries placed in the {@code KeyStore} may be retrieved later. Note that
+ * there is only one logical instance of the {@code KeyStore} per application
+ * UID so apps using the {@code sharedUid} facility will also share a
+ * {@code KeyStore}.
+ * <p>
+ * Keys may be generated using the {@link KeyPairGenerator} facility with a
+ * {@link KeyPairGeneratorSpec} to specify the entry's {@code alias}. A
+ * self-signed X.509 certificate will be attached to generated entries, but that
+ * may be replaced at a later time by a certificate signed by a real Certificate
+ * Authority.
*
- * <p><h3>Example: Asymmetric Key Pair</h3>
- * The following example illustrates how to import an EC key pair into the Android KeyStore under
- * alias {@code key2} authorized to be used only for signing with SHA-256 digest and only if
- * the user has been authenticated within the last ten minutes. Both the private and the public key
- * must export their key material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509}
- * format respectively.
- * <pre> {@code
- * PrivateKey privateKey = ...; // EC private key
- * Certificate[] certChain = ...; // Certificate chain with the first certificate
- * // containing the corresponding EC public key.
- *
- * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- * keyStore.load(null);
- * keyStore.setEntry(
- * "key2",
- * new KeyStore.PrivateKeyEntry(privateKey, certChain),
- * new KeyStoreParameter.Builder(context)
- * .setPurposes(KeyStoreKeyProperties.PURPOSE_SIGN)
- * .setDigests(KeyStoreKeyProperties.DIGEST_SHA256)
- * // Only permit this key to be used if the user
- * // authenticated within the last ten minutes.
- * .setUserAuthenticationRequired(true)
- * .setUserAuthenticationValidityDurationSeconds(10 * 60)
- * .build());
- * // Key pair imported, obtain a reference to it.
- * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
- * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
- * // The original private key can now be thrown away.
- * }</pre>
+ * @deprecated Use {@link KeyProtection} instead.
*/
+@Deprecated
public final class KeyStoreParameter implements ProtectionParameter {
private final Context mContext;
private final int mFlags;
- private final Date mKeyValidityStart;
- private final Date mKeyValidityForOriginationEnd;
- private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
- private final boolean mRandomizedEncryptionRequired;
- private final boolean mUserAuthenticationRequired;
- private final int mUserAuthenticationValidityDurationSeconds;
private KeyStoreParameter(
Context context,
- int flags,
- Date keyValidityStart,
- Date keyValidityForOriginationEnd,
- Date keyValidityForConsumptionEnd,
- @KeyStoreKeyProperties.PurposeEnum int purposes,
- @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
- @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
- @KeyStoreKeyProperties.DigestEnum String[] digests,
- @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
- boolean randomizedEncryptionRequired,
- boolean userAuthenticationRequired,
- int userAuthenticationValidityDurationSeconds) {
+ int flags) {
if (context == null) {
throw new IllegalArgumentException("context == null");
- } else if ((userAuthenticationValidityDurationSeconds < 0)
- && (userAuthenticationValidityDurationSeconds != -1)) {
- throw new IllegalArgumentException(
- "userAuthenticationValidityDurationSeconds must not be negative");
}
mContext = context;
mFlags = flags;
- mKeyValidityStart = keyValidityStart;
- mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
- mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
- mPurposes = purposes;
- mEncryptionPaddings =
- ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
- mSignaturePaddings =
- ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
- mDigests = ArrayUtils.cloneIfNotEmpty(digests);
- mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
- mRandomizedEncryptionRequired = randomizedEncryptionRequired;
- mUserAuthenticationRequired = userAuthenticationRequired;
- mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
}
/**
@@ -175,148 +81,17 @@ public final class KeyStoreParameter implements ProtectionParameter {
* Returns {@code true} if the {@link java.security.KeyStore} entry must be encrypted at rest.
* This will protect the entry with the secure lock screen credential (e.g., password, PIN, or
* pattern).
- */
- public boolean isEncryptionRequired() {
- return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
- }
-
- /**
- * Gets the time instant before which the key is not yet valid.
- *
- * @return instant or {@code null} if not restricted.
- */
- @Nullable
- public Date getKeyValidityStart() {
- return mKeyValidityStart;
- }
-
- /**
- * Gets the time instant after which the key is no long valid for decryption and verification.
- *
- * @return instant or {@code null} if not restricted.
- */
- @Nullable
- public Date getKeyValidityForConsumptionEnd() {
- return mKeyValidityForConsumptionEnd;
- }
-
- /**
- * Gets the time instant after which the key is no long valid for encryption and signing.
- *
- * @return instant or {@code null} if not restricted.
- */
- @Nullable
- public Date getKeyValidityForOriginationEnd() {
- return mKeyValidityForOriginationEnd;
- }
-
- /**
- * Gets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
- * Attempts to use the key for any other purpose will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
- */
- public @KeyStoreKeyProperties.PurposeEnum int getPurposes() {
- return mPurposes;
- }
-
- /**
- * Gets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code PKCS1Padding},
- * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to use
- * the key with any other padding scheme will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
- return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
- }
-
- /**
- * Gets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
- * can be used when signing/verifying. Attempts to use the key with any other padding scheme
- * will be rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code SIGNATURE_PADDING} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
- return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
- }
-
- /**
- * Gets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the key
- * can be used.
- *
- * @throws IllegalStateException if this set has not been specified.
- *
- * @see #isDigestsSpecified()
- * @see KeyStoreKeyProperties.Digest
- */
- @NonNull
- public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
- if (mDigests == null) {
- throw new IllegalStateException("Digests not specified");
- }
- return ArrayUtils.cloneIfNotEmpty(mDigests);
- }
-
- /**
- * Returns {@code true} if the set of digest algorithms with which the key can be used has been
- * specified.
- *
- * @see #getDigests()
- */
- @NonNull
- public boolean isDigestsSpecified() {
- return mDigests != null;
- }
-
- /**
- * Gets the set of block modes (e.g., {@code CBC}, {@code CTR}) with which the key can be used
- * when encrypting/decrypting. Attempts to use the key with any other block modes will be
- * rejected.
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
- */
- @NonNull
- public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
- return ArrayUtils.cloneIfNotEmpty(mBlockModes);
- }
-
- /**
- * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
- * different ciphertexts for the same plaintext every time. The formal cryptographic property
- * being required is <em>indistinguishability under chosen-plaintext attack ({@code
- * IND-CPA})</em>. This property is important because it mitigates several classes of
- * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
- * given plaintext always produces the same ciphertext, an attacker may see the repeated
- * ciphertexts and be able to deduce something about the plaintext.
- */
- public boolean isRandomizedEncryptionRequired() {
- return mRandomizedEncryptionRequired;
- }
-
- /**
- * Returns {@code true} if user authentication is required for this key to be used.
- *
- * @see #getUserAuthenticationValidityDurationSeconds()
- */
- public boolean isUserAuthenticationRequired() {
- return mUserAuthenticationRequired;
- }
-
- /**
- * Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if authentication is required for every use of the
- * key.
+ * <p>Note that encrypting the key at rest requires that the secure lock screen (e.g., password,
+ * PIN, pattern) is set up, otherwise key generation will fail. Moreover, this key will be
+ * deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device
+ * Administrator). Finally, this key cannot be used until the user unlocks the secure lock
+ * screen after boot.
*
- * @see #isUserAuthenticationRequired()
+ * @see KeyguardManager#isDeviceSecure()
*/
- public int getUserAuthenticationValidityDurationSeconds() {
- return mUserAuthenticationValidityDurationSeconds;
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
}
/**
@@ -332,24 +107,16 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* <pre class="prettyprint">
* KeyStoreParameter params = new KeyStoreParameter.Builder(mContext)
- * .setEncryptionRequired(true)
+ * .setEncryptionRequired()
* .build();
* </pre>
+ *
+ * @deprecated Use {@link KeyProtection.Builder} instead.
*/
+ @Deprecated
public final static class Builder {
private final Context mContext;
private int mFlags;
- private Date mKeyValidityStart;
- private Date mKeyValidityForOriginationEnd;
- private Date mKeyValidityForConsumptionEnd;
- private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
- private boolean mRandomizedEncryptionRequired = true;
- private boolean mUserAuthenticationRequired;
- private int mUserAuthenticationValidityDurationSeconds = -1;
/**
* Creates a new instance of the {@code Builder} with the given
@@ -388,244 +155,6 @@ public final class KeyStoreParameter implements ProtectionParameter {
}
/**
- * Sets the time instant before which the key is not yet valid.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see #setKeyValidityEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityStart(Date startDate) {
- mKeyValidityStart = startDate;
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see #setKeyValidityStart(Date)
- * @see #setKeyValidityForConsumptionEnd(Date)
- * @see #setKeyValidityForOriginationEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityEnd(Date endDate) {
- setKeyValidityForOriginationEnd(endDate);
- setKeyValidityForConsumptionEnd(endDate);
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid for encryption and signing.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see #setKeyValidityForConsumptionEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityForOriginationEnd(Date endDate) {
- mKeyValidityForOriginationEnd = endDate;
- return this;
- }
-
- /**
- * Sets the time instant after which the key is no longer valid for decryption and
- * verification.
- *
- * <p>By default, the key is valid at any instant.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see #setKeyValidityForOriginationEnd(Date)
- */
- @NonNull
- public Builder setKeyValidityForConsumptionEnd(Date endDate) {
- mKeyValidityForConsumptionEnd = endDate;
- return this;
- }
-
- /**
- * Sets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
- * Attempts to use the key for any other purpose will be rejected.
- *
- * <p>This must be specified for all keys. There is no default.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
- */
- @NonNull
- public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
- mPurposes = purposes;
- return this;
- }
-
- /**
- * Sets the set of padding schemes (e.g., {@code OAEPPadding}, {@code PKCS7Padding},
- * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to
- * use the key with any other padding scheme will be rejected.
- *
- * <p>This must be specified for keys which are used for encryption/decryption.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
- */
- @NonNull
- public Builder setEncryptionPaddings(
- @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
- mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
- return this;
- }
-
- /**
- * Sets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
- * can be used when signing/verifying. Attempts to use the key with any other padding scheme
- * will be rejected.
- *
- * <p>This must be specified for RSA keys which are used for signing/verification.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code SIGNATURE_PADDING} constants.
- */
- @NonNull
- public Builder setSignaturePaddings(
- @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
- mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
- return this;
- }
-
-
- /**
- * Sets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the
- * key can be used when signing/verifying or generating MACs. Attempts to use the key with
- * any other digest algorithm will be rejected.
- *
- * <p>For HMAC keys, the default is the digest algorithm specified in
- * {@link Key#getAlgorithm()}. For asymmetric signing keys the set of digest algorithms
- * must be specified.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see KeyStoreKeyProperties.Digest
- */
- @NonNull
- public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
- mDigests = ArrayUtils.cloneIfNotEmpty(digests);
- return this;
- }
-
- /**
- * Sets the set of block modes (e.g., {@code CBC}, {@code CTR}, {@code ECB}) with which the
- * key can be used when encrypting/decrypting. Attempts to use the key with any other block
- * modes will be rejected.
- *
- * <p>This must be specified for encryption/decryption keys.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
- */
- @NonNull
- public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
- mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
- return this;
- }
-
- /**
- * Sets whether encryption using this key must be sufficiently randomized to produce
- * different ciphertexts for the same plaintext every time. The formal cryptographic
- * property being required is <em>indistinguishability under chosen-plaintext attack
- * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
- * of weaknesses due to which ciphertext may leak information about plaintext. For example,
- * if a given plaintext always produces the same ciphertext, an attacker may see the
- * repeated ciphertexts and be able to deduce something about the plaintext.
- *
- * <p>By default, {@code IND-CPA} is required.
- *
- * <p>When {@code IND-CPA} is required:
- * <ul>
- * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using
- * {@code ECB} mode or RSA encryption without padding, are prohibited;</li>
- * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC},
- * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when
- * encrypting, to ensure that only random IVs are used.</li>
- *
- * <p>Before disabling this requirement, consider the following approaches instead:
- * <ul>
- * <li>If you are generating a random IV for encryption and then initializing a {@code}
- * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV
- * instead. This will occur if the {@code Cipher} is initialized for encryption without an
- * IV. The IV can then be queried via {@link Cipher#getIV()}.</li>
- * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully
- * random, such as the name of the file being encrypted, or transaction ID, or password,
- * or a device identifier), consider changing your design to use a random IV which will then
- * be provided in addition to the ciphertext to the entities which need to decrypt the
- * ciphertext.</li>
- * <li>If you are using RSA encryption without padding, consider switching to padding
- * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
- * </ul>
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- */
- @NonNull
- public Builder setRandomizedEncryptionRequired(boolean required) {
- mRandomizedEncryptionRequired = required;
- return this;
- }
-
- /**
- * Sets whether user authentication is required to use this key.
- *
- * <p>By default, the key can be used without user authentication.
- *
- * <p>When user authentication is required, the user authorizes the use of the key by
- * authenticating to this Android device using a subset of their secure lock screen
- * credentials. Different authentication methods are used depending on whether the every
- * use of the key must be authenticated (as specified by
- * {@link #setUserAuthenticationValidityDurationSeconds(int)}).
- * <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
- * information</a>.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @see #setUserAuthenticationValidityDurationSeconds(int)
- */
- @NonNull
- public Builder setUserAuthenticationRequired(boolean required) {
- mUserAuthenticationRequired = required;
- return this;
- }
-
- /**
- * Sets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated. This has effect only if user authentication is required.
- *
- * <p>By default, the user needs to authenticate for every use of the key.
- *
- * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
- *
- * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
- * every use of the key.
- *
- * @see #setUserAuthenticationRequired(boolean)
- */
- @NonNull
- public Builder setUserAuthenticationValidityDurationSeconds(
- @IntRange(from = -1) int seconds) {
- mUserAuthenticationValidityDurationSeconds = seconds;
- return this;
- }
-
- /**
* Builds the instance of the {@code KeyStoreParameter}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -635,18 +164,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
public KeyStoreParameter build() {
return new KeyStoreParameter(
mContext,
- mFlags,
- mKeyValidityStart,
- mKeyValidityForOriginationEnd,
- mKeyValidityForConsumptionEnd,
- mPurposes,
- mEncryptionPaddings,
- mSignaturePaddings,
- mDigests,
- mBlockModes,
- mRandomizedEncryptionRequired,
- mUserAuthenticationRequired,
- mUserAuthenticationValidityDurationSeconds);
+ mFlags);
}
}
}
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index 548296b..618ba47 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -18,6 +18,8 @@ package android.security;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
import libcore.util.EmptyArray;
@@ -55,7 +57,7 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
throw new InvalidKeySpecException(
"Key material export of Android KeyStore keys is not supported");
}
- if (!KeyStoreKeySpec.class.equals(keySpecClass)) {
+ if (!KeyInfo.class.equals(keySpecClass)) {
throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
}
String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
@@ -75,22 +77,22 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
}
boolean insideSecureHardware;
- @KeyStoreKeyProperties.OriginEnum int origin;
+ @KeyProperties.OriginEnum int origin;
int keySize;
- @KeyStoreKeyProperties.PurposeEnum int purposes;
+ @KeyProperties.PurposeEnum int purposes;
String[] encryptionPaddings;
- @KeyStoreKeyProperties.DigestEnum String[] digests;
- @KeyStoreKeyProperties.BlockModeEnum String[] blockModes;
+ @KeyProperties.DigestEnum String[] digests;
+ @KeyProperties.BlockModeEnum String[] blockModes;
int keymasterSwEnforcedUserAuthenticators;
int keymasterHwEnforcedUserAuthenticators;
try {
if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
insideSecureHardware = true;
- origin = KeyStoreKeyProperties.Origin.fromKeymaster(
+ origin = KeyProperties.Origin.fromKeymaster(
keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
insideSecureHardware = false;
- origin = KeyStoreKeyProperties.Origin.fromKeymaster(
+ origin = KeyProperties.Origin.fromKeymaster(
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else {
throw new InvalidKeySpecException("Key origin not available");
@@ -100,15 +102,14 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
throw new InvalidKeySpecException("Key size not available");
}
keySize = keySizeInteger;
- purposes = KeyStoreKeyProperties.Purpose.allFromKeymaster(
+ purposes = KeyProperties.Purpose.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE));
List<String> encryptionPaddingsList = new ArrayList<String>();
for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
- @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding;
+ @KeyProperties.EncryptionPaddingEnum String jcaPadding;
try {
- jcaPadding =
- KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
+ jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException(
"Unsupported encryption padding: " + keymasterPadding);
@@ -118,9 +119,9 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
encryptionPaddings =
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
- digests = KeyStoreKeyProperties.Digest.allFromKeymaster(
+ digests = KeyProperties.Digest.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
- blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster(
+ blockModes = KeyProperties.BlockMode.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
keymasterSwEnforcedUserAuthenticators =
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
@@ -154,7 +155,7 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
&& (keymasterHwEnforcedUserAuthenticators != 0)
&& (keymasterSwEnforcedUserAuthenticators == 0);
- return new KeyStoreKeySpec(entryAlias,
+ return new KeyInfo(entryAlias,
insideSecureHardware,
origin,
keySize,
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index df67ae7..0f8f190 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -16,7 +16,6 @@
package android.security;
-import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
@@ -73,7 +72,6 @@ public abstract class KeymasterUtils {
* use of the key needs authorization.
*/
public static void addUserAuthArgs(KeymasterArguments args,
- Context context,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
if (!userAuthenticationRequired) {
@@ -85,7 +83,7 @@ public abstract class KeymasterUtils {
// Every use of this key needs to be authorized by the user. This currently means
// fingerprint-only auth.
FingerprintManager fingerprintManager =
- context.getSystemService(FingerprintManager.class);
+ KeyStore.getApplicationContext().getSystemService(FingerprintManager.class);
if ((fingerprintManager == null) || (!fingerprintManager.isHardwareDetected())) {
throw new IllegalStateException(
"This device does not support keys which require authentication for every"
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/keystore/KeyExpiredException.java
index f58e48a..15b8d67 100644
--- a/keystore/java/android/security/KeyExpiredException.java
+++ b/keystore/java/android/security/keystore/KeyExpiredException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
new file mode 100644
index 0000000..fa3b1cb
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -0,0 +1,857 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.app.KeyguardManager;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.security.ArrayUtils;
+import android.security.KeyStore;
+
+import java.math.BigInteger;
+import java.security.KeyPairGenerator;
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Date;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * {@link AlgorithmParameterSpec} for initializing a {@link KeyPairGenerator} or a
+ * {@link KeyGenerator} of the <a href="{@docRoot}training/articles/keystore.html">Android Keystore
+ * system</a>. The spec determines whether user authentication is required for using the key, what
+ * uses the key is authorized for (e.g., only for signing -- decryption not permitted), whether the
+ * key should be encrypted at rest, the key's and validity start and end dates.
+ *
+ * <p>To generate an asymmetric key pair or a symmetric key, create an instance of this class using
+ * the {@link Builder}, initialize a {@code KeyPairGenerator} or a {@code KeyGenerator} of the
+ * desired key type (e.g., {@code EC} or {@code AES} -- see
+ * {@link KeyProperties}.{@code KEY_ALGORITHM} constants) from the {@code AndroidKeyStore} provider
+ * with the {@code KeyPairGeneratorSpec} instance, and then generate a key or key pair using
+ * {@link KeyPairGenerator#generateKeyPair()}.
+ *
+ * <p>The generated key pair or key will be returned by the generator and also stored in the Android
+ * Keystore system under the alias specified in this spec. To obtain the secret or private key from
+ * the Android KeyStore use {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)}
+ * or {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
+ * To obtain the public key from the Android Keystore system use
+ * {@link java.security.KeyStore#getCertificate(String)} and then
+ * {@link Certificate#getPublicKey()}.
+ *
+ * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
+ * the Android KeyStore. This is because the {@link java.security.KeyStore} abstraction does not
+ * support storing key pairs without a certificate. The subject, serial number, and validity dates
+ * of the certificate can be customized in this spec. The self-signed certificate may be replaced at
+ * a later time by a certificate signed by a Certificate Authority (CA).
+ *
+ * <p>NOTE: The key material of the generated symmetric and private keys is not accessible. The key
+ * material of the public keys is accessible.
+ *
+ * <p><h3>Example: Asymmetric key pair</h3>
+ * The following example illustrates how to generate an EC key pair in the Android KeyStore system
+ * under alias {@code key1} authorized to be used only for signing using SHA-256, SHA-384,
+ * or SHA-512 digest and only if the user has been authenticated within the last five minutes.
+ * <pre> {@code
+ * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_EC,
+ * "AndroidKeyStore");
+ * keyPairGenerator.initialize(
+ * new KeyGenParameterSpec.Builder("key1",
+ * KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ * .setDigests(KeyProperties.DIGEST_SHA256
+ * | KeyProperties.DIGEST_SHA384
+ * | KeyProperties.DIGEST_SHA512)
+ * // Only permit this key to be used if the user authenticated
+ * // within the last five minutes.
+ * .setUserAuthenticationRequired(true)
+ * .setUserAuthenticationValidityDurationSeconds(5 * 60)
+ * .build());
+ * KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ *
+ * // The key pair can also be obtained from the Android Keystore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null);
+ * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();
+ * }</pre>
+ *
+ * <p><h3>Example: Symmetric key</h3>
+ * The following example illustrates how to generate an AES key in the Android KeyStore system under
+ * alias {@code key2} authorized to be used only for encryption/decryption in CTR mode.
+ * <pre> {@code
+ * KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_HMAC_SHA256,
+ * "AndroidKeyStore");
+ * keyGenerator.initialize(
+ * new KeyGenParameterSpec.Builder("key2",
+ * KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ * .setBlockModes(KeyProperties.BLOCK_MODE_CTR)
+ * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ * .build());
+ * SecretKey key = keyGenerator.generateKey();
+ *
+ * // The key can also be obtained from the Android Keystore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * key = (SecretKey) keyStore.getKey("key2", null);
+ * }</pre>
+ */
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+
+ private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+ private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+ private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+ private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
+ private final String mKeystoreAlias;
+ private final int mKeySize;
+ private final AlgorithmParameterSpec mSpec;
+ private final X500Principal mCertificateSubject;
+ private final BigInteger mCertificateSerialNumber;
+ private final Date mCertificateNotBefore;
+ private final Date mCertificateNotAfter;
+ private final int mFlags;
+ private final Date mKeyValidityStart;
+ private final Date mKeyValidityForOriginationEnd;
+ private final Date mKeyValidityForConsumptionEnd;
+ private final @KeyProperties.PurposeEnum int mPurposes;
+ private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyProperties.BlockModeEnum String[] mBlockModes;
+ private final boolean mRandomizedEncryptionRequired;
+ private final boolean mUserAuthenticationRequired;
+ private final int mUserAuthenticationValidityDurationSeconds;
+
+ /**
+ * @hide should be built with Builder
+ */
+ public KeyGenParameterSpec(
+ String keyStoreAlias,
+ int keySize,
+ AlgorithmParameterSpec spec,
+ X500Principal certificateSubject,
+ BigInteger certificateSerialNumber,
+ Date certificateNotBefore,
+ Date certificateNotAfter,
+ int flags,
+ Date keyValidityStart,
+ Date keyValidityForOriginationEnd,
+ Date keyValidityForConsumptionEnd,
+ @KeyProperties.PurposeEnum int purposes,
+ @KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyProperties.BlockModeEnum String[] blockModes,
+ boolean randomizedEncryptionRequired,
+ boolean userAuthenticationRequired,
+ int userAuthenticationValidityDurationSeconds) {
+ if (TextUtils.isEmpty(keyStoreAlias)) {
+ throw new IllegalArgumentException("keyStoreAlias must not be empty");
+ } else if ((userAuthenticationValidityDurationSeconds < 0)
+ && (userAuthenticationValidityDurationSeconds != -1)) {
+ throw new IllegalArgumentException(
+ "userAuthenticationValidityDurationSeconds must not be negative");
+ }
+
+ if (certificateSubject == null) {
+ certificateSubject = DEFAULT_CERT_SUBJECT;
+ }
+ if (certificateNotBefore == null) {
+ certificateNotBefore = DEFAULT_CERT_NOT_BEFORE;
+ }
+ if (certificateNotAfter == null) {
+ certificateNotAfter = DEFAULT_CERT_NOT_AFTER;
+ }
+ if (certificateSerialNumber == null) {
+ certificateSerialNumber = DEFAULT_CERT_SERIAL_NUMBER;
+ }
+
+ if (certificateNotAfter.before(certificateNotBefore)) {
+ throw new IllegalArgumentException("certificateNotAfter < certificateNotBefore");
+ }
+
+ mKeystoreAlias = keyStoreAlias;
+ mKeySize = keySize;
+ mSpec = spec;
+ mCertificateSubject = certificateSubject;
+ mCertificateSerialNumber = certificateSerialNumber;
+ mCertificateNotBefore = certificateNotBefore;
+ mCertificateNotAfter = certificateNotAfter;
+ mFlags = flags;
+ mKeyValidityStart = keyValidityStart;
+ mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+ mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+ mPurposes = purposes;
+ mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ mEncryptionPaddings =
+ ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
+ mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
+ mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
+ mRandomizedEncryptionRequired = randomizedEncryptionRequired;
+ mUserAuthenticationRequired = userAuthenticationRequired;
+ mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+ }
+
+ /**
+ * Returns the alias that will be used in the {@code java.security.KeyStore}
+ * in conjunction with the {@code AndroidKeyStore}.
+ */
+ public String getKeystoreAlias() {
+ return mKeystoreAlias;
+ }
+
+ /**
+ * Returns the requested key size or {@code -1} if default size should be used.
+ */
+ public int getKeySize() {
+ return mKeySize;
+ }
+
+ /**
+ * Returns the {@link AlgorithmParameterSpec} that will be used for creation
+ * of the key pair.
+ */
+ @NonNull
+ public AlgorithmParameterSpec getAlgorithmParameterSpec() {
+ return mSpec;
+ }
+
+ /**
+ * Returns the subject distinguished name to be used on the X.509 certificate that will be put
+ * in the {@link java.security.KeyStore}.
+ */
+ @NonNull
+ public X500Principal getCertificateSubject() {
+ return mCertificateSubject;
+ }
+
+ /**
+ * Returns the serial number to be used on the X.509 certificate that will be put in the
+ * {@link java.security.KeyStore}.
+ */
+ @NonNull
+ public BigInteger getCertificateSerialNumber() {
+ return mCertificateSerialNumber;
+ }
+
+ /**
+ * Returns the start date to be used on the X.509 certificate that will be put in the
+ * {@link java.security.KeyStore}.
+ */
+ @NonNull
+ public Date getCertificateNotBefore() {
+ return mCertificateNotBefore;
+ }
+
+ /**
+ * Returns the end date to be used on the X.509 certificate that will be put in the
+ * {@link java.security.KeyStore}.
+ */
+ @NonNull
+ public Date getCertificateNotAfter() {
+ return mCertificateNotAfter;
+ }
+
+ /**
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns {@code true} if the key must be encrypted at rest. This will protect the key with the
+ * secure lock screen credential (e.g., password, PIN, or pattern).
+ *
+ * <p>Note that encrypting the key at rest requires that the secure lock screen (e.g., password,
+ * PIN, pattern) is set up, otherwise key generation will fail. Moreover, this key will be
+ * deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device
+ * Administrator). Finally, this key cannot be used until the user unlocks the secure lock
+ * screen after boot.
+ *
+ * @see KeyguardManager#isDeviceSecure()
+ */
+ public boolean isEncryptionAtRestRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+ }
+
+ /**
+ * Returns the time instant before which the key is not yet valid or {@code null} if not
+ * restricted.
+ */
+ @Nullable
+ public Date getKeyValidityStart() {
+ return mKeyValidityStart;
+ }
+
+ /**
+ * Returns the time instant after which the key is no longer valid for decryption and
+ * verification or {@code null} if not restricted.
+ */
+ @Nullable
+ public Date getKeyValidityForConsumptionEnd() {
+ return mKeyValidityForConsumptionEnd;
+ }
+
+ /**
+ * Returns the time instant after which the key is no longer valid for encryption and signing
+ * or {@code null} if not restricted.
+ */
+ @Nullable
+ public Date getKeyValidityForOriginationEnd() {
+ return mKeyValidityForOriginationEnd;
+ }
+
+ /**
+ * Returns the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
+ * Attempts to use the key for any other purpose will be rejected.
+ *
+ * <p>See {@link KeyProperties}.{@code PURPOSE} flags.
+ */
+ public @KeyProperties.PurposeEnum int getPurposes() {
+ return mPurposes;
+ }
+
+ /**
+ * Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the
+ * key can be used or {@code null} if not specified.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isDigestsSpecified()
+ */
+ @NonNull
+ public @KeyProperties.DigestEnum String[] getDigests() {
+ if (mDigests == null) {
+ throw new IllegalStateException("Digests not specified");
+ }
+ return ArrayUtils.cloneIfNotEmpty(mDigests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digest algorithms with which the key can be used has been
+ * specified.
+ *
+ * @see #getDigests()
+ */
+ @NonNull
+ public boolean isDigestsSpecified() {
+ return mDigests != null;
+ }
+
+ /**
+ * Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding},
+ * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
+ * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
+ * rejected.
+ *
+ * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ */
+ @NonNull
+ public @KeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
+ return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
+ }
+
+ /**
+ * Gets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
+ * can be used when signing/verifying. Attempts to use the key with any other padding scheme
+ * will be rejected.
+ *
+ * <p>See {@link KeyProperties}.{@code SIGNATURE_PADDING} constants.
+ */
+ @NonNull
+ public @KeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
+ return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
+ }
+
+ /**
+ * Gets the set of block modes (e.g., {@code CBC}, {@code CTR}) with which the key can be used
+ * when encrypting/decrypting. Attempts to use the key with any other block modes will be
+ * rejected.
+ *
+ * <p>See {@link KeyProperties}.{@code BLOCK_MODE} constants.
+ */
+ @NonNull
+ public @KeyProperties.BlockModeEnum String[] getBlockModes() {
+ return ArrayUtils.cloneIfNotEmpty(mBlockModes);
+ }
+
+ /**
+ * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
+ * different ciphertexts for the same plaintext every time. The formal cryptographic property
+ * being required is <em>indistinguishability under chosen-plaintext attack ({@code
+ * IND-CPA})</em>. This property is important because it mitigates several classes of
+ * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
+ * given plaintext always produces the same ciphertext, an attacker may see the repeated
+ * ciphertexts and be able to deduce something about the plaintext.
+ */
+ public boolean isRandomizedEncryptionRequired() {
+ return mRandomizedEncryptionRequired;
+ }
+
+ /**
+ * Returns {@code true} if user authentication is required for this key to be used.
+ *
+ * <p>This restriction applies only to private key operations. Public key operations are not
+ * restricted.
+ *
+ * @see #getUserAuthenticationValidityDurationSeconds()
+ */
+ public boolean isUserAuthenticationRequired() {
+ return mUserAuthenticationRequired;
+ }
+
+ /**
+ * Gets the duration of time (seconds) for which this key can be used after the user is
+ * successfully authenticated. This has effect only if user authentication is required.
+ *
+ * <p>This restriction applies only to private key operations. Public key operations are not
+ * restricted.
+ *
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
+ */
+ public int getUserAuthenticationValidityDurationSeconds() {
+ return mUserAuthenticationValidityDurationSeconds;
+ }
+
+ /**
+ * Builder of {@link KeyGenParameterSpec} instances.
+ */
+ public final static class Builder {
+ private final String mKeystoreAlias;
+ private @KeyProperties.PurposeEnum int mPurposes;
+
+ private int mKeySize = -1;
+ private AlgorithmParameterSpec mSpec;
+ private X500Principal mCertificateSubject;
+ private BigInteger mCertificateSerialNumber;
+ private Date mCertificateNotBefore;
+ private Date mCertificateNotAfter;
+ private int mFlags;
+ private Date mKeyValidityStart;
+ private Date mKeyValidityForOriginationEnd;
+ private Date mKeyValidityForConsumptionEnd;
+ private @KeyProperties.DigestEnum String[] mDigests;
+ private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private @KeyProperties.BlockModeEnum String[] mBlockModes;
+ private boolean mRandomizedEncryptionRequired = true;
+ private boolean mUserAuthenticationRequired;
+ private int mUserAuthenticationValidityDurationSeconds = -1;
+
+ /**
+ * Creates a new instance of the {@code Builder}.
+ *
+ * @param keystoreAlias alias of the entry in which the generated key will appear in
+ * Android KeyStore.
+ * @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
+ * used. Attempts to use the key for any other purpose will be rejected.
+ *
+ * <p>If the set of purposes for which the key can be used does not contain
+ * {@link KeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
+ * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an
+ * invalid signature. This is OK if the certificate is only used for obtaining the
+ * public key from Android KeyStore.
+ *
+ * <p><b>NOTE: The {@code purposes} parameter has currently no effect on asymmetric
+ * key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code PURPOSE} flags.
+ */
+ public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) {
+ if (keystoreAlias == null) {
+ throw new NullPointerException("keystoreAlias == null");
+ }
+ mKeystoreAlias = keystoreAlias;
+ mPurposes = purposes;
+ }
+
+ /**
+ * Sets the size (in bits) of the key to be generated. For instance, for RSA keys this sets
+ * 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.
+ */
+ @NonNull
+ public Builder setKeySize(int keySize) {
+ if (keySize < 0) {
+ throw new IllegalArgumentException("keySize < 0");
+ }
+ mKeySize = keySize;
+ return this;
+ }
+
+ /**
+ * Sets the algorithm-specific key generation parameters. For example, for RSA keys this may
+ * be an instance of {@link java.security.spec.RSAKeyGenParameterSpec}.
+ */
+ public Builder setAlgorithmParameterSpec(@NonNull AlgorithmParameterSpec spec) {
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+ mSpec = spec;
+ return this;
+ }
+
+ /**
+ * Sets the subject used for the self-signed certificate of the generated key pair.
+ *
+ * <p>By default, the subject is {@code CN=fake}.
+ */
+ @NonNull
+ public Builder setCertificateSubject(@NonNull X500Principal subject) {
+ if (subject == null) {
+ throw new NullPointerException("subject == null");
+ }
+ mCertificateSubject = subject;
+ return this;
+ }
+
+ /**
+ * Sets the serial number used for the self-signed certificate of the generated key pair.
+ *
+ * <p>By default, the serial number is {@code 1}.
+ */
+ @NonNull
+ public Builder setCertificateSerialNumber(@NonNull BigInteger serialNumber) {
+ if (serialNumber == null) {
+ throw new NullPointerException("serialNumber == null");
+ }
+ mCertificateSerialNumber = serialNumber;
+ return this;
+ }
+
+ /**
+ * Sets the start of the validity period for the self-signed certificate of the generated
+ * key pair.
+ *
+ * <p>By default, this date is {@code Jan 1 1970}.
+ */
+ @NonNull
+ public Builder setCertificateNotBefore(@NonNull Date date) {
+ if (date == null) {
+ throw new NullPointerException("date == null");
+ }
+ mCertificateNotBefore = date;
+ return this;
+ }
+
+ /**
+ * Sets the end of the validity period for the self-signed certificate of the generated key
+ * pair.
+ *
+ * <p>By default, this date is {@code Jan 1 2048}.
+ */
+ @NonNull
+ public Builder setCertificateNotAfter(@NonNull Date date) {
+ if (date == null) {
+ throw new NullPointerException("date == null");
+ }
+ mCertificateNotAfter = date;
+ return this;
+ }
+
+ /**
+ * Sets whether this key pair or key must be encrypted at rest. This will protect the key
+ * pair or key with the secure lock screen credential (e.g., password, PIN, or pattern).
+ *
+ * <p>Note that enabling this feature requires that the secure lock screen (e.g., password,
+ * PIN, pattern) is set up, otherwise key generation will fail. Moreover, this key will be
+ * deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device
+ * Administrator). Finally, this key cannot be used until the user unlocks the secure lock
+ * screen after boot.
+ *
+ * @see KeyguardManager#isDeviceSecure()
+ */
+ @NonNull
+ public Builder setEncryptionAtRestRequired(boolean required) {
+ if (required) {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ } else {
+ mFlags &= ~KeyStore.FLAG_ENCRYPTED;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the time instant before which the key is not yet valid.
+ *
+ * <p>By default, the key is valid at any instant.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * @see #setKeyValidityEnd(Date)
+ */
+ @NonNull
+ public Builder setKeyValidityStart(Date startDate) {
+ mKeyValidityStart = startDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid.
+ *
+ * <p>By default, the key is valid at any instant.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * @see #setKeyValidityStart(Date)
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ * @see #setKeyValidityForOriginationEnd(Date)
+ */
+ @NonNull
+ public Builder setKeyValidityEnd(Date endDate) {
+ setKeyValidityForOriginationEnd(endDate);
+ setKeyValidityForConsumptionEnd(endDate);
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for encryption and signing.
+ *
+ * <p>By default, the key is valid at any instant.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * @see #setKeyValidityForConsumptionEnd(Date)
+ */
+ @NonNull
+ public Builder setKeyValidityForOriginationEnd(Date endDate) {
+ mKeyValidityForOriginationEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Sets the time instant after which the key is no longer valid for decryption and
+ * verification.
+ *
+ * <p>By default, the key is valid at any instant.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * @see #setKeyValidityForOriginationEnd(Date)
+ */
+ @NonNull
+ public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+ mKeyValidityForConsumptionEnd = endDate;
+ return this;
+ }
+
+ /**
+ * Sets the set of digests algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which
+ * the key can be used when signing/verifying. Attempts to use the key with any other digest
+ * algorithm will be rejected.
+ *
+ * <p>This must be specified for keys which are used for signing/verification. For HMAC
+ * keys, the set of digests defaults to the digest associated with the key algorithm (e.g.,
+ * {@code SHA-256} for key algorithm {@code HmacSHA256}
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * @see KeyProperties.Digest
+ */
+ @NonNull
+ public Builder setDigests(@KeyProperties.DigestEnum String... digests) {
+ mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ return this;
+ }
+
+ /**
+ * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
+ * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
+ * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
+ * rejected.
+ *
+ * <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ */
+ @NonNull
+ public Builder setEncryptionPaddings(
+ @KeyProperties.EncryptionPaddingEnum String... paddings) {
+ mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
+ return this;
+ }
+
+ /**
+ * Sets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
+ * can be used when signing/verifying. Attempts to use the key with any other padding scheme
+ * will be rejected.
+ *
+ * <p>This must be specified for RSA keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code SIGNATURE_PADDING} constants.
+ */
+ @NonNull
+ public Builder setSignaturePaddings(
+ @KeyProperties.SignaturePaddingEnum String... paddings) {
+ mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
+ return this;
+ }
+
+ /**
+ * Sets the set of block modes (e.g., {@code CBC}, {@code CTR}, {@code ECB}) with which the
+ * key can be used when encrypting/decrypting. Attempts to use the key with any other block
+ * modes will be rejected.
+ *
+ * <p>This must be specified for encryption/decryption keys.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code BLOCK_MODE} constants.
+ */
+ @NonNull
+ public Builder setBlockModes(@KeyProperties.BlockModeEnum String... blockModes) {
+ mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
+ return this;
+ }
+
+ /**
+ * Sets whether encryption using this key must be sufficiently randomized to produce
+ * different ciphertexts for the same plaintext every time. The formal cryptographic
+ * property being required is <em>indistinguishability under chosen-plaintext attack
+ * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
+ * of weaknesses due to which ciphertext may leak information about plaintext. For example,
+ * if a given plaintext always produces the same ciphertext, an attacker may see the
+ * repeated ciphertexts and be able to deduce something about the plaintext.
+ *
+ * <p>By default, {@code IND-CPA} is required.
+ *
+ * <p>When {@code IND-CPA} is required:
+ * <ul>
+ * <li>encryption/decryption transformation which do not offer {@code IND-CPA}, such as
+ * {@code ECB} with a symmetric encryption algorithm, or RSA encryption/decryption without
+ * padding, are prohibited;</li>
+ * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM},
+ * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are
+ * used.</li>
+ * </ul>
+ *
+ * <p>Before disabling this requirement, consider the following approaches instead:
+ * <ul>
+ * <li>If you are generating a random IV for encryption and then initializing a {@code}
+ * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV
+ * instead. This will occur if the {@code Cipher} is initialized for encryption without an
+ * IV. The IV can then be queried via {@link Cipher#getIV()}.</li>
+ * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully
+ * random, such as the name of the file being encrypted, or transaction ID, or password,
+ * or a device identifier), consider changing your design to use a random IV which will then
+ * be provided in addition to the ciphertext to the entities which need to decrypt the
+ * ciphertext.</li>
+ * <li>If you are using RSA encryption without padding, consider switching to encryption
+ * padding schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
+ * </ul>
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ */
+ @NonNull
+ public Builder setRandomizedEncryptionRequired(boolean required) {
+ mRandomizedEncryptionRequired = required;
+ return this;
+ }
+
+ /**
+ * Sets whether user authentication is required to use this key.
+ *
+ * <p>By default, the key can be used without user authentication.
+ *
+ * <p>When user authentication is required, the user authorizes the use of the key by
+ * authenticating to this Android device using a subset of their secure lock screen
+ * credentials. Different authentication methods are used depending on whether the every
+ * use of the key must be authenticated (as specified by
+ * {@link #setUserAuthenticationValidityDurationSeconds(int)}).
+ * <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
+ * information</a>.
+ *
+ * <p>This restriction applies only to private key operations. Public key operations are not
+ * restricted.
+ *
+ * <p><b>NOTE: This has currently no effect.</b>
+ *
+ * @see #setUserAuthenticationValidityDurationSeconds(int)
+ */
+ @NonNull
+ public Builder setUserAuthenticationRequired(boolean required) {
+ mUserAuthenticationRequired = required;
+ return this;
+ }
+
+ /**
+ * Sets the duration of time (seconds) for which this key can be used after the user is
+ * successfully authenticated. This has effect only if user authentication is required.
+ *
+ * <p>By default, the user needs to authenticate for every use of the key.
+ *
+ * <p><b>NOTE: This has currently no effect.</b>
+ *
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
+ * every use of the key.
+ *
+ * @see #setUserAuthenticationRequired(boolean)
+ */
+ @NonNull
+ public Builder setUserAuthenticationValidityDurationSeconds(
+ @IntRange(from = -1) int seconds) {
+ mUserAuthenticationValidityDurationSeconds = seconds;
+ return this;
+ }
+
+ /**
+ * Builds an instance of {@code KeyGenParameterSpec}.
+ *
+ * @throws IllegalArgumentException if a required field is missing
+ */
+ @NonNull
+ public KeyGenParameterSpec build() {
+ return new KeyGenParameterSpec(
+ mKeystoreAlias,
+ mKeySize,
+ mSpec,
+ mCertificateSubject,
+ mCertificateSerialNumber,
+ mCertificateNotBefore,
+ mCertificateNotAfter,
+ mFlags,
+ mKeyValidityStart,
+ mKeyValidityForOriginationEnd,
+ mKeyValidityForConsumptionEnd,
+ mPurposes,
+ mDigests,
+ mEncryptionPaddings,
+ mSignaturePaddings,
+ mBlockModes,
+ mRandomizedEncryptionRequired,
+ mUserAuthenticationRequired,
+ mUserAuthenticationValidityDurationSeconds);
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/keystore/KeyInfo.java
index 4c43f89..aec2512 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.security.ArrayUtils;
import java.security.PrivateKey;
import java.security.spec.KeySpec;
@@ -27,55 +28,55 @@ import javax.crypto.SecretKey;
/**
* Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
- * KeyStore</a>. This class describes whether the key material is available in
+ * Keystore system</a>. This class describes whether the key material is available in
* plaintext outside of secure hardware, whether user authentication is required for using the key
* and whether this requirement is enforced by secure hardware, the key's origin, what uses the key
* is authorized for (e.g., only in {@code CBC} mode, or signing only), whether the key should be
* encrypted at rest, the key's and validity start and end dates.
*
* <p><h3>Example: Symmetric Key</h3>
- * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided
- * Android KeyStore {@link SecretKey}.
+ * The following example illustrates how to obtain a {@code KeyInfo} describing the provided Android
+ * Keystore {@link SecretKey}.
* <pre> {@code
- * SecretKey key = ...; // Android KeyStore key
+ * SecretKey key = ...; // Android Keystore key
*
* SecretKeyFactory factory = SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
- * KeyStoreKeySpec spec;
+ * KeyInfo keyInfo;
* try &#123;
- * spec = (KeyStoreKeySpec) factory.getKeySpec(key, KeyStoreKeySpec.class);
+ * keyInfo = (KeyInfo) factory.getKeySpec(key, KeyInfo.class);
* &#125; catch (InvalidKeySpecException e) &#123;
* // Not an Android KeyStore key.
* &#125;
* }</pre>
*
* <p><h3>Example: Private Key</h3>
- * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided
+ * The following example illustrates how to obtain a {@code KeyInfo} describing the provided
* Android KeyStore {@link PrivateKey}.
* <pre> {@code
* PrivateKey key = ...; // Android KeyStore key
*
* KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
- * KeyStoreKeySpec spec;
+ * KeyInfo keyInfo;
* try &#123;
- * spec = factory.getKeySpec(key, KeyStoreKeySpec.class);
+ * keyInfo = factory.getKeySpec(key, KeyInfo.class);
* &#125; catch (InvalidKeySpecException e) &#123;
* // Not an Android KeyStore key.
* &#125;
* }</pre>
*/
-public class KeyStoreKeySpec implements KeySpec {
+public class KeyInfo implements KeySpec {
private final String mKeystoreAlias;
private final int mKeySize;
private final boolean mInsideSecureHardware;
- private final @KeyStoreKeyProperties.OriginEnum int mOrigin;
+ private final @KeyProperties.OriginEnum int mOrigin;
private final Date mKeyValidityStart;
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
+ private final @KeyProperties.PurposeEnum int mPurposes;
+ private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
@@ -83,18 +84,18 @@ public class KeyStoreKeySpec implements KeySpec {
/**
* @hide
*/
- KeyStoreKeySpec(String keystoreKeyAlias,
+ public KeyInfo(String keystoreKeyAlias,
boolean insideSecureHardware,
- @KeyStoreKeyProperties.OriginEnum int origin,
+ @KeyProperties.OriginEnum int origin,
int keySize,
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
- @KeyStoreKeyProperties.PurposeEnum int purposes,
- @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
- @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
- @KeyStoreKeyProperties.DigestEnum String[] digests,
- @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
+ @KeyProperties.PurposeEnum int purposes,
+ @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.BlockModeEnum String[] blockModes,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
boolean userAuthenticationRequirementEnforcedBySecureHardware) {
@@ -135,9 +136,9 @@ public class KeyStoreKeySpec implements KeySpec {
}
/**
- * Gets the origin of the key. See {@link KeyStoreKeyProperties}.{@code ORIGIN} constants.
+ * Gets the origin of the key. See {@link KeyProperties}.{@code ORIGIN} constants.
*/
- public @KeyStoreKeyProperties.OriginEnum int getOrigin() {
+ public @KeyProperties.OriginEnum int getOrigin() {
return mOrigin;
}
@@ -182,9 +183,9 @@ public class KeyStoreKeySpec implements KeySpec {
* Gets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
* Attempts to use the key for any other purpose will be rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
+ * <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
- public @KeyStoreKeyProperties.PurposeEnum int getPurposes() {
+ public @KeyProperties.PurposeEnum int getPurposes() {
return mPurposes;
}
@@ -193,10 +194,10 @@ public class KeyStoreKeySpec implements KeySpec {
* when encrypting/decrypting. Attempts to use the key with any other block modes will be
* rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
+ * <p>See {@link KeyProperties}.{@code BLOCK_MODE} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
+ public @KeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -205,10 +206,10 @@ public class KeyStoreKeySpec implements KeySpec {
* {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to use
* the key with any other padding scheme will be rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
+ public @KeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
@@ -217,10 +218,10 @@ public class KeyStoreKeySpec implements KeySpec {
* can be used when signing/verifying. Attempts to use the key with any other padding scheme
* will be rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code SIGNATURE_PADDING} constants.
+ * <p>See {@link KeyProperties}.{@code SIGNATURE_PADDING} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
+ public @KeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
@@ -228,10 +229,10 @@ public class KeyStoreKeySpec implements KeySpec {
* Gets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the key
* can be used.
*
- * @see KeyStoreKeyProperties.Digest
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
+ public @KeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/keystore/KeyNotYetValidException.java
index 4ea27ef..2cec77d 100644
--- a/keystore/java/android/security/KeyNotYetValidException.java
+++ b/keystore/java/android/security/keystore/KeyNotYetValidException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/KeyPermanentlyInvalidatedException.java b/keystore/java/android/security/keystore/KeyPermanentlyInvalidatedException.java
index 229eab0..e320c9c 100644
--- a/keystore/java/android/security/KeyPermanentlyInvalidatedException.java
+++ b/keystore/java/android/security/keystore/KeyPermanentlyInvalidatedException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index b58a7dd..e3c2d1d 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -30,10 +30,10 @@ import java.util.Collection;
import java.util.Locale;
/**
- * Properties of {@code AndroidKeyStore} keys.
+ * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
*/
-public abstract class KeyStoreKeyProperties {
- private KeyStoreKeyProperties() {}
+public abstract class KeyProperties {
+ private KeyProperties() {}
/**
* @hide
@@ -68,10 +68,13 @@ public abstract class KeyStoreKeyProperties {
*/
public static final int PURPOSE_VERIFY = 1 << 3;
- static abstract class Purpose {
+ /**
+ * @hide
+ */
+ public static abstract class Purpose {
private Purpose() {}
- static int toKeymaster(@PurposeEnum int purpose) {
+ public static int toKeymaster(@PurposeEnum int purpose) {
switch (purpose) {
case PURPOSE_ENCRYPT:
return KeymasterDefs.KM_PURPOSE_ENCRYPT;
@@ -86,7 +89,7 @@ public abstract class KeyStoreKeyProperties {
}
}
- static @PurposeEnum int fromKeymaster(int purpose) {
+ public static @PurposeEnum int fromKeymaster(int purpose) {
switch (purpose) {
case KeymasterDefs.KM_PURPOSE_ENCRYPT:
return PURPOSE_ENCRYPT;
@@ -102,7 +105,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static int[] allToKeymaster(@PurposeEnum int purposes) {
+ public static int[] allToKeymaster(@PurposeEnum int purposes) {
int[] result = getSetFlags(purposes);
for (int i = 0; i < result.length; i++) {
result[i] = toKeymaster(result[i]);
@@ -110,7 +113,7 @@ public abstract class KeyStoreKeyProperties {
return result;
}
- static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
+ public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
@PurposeEnum int result = 0;
for (int keymasterPurpose : purposes) {
result |= fromKeymaster(keymasterPurpose);
@@ -159,10 +162,14 @@ public abstract class KeyStoreKeyProperties {
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";
- static abstract class KeyAlgorithm {
+ /**
+ * @hide
+ */
+ public static abstract class KeyAlgorithm {
private KeyAlgorithm() {}
- static int toKeymasterSecretKeyAlgorithm(@NonNull @KeyAlgorithmEnum String algorithm) {
+ public static int toKeymasterSecretKeyAlgorithm(
+ @NonNull @KeyAlgorithmEnum String algorithm) {
if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
return KeymasterDefs.KM_ALGORITHM_AES;
} else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
@@ -174,7 +181,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
+ public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
int keymasterAlgorithm, int keymasterDigest) {
switch (keymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_AES:
@@ -210,7 +217,7 @@ public abstract class KeyStoreKeyProperties {
*
* @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
*/
- static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
+ public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
String algorithmUpper = algorithm.toUpperCase(Locale.US);
if (algorithmUpper.startsWith("HMAC")) {
String digestUpper = algorithmUpper.substring("HMAC".length());
@@ -259,10 +266,13 @@ public abstract class KeyStoreKeyProperties {
/** Galois/Counter Mode (GCM) block mode. */
public static final String BLOCK_MODE_GCM = "GCM";
- static abstract class BlockMode {
+ /**
+ * @hide
+ */
+ public static abstract class BlockMode {
private BlockMode() {}
- static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
+ public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
return KeymasterDefs.KM_MODE_ECB;
} else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
@@ -277,7 +287,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @BlockModeEnum String fromKeymaster(int blockMode) {
+ public static @BlockModeEnum String fromKeymaster(int blockMode) {
switch (blockMode) {
case KeymasterDefs.KM_MODE_ECB:
return BLOCK_MODE_ECB;
@@ -293,7 +303,8 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @BlockModeEnum String[] allFromKeymaster(@NonNull Collection<Integer> blockModes) {
+ public static @BlockModeEnum String[] allFromKeymaster(
+ @NonNull Collection<Integer> blockModes) {
if ((blockModes == null) || (blockModes.isEmpty())) {
return EmptyArray.STRING;
}
@@ -306,7 +317,7 @@ public abstract class KeyStoreKeyProperties {
return result;
}
- static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
+ public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
if ((blockModes == null) || (blockModes.length == 0)) {
return EmptyArray.INT;
}
@@ -350,10 +361,13 @@ public abstract class KeyStoreKeyProperties {
*/
public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
- static abstract class EncryptionPadding {
+ /**
+ * @hide
+ */
+ public static abstract class EncryptionPadding {
private EncryptionPadding() {}
- static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
+ public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
return KeymasterDefs.KM_PAD_NONE;
} else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
@@ -369,7 +383,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @EncryptionPaddingEnum String fromKeymaster(int padding) {
+ public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
switch (padding) {
case KeymasterDefs.KM_PAD_NONE:
return ENCRYPTION_PADDING_NONE;
@@ -386,7 +400,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
+ public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
if ((paddings == null) || (paddings.length == 0)) {
return EmptyArray.INT;
}
@@ -508,10 +522,13 @@ public abstract class KeyStoreKeyProperties {
*/
public static final String DIGEST_SHA512 = "SHA-512";
- static abstract class Digest {
+ /**
+ * @hide
+ */
+ public static abstract class Digest {
private Digest() {}
- static int toKeymaster(@NonNull @DigestEnum String digest) {
+ public static int toKeymaster(@NonNull @DigestEnum String digest) {
switch (digest.toUpperCase(Locale.US)) {
case DIGEST_SHA1:
return KeymasterDefs.KM_DIGEST_SHA1;
@@ -533,7 +550,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @DigestEnum String fromKeymaster(int digest) {
+ public static @DigestEnum String fromKeymaster(int digest) {
switch (digest) {
case KeymasterDefs.KM_DIGEST_NONE:
return DIGEST_NONE;
@@ -555,7 +572,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
+ public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
if (digests.isEmpty()) {
return EmptyArray.STRING;
}
@@ -569,7 +586,7 @@ public abstract class KeyStoreKeyProperties {
}
@NonNull
- static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
+ public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
if ((digests == null) || (digests.length == 0)) {
return EmptyArray.INT;
}
@@ -606,10 +623,13 @@ public abstract class KeyStoreKeyProperties {
*/
public static final int ORIGIN_UNKNOWN = 1 << 2;
- static abstract class Origin {
+ /**
+ * @hide
+ */
+ public static abstract class Origin {
private Origin() {}
- static @OriginEnum int fromKeymaster(int origin) {
+ public static @OriginEnum int fromKeymaster(int origin) {
switch (origin) {
case KeymasterDefs.KM_ORIGIN_GENERATED:
return ORIGIN_GENERATED;
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/keystore/KeyProtection.java
index e63566b..113159d 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -14,118 +14,137 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
-import android.text.TextUtils;
+import android.security.ArrayUtils;
+import android.security.KeyStore;
-import java.security.spec.AlgorithmParameterSpec;
+import java.security.Key;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.cert.Certificate;
import java.util.Date;
import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
/**
- * {@link AlgorithmParameterSpec} for initializing a {@link KeyGenerator} of the
+ * Specification of how a key or key pair is secured when imported into the
* <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. This class
- * specifies whether user authentication is required for using the key, what uses the key is
- * authorized for (e.g., only in {@code CBC} mode), whether the key should be encrypted at rest, the
- * key's and validity start and end dates.
+ * specifies parameters such as whether user authentication is required for using the key, what uses
+ * the key is authorized for (e.g., only in {@code CTR} mode, or only for signing -- decryption not
+ * permitted), whether the key should be encrypted at rest, the key's and validity start and end
+ * dates.
*
- * <p>To generate a key, create an instance of this class using the {@link Builder}, initialize a
- * {@code KeyGenerator} of the desired key type (e.g., {@code AES} or {@code HmacSHA256}) from the
- * {@code AndroidKeyStore} provider with the {@code KeyGeneratorSpec} instance, and then generate a
- * key using {@link KeyGenerator#generateKey()}.
+ * <p>To import a key or key pair into the Android KeyStore, create an instance of this class using
+ * the {@link Builder} and pass the instance into {@link java.security.KeyStore#setEntry(String, java.security.KeyStore.Entry, ProtectionParameter) KeyStore.setEntry}
+ * with the key or key pair being imported.
*
- * <p>The generated key will be returned by the {@code KeyGenerator} and also stored in the Android
- * KeyStore under the alias specified in this {@code KeyGeneratorSpec}. To obtain the key from the
- * Android KeyStore use
+ * <p>To obtain the secret/symmetric or private key from the Android KeyStore use
* {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or
* {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}.
+ * To obtain the public key from the Android KeyStore use
+ * {@link java.security.KeyStore#getCertificate(String)} and then
+ * {@link Certificate#getPublicKey()}.
*
- * <p>NOTE: The key material of the keys generating using the {@code KeyGeneratorSpec} is not
- * accessible.
+ * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible.
*
- * <p><h3>Example</h3>
- * The following example illustrates how to generate an HMAC key in the Android KeyStore under alias
- * {@code key1} authorized to be used only for HMAC with SHA-256 digest and only if the user has
- * been authenticated within the last five minutes.
+ * <p><h3>Example: Symmetric Key</h3>
+ * The following example illustrates how to import an AES key into the Android KeyStore under alias
+ * {@code key1} authorized to be used only for encryption/decryption in CBC mode with PKCS#7
+ * padding. The key must export its key material via {@link Key#getEncoded()} in {@code RAW} format.
* <pre> {@code
- * KeyGenerator keyGenerator = KeyGenerator.getInstance(
- * KeyStoreKeyProperties.KEY_ALGORITHM_HMAC_SHA256,
- * "AndroidKeyStore");
- * keyGenerator.initialize(
- * new KeyGeneratorSpec.Builder(context)
- * .setAlias("key1")
- * .setPurposes(KeyStoreKeyProperties.PURPOSE_SIGN
- * | KeyStoreKeyProperties.PURPOSE_VERIFY)
- * // Only permit this key to be used if the user authenticated
- * // within the last five minutes.
- * .setUserAuthenticationRequired(true)
- * .setUserAuthenticationValidityDurationSeconds(5 * 60)
+ * SecretKey key = ...; // AES key
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ * "key1",
+ * new KeyStore.SecretKeyEntry(key),
+ * new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ * .setBlockMode(KeyProperties.BLOCK_MODE_CBC)
+ * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
* .build());
- * SecretKey key = keyGenerator.generateKey();
+ * // Key imported, obtain a reference to it.
+ * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
+ * // The original key can now be thrown away.
+ * }</pre>
+ *
+ * <p><h3>Example: Asymmetric Key Pair</h3>
+ * The following example illustrates how to import an EC key pair into the Android KeyStore under
+ * alias {@code key2} authorized to be used only for signing with SHA-256 digest and only if
+ * the user has been authenticated within the last ten minutes. Both the private and the public key
+ * must export their key material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509}
+ * format respectively.
+ * <pre> {@code
+ * PrivateKey privateKey = ...; // EC private key
+ * Certificate[] certChain = ...; // Certificate chain with the first certificate
+ * // containing the corresponding EC public key.
*
- * // The key can also be obtained from the Android KeyStore any time as follows:
* KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
* keyStore.load(null);
- * SecretKey key = (SecretKey) keyStore.getKey("key1", null);
+ * keyStore.setEntry(
+ * "key2",
+ * new KeyStore.PrivateKeyEntry(privateKey, certChain),
+ * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ * .setDigests(KeyProperties.DIGEST_SHA256)
+ * // Only permit this key to be used if the user
+ * // authenticated within the last ten minutes.
+ * .setUserAuthenticationRequired(true)
+ * .setUserAuthenticationValidityDurationSeconds(10 * 60)
+ * .build());
+ * // Key pair imported, obtain a reference to it.
+ * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
+ * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
+ * // The original private key can now be thrown away.
* }</pre>
*/
-public class KeyGeneratorSpec implements AlgorithmParameterSpec {
-
- private final Context mContext;
- private final String mKeystoreAlias;
+public final class KeyProtection implements ProtectionParameter {
private final int mFlags;
- private final int mKeySize;
private final Date mKeyValidityStart;
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
+ private final @KeyProperties.PurposeEnum int mPurposes;
+ private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
- private KeyGeneratorSpec(
- Context context,
- String keyStoreAlias,
+ private KeyProtection(
int flags,
- int keySize,
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
- @KeyStoreKeyProperties.PurposeEnum int purposes,
- @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
- @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
+ @KeyProperties.PurposeEnum int purposes,
+ @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
- if (context == null) {
- throw new IllegalArgumentException("context == null");
- } else if (TextUtils.isEmpty(keyStoreAlias)) {
- throw new IllegalArgumentException("keyStoreAlias must not be empty");
- } else if ((userAuthenticationValidityDurationSeconds < 0)
+ if ((userAuthenticationValidityDurationSeconds < 0)
&& (userAuthenticationValidityDurationSeconds != -1)) {
throw new IllegalArgumentException(
"userAuthenticationValidityDurationSeconds must not be negative");
}
- mContext = context;
- mKeystoreAlias = keyStoreAlias;
mFlags = flags;
- mKeySize = keySize;
mKeyValidityStart = keyValidityStart;
mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
mPurposes = purposes;
mEncryptionPaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
+ mSignaturePaddings =
+ ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
+ mDigests = ArrayUtils.cloneIfNotEmpty(digests);
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
@@ -133,21 +152,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Gets the Android context used for operations with this instance.
- */
- public Context getContext() {
- return mContext;
- }
-
- /**
- * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with
- * the {@code AndroidKeyStore}.
- */
- public String getKeystoreAlias() {
- return mKeystoreAlias;
- }
-
- /**
* @hide
*/
public int getFlags() {
@@ -155,10 +159,12 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Returns the requested key size or {@code -1} if default size should be used.
+ * Returns {@code true} if the {@link java.security.KeyStore} entry must be encrypted at rest.
+ * This will protect the entry with the secure lock screen credential (e.g., password, PIN, or
+ * pattern).
*/
- public int getKeySize() {
- return mKeySize;
+ public boolean isEncryptionAtRestRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
}
/**
@@ -172,7 +178,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Gets the time instant after which the key is no longer valid for decryption and verification.
+ * Gets the time instant after which the key is no long valid for decryption and verification.
*
* @return instant or {@code null} if not restricted.
*/
@@ -182,7 +188,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Gets the time instant after which the key is no longer valid for encryption and signing.
+ * Gets the time instant after which the key is no long valid for encryption and signing.
*
* @return instant or {@code null} if not restricted.
*/
@@ -195,33 +201,73 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
* Gets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
* Attempts to use the key for any other purpose will be rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
+ * <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
- public @KeyStoreKeyProperties.PurposeEnum int getPurposes() {
+ public @KeyProperties.PurposeEnum int getPurposes() {
return mPurposes;
}
/**
- * Gets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code NoPadding}) with
- * which the key can be used when encrypting/decrypting. Attempts to use the key with any
- * other padding scheme will be rejected.
+ * Gets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code PKCS1Padding},
+ * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to use
+ * the key with any other padding scheme will be rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
+ public @KeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
+ * Gets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
+ * can be used when signing/verifying. Attempts to use the key with any other padding scheme
+ * will be rejected.
+ *
+ * <p>See {@link KeyProperties}.{@code SIGNATURE_PADDING} constants.
+ */
+ @NonNull
+ public @KeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
+ return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
+ }
+
+ /**
+ * Gets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the key
+ * can be used.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isDigestsSpecified()
+ */
+ @NonNull
+ public @KeyProperties.DigestEnum String[] getDigests() {
+ if (mDigests == null) {
+ throw new IllegalStateException("Digests not specified");
+ }
+ return ArrayUtils.cloneIfNotEmpty(mDigests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digest algorithms with which the key can be used has been
+ * specified.
+ *
+ * @see #getDigests()
+ */
+ public boolean isDigestsSpecified() {
+ return mDigests != null;
+ }
+
+ /**
* Gets the set of block modes (e.g., {@code CBC}, {@code CTR}) with which the key can be used
* when encrypting/decrypting. Attempts to use the key with any other block modes will be
* rejected.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
+ * <p>See {@link KeyProperties}.{@code BLOCK_MODE} constants.
*/
@NonNull
- public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
+ public @KeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -261,82 +307,58 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Returns {@code true} if the key must be encrypted at rest. This will protect the key with the
- * secure lock screen credential (e.g., password, PIN, or pattern).
+ * Builder of {@link KeyProtection} instances.
*/
- public boolean isEncryptionRequired() {
- return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
- }
+ public final static class Builder {
+ private @KeyProperties.PurposeEnum int mPurposes;
- public static class Builder {
- private final Context mContext;
- private String mKeystoreAlias;
private int mFlags;
- private int mKeySize = -1;
private Date mKeyValidityStart;
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
- private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
+ private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private @KeyProperties.DigestEnum String[] mDigests;
+ private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
/**
- * Creates a new instance of the {@code Builder} with the given {@code context}. The
- * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
- * initialize the Android KeyStore facility.
- */
- public Builder(@NonNull Context context) {
- if (context == null) {
- throw new NullPointerException("context == null");
- }
- mContext = context;
- }
-
- /**
- * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore}
- * instance using the {@code AndroidKeyStore} provider.
+ * Creates a new instance of the {@code Builder}.
*
- * <p>The alias must be provided. There is no default.
- */
- @NonNull
- public Builder setAlias(@NonNull String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
- mKeystoreAlias = alias;
- return this;
- }
-
- /**
- * Sets the size (in bits) of the key to be generated.
+ * @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
+ * used. Attempts to use the key for any other purpose will be rejected.
*
- * <p>By default, the key size will be determines based on the key algorithm. For example,
- * for {@code HmacSHA256}, the key size will default to {@code 256}.
+ * <p><b>NOTE: The {@code purposes} parameter has currently no effect on asymmetric
+ * key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
- @NonNull
- public Builder setKeySize(int keySize) {
- mKeySize = keySize;
- return this;
+ public Builder(@KeyProperties.PurposeEnum int purposes) {
+ mPurposes = purposes;
}
/**
- * Indicates that this key must be encrypted at rest. This will protect the key with the
- * secure lock screen credential (e.g., password, PIN, or pattern).
+ * Sets whether this {@link java.security.KeyStore} entry must be encrypted at rest.
+ * Encryption at rest will protect the entry with the secure lock screen credential (e.g.,
+ * password, PIN, or pattern).
*
- * <p>Note that this feature requires that the secure lock screen (e.g., password, PIN,
- * pattern) is set up, otherwise key generation will fail. Moreover, this key will be
- * deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device
- * Administrator). Finally, this key cannot be used until the user unlocks the secure lock
- * screen after boot.
+ * <p>Note that enabling this feature requires that the secure lock screen (e.g., password,
+ * PIN, pattern) is set up, otherwise setting the {@code KeyStore} entry will fail.
+ * Moreover, this entry will be deleted when the secure lock screen is disabled or reset
+ * (e.g., by the user or a Device Administrator). Finally, this entry cannot be used until
+ * the user unlocks the secure lock screen after boot.
*
* @see KeyguardManager#isDeviceSecure()
*/
@NonNull
- public Builder setEncryptionRequired() {
- mFlags |= KeyStore.FLAG_ENCRYPTED;
+ public Builder setEncryptionAtRestRequired(boolean required) {
+ if (required) {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ } else {
+ mFlags &= ~KeyStore.FLAG_ENCRYPTED;
+ }
return this;
}
@@ -345,6 +367,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @see #setKeyValidityEnd(Date)
*/
@NonNull
@@ -358,6 +382,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
@@ -374,6 +400,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @see #setKeyValidityForConsumptionEnd(Date)
*/
@NonNull
@@ -388,6 +416,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @see #setKeyValidityForOriginationEnd(Date)
*/
@NonNull
@@ -397,46 +427,73 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Sets the set of purposes (e.g., encrypt, decrypt, sign) for which the key can be used.
- * Attempts to use the key for any other purpose will be rejected.
+ * Sets the set of padding schemes (e.g., {@code OAEPPadding}, {@code PKCS7Padding},
+ * {@code NoPadding}) with which the key can be used when encrypting/decrypting. Attempts to
+ * use the key with any other padding scheme will be rejected.
+ *
+ * <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ */
+ @NonNull
+ public Builder setEncryptionPaddings(
+ @KeyProperties.EncryptionPaddingEnum String... paddings) {
+ mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
+ return this;
+ }
+
+ /**
+ * Sets the set of padding schemes (e.g., {@code PSS}, {@code PKCS#1}) with which the key
+ * can be used when signing/verifying. Attempts to use the key with any other padding scheme
+ * will be rejected.
*
- * <p>This must be specified for all keys. There is no default.
+ * <p>This must be specified for RSA keys which are used for signing/verification.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code PURPOSE} flags.
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code SIGNATURE_PADDING} constants.
*/
@NonNull
- public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
- mPurposes = purposes;
+ public Builder setSignaturePaddings(
+ @KeyProperties.SignaturePaddingEnum String... paddings) {
+ mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
/**
- * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code NoPadding}) with
- * which the key can be used when encrypting/decrypting. Attempts to use the key with any
- * other padding scheme will be rejected.
+ * Sets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the
+ * key can be used when signing/verifying or generating MACs. Attempts to use the key with
+ * any other digest algorithm will be rejected.
*
- * <p>This must be specified for keys which are used for encryption/decryption.
+ * <p>For HMAC keys, the default is the digest algorithm specified in
+ * {@link Key#getAlgorithm()}. For asymmetric signing keys the set of digest algorithms
+ * must be specified.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code ENCRYPTION_PADDING} constants.
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- public Builder setEncryptionPaddings(
- @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
- mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
+ public Builder setDigests(@KeyProperties.DigestEnum String... digests) {
+ mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
/**
- * Sets the set of block modes (e.g., {@code CBC}, {@code CTR}) with which the key can be
- * used when encrypting/decrypting. Attempts to use the key with any other block modes will
- * be rejected.
+ * Sets the set of block modes (e.g., {@code CBC}, {@code CTR}, {@code ECB}) with which the
+ * key can be used when encrypting/decrypting. Attempts to use the key with any other block
+ * modes will be rejected.
*
* <p>This must be specified for encryption/decryption keys.
*
- * <p>See {@link KeyStoreKeyProperties}.{@code BLOCK_MODE} constants.
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
+ * <p>See {@link KeyProperties}.{@code BLOCK_MODE} constants.
*/
@NonNull
- public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
+ public Builder setBlockModes(@KeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -454,11 +511,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>When {@code IND-CPA} is required:
* <ul>
- * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited;
- * </li>
- * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM},
- * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are
- * used.</li>
+ * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using
+ * {@code ECB} mode or RSA encryption without padding, are prohibited;</li>
+ * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC},
+ * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when
+ * encrypting, to ensure that only random IVs are used.</li>
*
* <p>Before disabling this requirement, consider the following approaches instead:
* <ul>
@@ -471,7 +528,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
* or a device identifier), consider changing your design to use a random IV which will then
* be provided in addition to the ciphertext to the entities which need to decrypt the
* ciphertext.</li>
+ * <li>If you are using RSA encryption without padding, consider switching to padding
+ * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
* </ul>
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
*/
@NonNull
public Builder setRandomizedEncryptionRequired(boolean required) {
@@ -492,6 +553,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
* <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
* information</a>.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @see #setUserAuthenticationValidityDurationSeconds(int)
*/
@NonNull
@@ -506,6 +569,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the user needs to authenticate for every use of the key.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.</b>
+ *
* @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
@@ -519,21 +584,21 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Builds a new instance instance of {@code KeyGeneratorSpec}.
+ * Builds an instance of {@link KeyProtection}.
*
- * @throws IllegalArgumentException if a required field is missing or violates a constraint.
+ * @throws IllegalArgumentException if a required field is missing
*/
@NonNull
- public KeyGeneratorSpec build() {
- return new KeyGeneratorSpec(mContext,
- mKeystoreAlias,
+ public KeyProtection build() {
+ return new KeyProtection(
mFlags,
- mKeySize,
mKeyValidityStart,
mKeyValidityForOriginationEnd,
mKeyValidityForConsumptionEnd,
mPurposes,
mEncryptionPaddings,
+ mSignaturePaddings,
+ mDigests,
mBlockModes,
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/keystore/UserNotAuthenticatedException.java
index 2954fa7..21f861c 100644
--- a/keystore/java/android/security/UserNotAuthenticatedException.java
+++ b/keystore/java/android/security/keystore/UserNotAuthenticatedException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security;
+package android.security.keystore;
import java.security.InvalidKeyException;
diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
index 681a9ff..bc8dd13 100644
--- a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
@@ -24,11 +24,6 @@ import java.util.Date;
import javax.security.auth.x500.X500Principal;
public class KeyPairGeneratorSpecTest extends AndroidTestCase {
- private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
- private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
- private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1980
- private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
-
private static final String TEST_ALIAS_1 = "test1";
private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
@@ -110,37 +105,46 @@ public class KeyPairGeneratorSpecTest extends AndroidTestCase {
}
}
- public void testConstructor_NullSubjectDN_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
- getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS, 0);
- assertEquals(DEFAULT_CERT_SUBJECT, spec.getSubjectDN());
+ public void testConstructor_NullSubjectDN_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS, 0);
+ fail("Should throw IllegalArgumentException when subjectDN is null");
+ } catch (IllegalArgumentException success) {
+ }
}
- public void testConstructor_NullSerial_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
- getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
- NOW_PLUS_10_YEARS, 0);
- assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getSerialNumber());
+ public void testConstructor_NullSerial_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
+ NOW_PLUS_10_YEARS, 0);
+ fail("Should throw IllegalArgumentException when startDate is null");
+ } catch (IllegalArgumentException success) {
+ }
}
- public void testConstructor_NullStartDate_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
- getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, null,
- NOW_PLUS_10_YEARS, 0);
- assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getStartDate());
+ public void testConstructor_NullStartDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+ null, NOW_PLUS_10_YEARS, 0);
+ fail("Should throw IllegalArgumentException when startDate is null");
+ } catch (IllegalArgumentException success) {
+ }
}
- public void testConstructor_NullEndDate_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
- getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW, null, 0);
- assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getEndDate());
+ public void testConstructor_NullEndDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+ NOW, null, 0);
+ fail("Should throw IllegalArgumentException when keystoreAlias is null");
+ } catch (IllegalArgumentException success) {
+ }
}
public void testConstructor_EndBeforeStart_Failure() throws Exception {
try {
- new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1,
- SERIAL_1, NOW_PLUS_10_YEARS, NOW, 0);
+ new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+ NOW_PLUS_10_YEARS, NOW, 0);
fail("Should throw IllegalArgumentException when end is before start");
} catch (IllegalArgumentException success) {
}
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index d138c24..7421120 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -21,7 +21,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.ServiceManager;
-import android.security.KeyStore;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;