Loading keystore/java/android/security/AndroidKeyStoreProvider.java +4 −0 Original line number Diff line number Diff line Loading @@ -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()); Loading keystore/java/android/security/KeyGeneratorSpec.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading keystore/java/android/security/KeyStoreKeyCharacteristics.java 0 → 100644 +52 −0 Original line number Diff line number Diff line 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); } } } } keystore/java/android/security/KeyStoreKeySpec.java 0 → 100644 +210 −0 Original line number Diff line number Diff line 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; } } keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java 0 → 100644 +140 −0 Original line number Diff line number Diff line 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"); } } Loading
keystore/java/android/security/AndroidKeyStoreProvider.java +4 −0 Original line number Diff line number Diff line Loading @@ -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()); Loading
keystore/java/android/security/KeyGeneratorSpec.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading
keystore/java/android/security/KeyStoreKeyCharacteristics.java 0 → 100644 +52 −0 Original line number Diff line number Diff line 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); } } } }
keystore/java/android/security/KeyStoreKeySpec.java 0 → 100644 +210 −0 Original line number Diff line number Diff line 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; } }
keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java 0 → 100644 +140 −0 Original line number Diff line number Diff line 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"); } }