summaryrefslogtreecommitdiffstats
path: root/keystore/java/android/security
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2015-03-31 23:32:40 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-03-31 23:32:41 +0000
commit19e79e12d82fa473f2f7beec337de11a0c3b3a03 (patch)
treeb343591046b85799cb422221c01752e9c10b855b /keystore/java/android/security
parentd720dedb03dfc937361e7e722171f8a1b9d74cf1 (diff)
parentacc835f3857c1a1bf2618fabb7307794aba5d76a (diff)
downloadframeworks_base-19e79e12d82fa473f2f7beec337de11a0c3b3a03.zip
frameworks_base-19e79e12d82fa473f2f7beec337de11a0c3b3a03.tar.gz
frameworks_base-19e79e12d82fa473f2f7beec337de11a0c3b3a03.tar.bz2
Merge "Add SecretKeyFactory backed by AndroidKeyStore."
Diffstat (limited to 'keystore/java/android/security')
-rw-r--r--keystore/java/android/security/AndroidKeyStoreProvider.java4
-rw-r--r--keystore/java/android/security/KeyGeneratorSpec.java2
-rw-r--r--keystore/java/android/security/KeyStoreKeyCharacteristics.java52
-rw-r--r--keystore/java/android/security/KeyStoreKeySpec.java210
-rw-r--r--keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java140
-rw-r--r--keystore/java/android/security/KeymasterUtils.java38
6 files changed, 445 insertions, 1 deletions
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 6cf9b7a..39f9d9c 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -40,6 +40,10 @@ public class AndroidKeyStoreProvider extends Provider {
put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
+ // java.security.SecretKeyFactory
+ put("SecretKeyFactory.AES", KeyStoreSecretKeyFactorySpi.class.getName());
+ put("SecretKeyFactory.HmacSHA256", KeyStoreSecretKeyFactorySpi.class.getName());
+
// javax.crypto.Mac
putMacImpl("HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 6274b70..61573f2 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -146,7 +146,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Gets the set of purposes for which the key can be used to the provided set of purposes.
+ * Gets the set of purposes for which the key can be used.
*
* @return set of purposes or {@code null} if the key can be used for any purpose.
*/
diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
new file mode 100644
index 0000000..c06dc3b
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
@@ -0,0 +1,52 @@
+package android.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Characteristics of {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyCharacteristics {
+ private KeyStoreKeyCharacteristics() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED})
+ public @interface OriginEnum {}
+
+ /**
+ * Origin of the key.
+ */
+ public static abstract class Origin {
+ private Origin() {}
+
+ /** Key was generated inside a TEE. */
+ public static final int GENERATED_INSIDE_TEE = 1;
+
+ /** Key was generated outside of a TEE. */
+ public static final int GENERATED_OUTSIDE_OF_TEE = 2;
+
+ /** Key was imported. */
+ public static final int IMPORTED = 0;
+
+ /**
+ * @hide
+ */
+ public static @OriginEnum int fromKeymaster(int origin) {
+ switch (origin) {
+ case KeymasterDefs.KM_ORIGIN_HARDWARE:
+ return GENERATED_INSIDE_TEE;
+ case KeymasterDefs.KM_ORIGIN_SOFTWARE:
+ return GENERATED_OUTSIDE_OF_TEE;
+ case KeymasterDefs.KM_ORIGIN_IMPORTED:
+ return IMPORTED;
+ default:
+ throw new IllegalArgumentException("Unknown origin: " + origin);
+ }
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
new file mode 100644
index 0000000..19fba68
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -0,0 +1,210 @@
+package android.security;
+
+import java.security.spec.KeySpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
+ * KeyStore</a>.
+ *
+ * @hide
+ */
+public class KeyStoreKeySpec implements KeySpec {
+ private final String mKeystoreAlias;
+ private final int mKeySize;
+ private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin;
+ private final Date mKeyValidityStart;
+ private final Date mKeyValidityForOriginationEnd;
+ private final Date mKeyValidityForConsumptionEnd;
+ private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+ private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+ private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+ private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private final Integer mMinSecondsBetweenOperations;
+ private final Integer mMaxUsesPerBoot;
+ private final Set<Integer> mUserAuthenticators;
+ private final Set<Integer> mTeeBackedUserAuthenticators;
+ private final Integer mUserAuthenticationValidityDurationSeconds;
+
+
+ /**
+ * @hide
+ */
+ KeyStoreKeySpec(String keystoreKeyAlias,
+ @KeyStoreKeyCharacteristics.OriginEnum int origin,
+ int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
+ Date keyValidityForConsumptionEnd,
+ @KeyStoreKeyConstraints.PurposeEnum int purposes,
+ @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+ @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+ @KeyStoreKeyConstraints.DigestEnum Integer digest,
+ @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+ Integer minSecondsBetweenOperations,
+ Integer maxUsesPerBoot,
+ Set<Integer> userAuthenticators,
+ Set<Integer> teeBackedUserAuthenticators,
+ Integer userAuthenticationValidityDurationSeconds) {
+ mKeystoreAlias = keystoreKeyAlias;
+ mOrigin = origin;
+ mKeySize = keySize;
+ mKeyValidityStart = keyValidityStart;
+ mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+ mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+ mPurposes = purposes;
+ mAlgorithm = algorithm;
+ mPadding = padding;
+ mDigest = digest;
+ mBlockMode = blockMode;
+ mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+ mMaxUsesPerBoot = maxUsesPerBoot;
+ mUserAuthenticators = (userAuthenticators != null)
+ ? new HashSet<Integer>(userAuthenticators)
+ : Collections.<Integer>emptySet();
+ mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
+ ? new HashSet<Integer>(teeBackedUserAuthenticators)
+ : Collections.<Integer>emptySet();
+ mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+ }
+
+ /**
+ * Gets the entry alias under which the key is stored in the {@code AndroidKeyStore}.
+ */
+ public String getKeystoreAlias() {
+ return mKeystoreAlias;
+ }
+
+ /**
+ * Gets the origin of the key.
+ */
+ public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() {
+ return mOrigin;
+ }
+
+ /**
+ * Gets the key's size in bits.
+ */
+ public int getKeySize() {
+ return mKeySize;
+ }
+
+ /**
+ * Gets the time instant before which the key is not yet valid.
+ *
+ * @return instant or {@code null} if not restricted.
+ */
+ 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.
+ */
+ 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.
+ */
+ public Date getKeyValidityForOriginationEnd() {
+ return mKeyValidityForOriginationEnd;
+ }
+
+ /**
+ * Gets the set of purposes for which the key can be used.
+ */
+ public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
+ return mPurposes;
+ }
+
+ /**
+ * Gets the algorithm of the key.
+ */
+ public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() {
+ return mAlgorithm;
+ }
+
+ /**
+ * Gets the only block mode with which the key can be used.
+ *
+ * @return block mode or {@code null} if the block mode is not restricted.
+ */
+ public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+ return mBlockMode;
+ }
+
+ /**
+ * Gets the only padding mode with which the key can be used.
+ *
+ * @return padding mode or {@code null} if the padding mode is not restricted.
+ */
+ public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+ return mPadding;
+ }
+
+ /**
+ * Gets the only digest algorithm with which the key can be used.
+ *
+ * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
+ */
+ public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+ return mDigest;
+ }
+
+ /**
+ * Gets the minimum number of seconds that must expire since the most recent use of the key
+ * before it can be used again.
+ *
+ * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+ * can be used.
+ */
+ public Integer getMinSecondsBetweenOperations() {
+ return mMinSecondsBetweenOperations;
+ }
+
+ /**
+ * Gets the number of times the key can be used without rebooting the device.
+ *
+ * @return maximum number of times or {@code null} if there is no restriction.
+ */
+ public Integer getMaxUsesPerBoot() {
+ return mMaxUsesPerBoot;
+ }
+
+ /**
+ * Gets the user authenticators which protect access to the key. The key can only be used iff
+ * the user has authenticated to at least one of these user authenticators.
+ *
+ * @return user authenticators or empty set if the key can be used without user authentication.
+ */
+ public Set<Integer> getUserAuthenticators() {
+ return new HashSet<Integer>(mUserAuthenticators);
+ }
+
+ /**
+ * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
+ * the user authentications returned by {@link #getUserAuthenticators()}.
+ */
+ public Set<Integer> getTeeBackedUserAuthenticators() {
+ return new HashSet<Integer>(mTeeBackedUserAuthenticators);
+ }
+
+ /**
+ * Gets the duration of time (seconds) for which the key can be used after the user
+ * successfully authenticates to one of the associated user authenticators.
+ *
+ * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * is required for every use of the key.
+ */
+ public Integer getUserAuthenticationValidityDurationSeconds() {
+ return mUserAuthenticationValidityDurationSeconds;
+ }
+}
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
new file mode 100644
index 0000000..70e643d
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -0,0 +1,140 @@
+package android.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link SecretKeyFactorySpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+
+ @Override
+ protected KeySpec engineGetKeySpec(SecretKey key,
+ @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
+ if (keySpecClass == null) {
+ throw new InvalidKeySpecException("keySpecClass == null");
+ }
+ if (!(key instanceof KeyStoreSecretKey)) {
+ throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
+ ((key != null) ? key.getClass().getName() : "null"));
+ }
+ if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
+ throw new InvalidKeySpecException(
+ "Key material export of Android KeyStore keys is not supported");
+ }
+ if (!KeyStoreKeySpec.class.equals(keySpecClass)) {
+ throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+ }
+ String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+ String entryAlias;
+ if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+ entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+ } else {
+ throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
+ }
+
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode =
+ mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new InvalidKeySpecException("Failed to obtain information about key."
+ + " Keystore error: " + errorCode);
+ }
+
+ @KeyStoreKeyCharacteristics.OriginEnum Integer origin;
+ int keySize;
+ @KeyStoreKeyConstraints.PurposeEnum int purposes;
+ @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
+ @KeyStoreKeyConstraints.PaddingEnum Integer padding;
+ @KeyStoreKeyConstraints.DigestEnum Integer digest;
+ @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
+ try {
+ origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
+ if (origin == null) {
+ throw new InvalidKeySpecException("Key origin not available");
+ }
+ origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin);
+ Integer keySizeInteger =
+ KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE);
+ if (keySizeInteger == null) {
+ throw new InvalidKeySpecException("Key size not available");
+ }
+ keySize = keySizeInteger;
+ purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster(
+ KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE));
+ Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM);
+ if (alg == null) {
+ throw new InvalidKeySpecException("Key algorithm not available");
+ }
+ algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
+ padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
+ if (padding != null) {
+ padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
+ }
+ digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
+ if (digest != null) {
+ digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
+ }
+ blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
+ if (blockMode != null) {
+ blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidKeySpecException("Unsupported key characteristic", e);
+ }
+
+ // TODO: Read user authentication IDs once the Keymaster API has stabilized
+ Set<Integer> userAuthenticators = Collections.emptySet();
+ Set<Integer> teeBackedUserAuthenticators = Collections.emptySet();
+// Set<Integer> userAuthenticators = new HashSet<Integer>(
+// getInts(keyCharacteristics, KeymasterDefs.KM_TAG_USER_AUTH_ID));
+// Set<Integer> teeBackedUserAuthenticators = new HashSet<Integer>(
+// keyCharacteristics.hwEnforced.getInts(KeymasterDefs.KM_TAG_USER_AUTH_ID));
+
+ return new KeyStoreKeySpec(entryAlias,
+ origin,
+ keySize,
+ KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME),
+ KeymasterUtils.getDate(keyCharacteristics,
+ KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME),
+ KeymasterUtils.getDate(keyCharacteristics,
+ KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME),
+ purposes,
+ algorithm,
+ padding,
+ digest,
+ blockMode,
+ KeymasterUtils.getInt(keyCharacteristics,
+ KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
+ KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
+ userAuthenticators,
+ teeBackedUserAuthenticators,
+ KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT));
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
+ throw new UnsupportedOperationException(
+ "Key import into Android KeyStore is not supported");
+ }
+
+ @Override
+ protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
+ throw new UnsupportedOperationException(
+ "Key import into Android KeyStore is not supported");
+ }
+}
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index 4f17586..e9ad69f 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -1,7 +1,12 @@
package android.security;
+import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
/**
* @hide
*/
@@ -20,4 +25,37 @@ public abstract class KeymasterUtils {
KeymasterDefs.getErrorMessage(keymasterErrorCode));
}
}
+
+ public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) {
+ if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+ return keyCharacteristics.hwEnforced.getInt(tag, -1);
+ } else if (keyCharacteristics.swEnforced.containsTag(tag)) {
+ return keyCharacteristics.swEnforced.getInt(tag, -1);
+ } else {
+ return null;
+ }
+ }
+
+ public static List<Integer> getInts(KeyCharacteristics keyCharacteristics, int tag) {
+ List<Integer> result = new ArrayList<Integer>();
+ result.addAll(keyCharacteristics.hwEnforced.getInts(tag));
+ result.addAll(keyCharacteristics.swEnforced.getInts(tag));
+ return result;
+ }
+
+ public static Date getDate(KeyCharacteristics keyCharacteristics, int tag) {
+ Date result = keyCharacteristics.hwEnforced.getDate(tag, null);
+ if (result == null) {
+ result = keyCharacteristics.swEnforced.getDate(tag, null);
+ }
+ return result;
+ }
+
+ public static boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
+ if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+ return keyCharacteristics.hwEnforced.getBoolean(tag, false);
+ } else {
+ return keyCharacteristics.swEnforced.getBoolean(tag, false);
+ }
+ }
}