am 9c0f257f: am 19e79e12: Merge "Add SecretKeyFactory backed by AndroidKeyStore."

* commit '9c0f257f0080aba0743f34fb6202215fca295146':
  Add SecretKeyFactory backed by AndroidKeyStore.
This commit is contained in:
Alex Klyubin
2015-04-01 02:05:37 +00:00
committed by Android Git Automerger
6 changed files with 445 additions and 1 deletions

View File

@ -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());

View File

@ -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.
*/

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}
}