/* * Copyright (C) 2015 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.security.Credentials; import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterDefs; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.ProviderException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactorySpi; import javax.crypto.spec.SecretKeySpec; /** * {@link SecretKeyFactorySpi} backed by Android Keystore. * * @hide */ public class AndroidKeyStoreSecretKeyFactorySpi 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 AndroidKeyStoreSecretKey)) { 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 (!KeyInfo.class.equals(keySpecClass)) { throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); } AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key; String keyAliasInKeystore = keystoreKey.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); } return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid()); } static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore, int keyUid) { KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); int errorCode = keyStore.getKeyCharacteristics( keyAliasInKeystore, null, null, keyUid, keyCharacteristics); if (errorCode != KeyStore.NO_ERROR) { throw new ProviderException("Failed to obtain information about key." + " Keystore error: " + errorCode); } boolean insideSecureHardware; @KeyProperties.OriginEnum int origin; int keySize; @KeyProperties.PurposeEnum int purposes; String[] encryptionPaddings; String[] signaturePaddings; @KeyProperties.DigestEnum String[] digests; @KeyProperties.BlockModeEnum String[] blockModes; int keymasterSwEnforcedUserAuthenticators; int keymasterHwEnforcedUserAuthenticators; List keymasterSecureUserIds; try { if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { insideSecureHardware = true; origin = KeyProperties.Origin.fromKeymaster( keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { insideSecureHardware = false; origin = KeyProperties.Origin.fromKeymaster( keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else { throw new ProviderException("Key origin not available"); } long keySizeUnsigned = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); if (keySizeUnsigned == -1) { throw new ProviderException("Key size not available"); } else if (keySizeUnsigned > Integer.MAX_VALUE) { throw new ProviderException("Key too large: " + keySizeUnsigned + " bits"); } keySize = (int) keySizeUnsigned; purposes = KeyProperties.Purpose.allFromKeymaster( keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE)); List encryptionPaddingsList = new ArrayList(); List signaturePaddingsList = new ArrayList(); // Keymaster stores both types of paddings in the same array -- we split it into two. for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) { try { @KeyProperties.EncryptionPaddingEnum String jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); encryptionPaddingsList.add(jcaPadding); } catch (IllegalArgumentException e) { try { @KeyProperties.SignaturePaddingEnum String padding = KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); signaturePaddingsList.add(padding); } catch (IllegalArgumentException e2) { throw new ProviderException( "Unsupported encryption padding: " + keymasterPadding); } } } encryptionPaddings = encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); signaturePaddings = signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); digests = KeyProperties.Digest.allFromKeymaster( keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST)); blockModes = KeyProperties.BlockMode.allFromKeymaster( keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE)); keymasterSwEnforcedUserAuthenticators = keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); keymasterHwEnforcedUserAuthenticators = keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); keymasterSecureUserIds = keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID); } catch (IllegalArgumentException e) { throw new ProviderException("Unsupported key characteristic", e); } Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); Date keyValidityForOriginationEnd = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); Date keyValidityForConsumptionEnd = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); boolean userAuthenticationRequired = !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); long userAuthenticationValidityDurationSeconds = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) { throw new ProviderException("User authentication timeout validity too long: " + userAuthenticationValidityDurationSeconds + " seconds"); } boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) && (keymasterHwEnforcedUserAuthenticators != 0) && (keymasterSwEnforcedUserAuthenticators == 0); boolean userAuthenticationValidWhileOnBody = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); boolean invalidatedByBiometricEnrollment = false; if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT) { // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list. invalidatedByBiometricEnrollment = keymasterSecureUserIds != null && !keymasterSecureUserIds.isEmpty() && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId()); } return new KeyInfo(entryAlias, insideSecureHardware, origin, keySize, keyValidityStart, keyValidityForOriginationEnd, keyValidityForConsumptionEnd, purposes, encryptionPaddings, signaturePaddings, digests, blockModes, userAuthenticationRequired, (int) userAuthenticationValidityDurationSeconds, userAuthenticationRequirementEnforcedBySecureHardware, userAuthenticationValidWhileOnBody, invalidatedByBiometricEnrollment); } private static BigInteger getGateKeeperSecureUserId() throws ProviderException { try { return BigInteger.valueOf(GateKeeper.getSecureUserId()); } catch (IllegalStateException e) { throw new ProviderException("Failed to get GateKeeper secure user ID", e); } } @Override protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { throw new InvalidKeySpecException( "To generate secret key in Android Keystore, use KeyGenerator initialized with " + KeyGenParameterSpec.class.getName()); } @Override protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { if (key == null) { throw new InvalidKeyException("key == null"); } else if (!(key instanceof AndroidKeyStoreSecretKey)) { throw new InvalidKeyException( "To import a secret key into Android Keystore, use KeyStore.setEntry"); } return key; } }