AndroidKeyStoreSecretKeyFactorySpi.java revision 512c132f49fc6e8e4fc119f4cf167d33b2393509
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.security.keystore; 18 19import android.security.Credentials; 20import android.security.KeyStore; 21import android.security.keymaster.KeyCharacteristics; 22import android.security.keymaster.KeymasterDefs; 23 24import java.security.InvalidKeyException; 25import java.security.ProviderException; 26import java.security.spec.InvalidKeySpecException; 27import java.security.spec.KeySpec; 28import java.util.ArrayList; 29import java.util.Date; 30import java.util.List; 31 32import javax.crypto.SecretKey; 33import javax.crypto.SecretKeyFactorySpi; 34import javax.crypto.spec.SecretKeySpec; 35 36/** 37 * {@link SecretKeyFactorySpi} backed by Android Keystore. 38 * 39 * @hide 40 */ 41public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 42 43 private final KeyStore mKeyStore = KeyStore.getInstance(); 44 45 @Override 46 protected KeySpec engineGetKeySpec(SecretKey key, 47 @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException { 48 if (keySpecClass == null) { 49 throw new InvalidKeySpecException("keySpecClass == null"); 50 } 51 if (!(key instanceof AndroidKeyStoreSecretKey)) { 52 throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " + 53 ((key != null) ? key.getClass().getName() : "null")); 54 } 55 if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) { 56 throw new InvalidKeySpecException( 57 "Key material export of Android KeyStore keys is not supported"); 58 } 59 if (!KeyInfo.class.equals(keySpecClass)) { 60 throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); 61 } 62 AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key; 63 String keyAliasInKeystore = keystoreKey.getAlias(); 64 String entryAlias; 65 if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 66 entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 67 } else { 68 throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); 69 } 70 71 return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid()); 72 } 73 74 static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore, 75 int keyUid) { 76 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 77 int errorCode = keyStore.getKeyCharacteristics( 78 keyAliasInKeystore, null, null, keyUid, keyCharacteristics); 79 if (errorCode != KeyStore.NO_ERROR) { 80 throw new ProviderException("Failed to obtain information about key." 81 + " Keystore error: " + errorCode); 82 } 83 84 boolean insideSecureHardware; 85 @KeyProperties.OriginEnum int origin; 86 int keySize; 87 @KeyProperties.PurposeEnum int purposes; 88 String[] encryptionPaddings; 89 String[] signaturePaddings; 90 @KeyProperties.DigestEnum String[] digests; 91 @KeyProperties.BlockModeEnum String[] blockModes; 92 int keymasterSwEnforcedUserAuthenticators; 93 int keymasterHwEnforcedUserAuthenticators; 94 try { 95 if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 96 insideSecureHardware = true; 97 origin = KeyProperties.Origin.fromKeymaster( 98 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 99 } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 100 insideSecureHardware = false; 101 origin = KeyProperties.Origin.fromKeymaster( 102 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 103 } else { 104 throw new ProviderException("Key origin not available"); 105 } 106 long keySizeUnsigned = 107 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); 108 if (keySizeUnsigned == -1) { 109 throw new ProviderException("Key size not available"); 110 } else if (keySizeUnsigned > Integer.MAX_VALUE) { 111 throw new ProviderException("Key too large: " + keySizeUnsigned + " bits"); 112 } 113 keySize = (int) keySizeUnsigned; 114 purposes = KeyProperties.Purpose.allFromKeymaster( 115 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE)); 116 117 List<String> encryptionPaddingsList = new ArrayList<String>(); 118 List<String> signaturePaddingsList = new ArrayList<String>(); 119 // Keymaster stores both types of paddings in the same array -- we split it into two. 120 for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) { 121 try { 122 @KeyProperties.EncryptionPaddingEnum String jcaPadding = 123 KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); 124 encryptionPaddingsList.add(jcaPadding); 125 } catch (IllegalArgumentException e) { 126 try { 127 @KeyProperties.SignaturePaddingEnum String padding = 128 KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); 129 signaturePaddingsList.add(padding); 130 } catch (IllegalArgumentException e2) { 131 throw new ProviderException( 132 "Unsupported encryption padding: " + keymasterPadding); 133 } 134 } 135 136 } 137 encryptionPaddings = 138 encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); 139 signaturePaddings = 140 signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); 141 142 digests = KeyProperties.Digest.allFromKeymaster( 143 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST)); 144 blockModes = KeyProperties.BlockMode.allFromKeymaster( 145 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE)); 146 keymasterSwEnforcedUserAuthenticators = 147 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 148 keymasterHwEnforcedUserAuthenticators = 149 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 150 } catch (IllegalArgumentException e) { 151 throw new ProviderException("Unsupported key characteristic", e); 152 } 153 154 Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); 155 Date keyValidityForOriginationEnd = 156 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); 157 Date keyValidityForConsumptionEnd = 158 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); 159 boolean userAuthenticationRequired = 160 !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); 161 long userAuthenticationValidityDurationSeconds = 162 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); 163 if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) { 164 throw new ProviderException("User authentication timeout validity too long: " 165 + userAuthenticationValidityDurationSeconds + " seconds"); 166 } 167 boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) 168 && (keymasterHwEnforcedUserAuthenticators != 0) 169 && (keymasterSwEnforcedUserAuthenticators == 0); 170 boolean userAuthenticationValidWhileOnBody = 171 keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); 172 173 return new KeyInfo(entryAlias, 174 insideSecureHardware, 175 origin, 176 keySize, 177 keyValidityStart, 178 keyValidityForOriginationEnd, 179 keyValidityForConsumptionEnd, 180 purposes, 181 encryptionPaddings, 182 signaturePaddings, 183 digests, 184 blockModes, 185 userAuthenticationRequired, 186 (int) userAuthenticationValidityDurationSeconds, 187 userAuthenticationRequirementEnforcedBySecureHardware, 188 userAuthenticationValidWhileOnBody); 189 } 190 191 @Override 192 protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { 193 throw new InvalidKeySpecException( 194 "To generate secret key in Android Keystore, use KeyGenerator initialized with " 195 + KeyGenParameterSpec.class.getName()); 196 } 197 198 @Override 199 protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { 200 if (key == null) { 201 throw new InvalidKeyException("key == null"); 202 } else if (!(key instanceof AndroidKeyStoreSecretKey)) { 203 throw new InvalidKeyException( 204 "To import a secret key into Android Keystore, use KeyStore.setEntry"); 205 } 206 207 return key; 208 } 209} 210