AndroidKeyStoreSecretKeyFactorySpi.java revision ae6cb7aad56bb006769cd8a69b92af7236644fc1
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 String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias(); 63 String entryAlias; 64 if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 65 entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 66 } else { 67 throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); 68 } 69 70 return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore); 71 } 72 73 static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) { 74 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 75 int errorCode = 76 keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics); 77 if (errorCode != KeyStore.NO_ERROR) { 78 throw new ProviderException("Failed to obtain information about key." 79 + " Keystore error: " + errorCode); 80 } 81 82 boolean insideSecureHardware; 83 @KeyProperties.OriginEnum int origin; 84 int keySize; 85 @KeyProperties.PurposeEnum int purposes; 86 String[] encryptionPaddings; 87 String[] signaturePaddings; 88 @KeyProperties.DigestEnum String[] digests; 89 @KeyProperties.BlockModeEnum String[] blockModes; 90 int keymasterSwEnforcedUserAuthenticators; 91 int keymasterHwEnforcedUserAuthenticators; 92 try { 93 if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 94 insideSecureHardware = true; 95 origin = KeyProperties.Origin.fromKeymaster( 96 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 97 } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 98 insideSecureHardware = false; 99 origin = KeyProperties.Origin.fromKeymaster( 100 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 101 } else { 102 throw new ProviderException("Key origin not available"); 103 } 104 long keySizeUnsigned = 105 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); 106 if (keySizeUnsigned == -1) { 107 throw new ProviderException("Key size not available"); 108 } else if (keySizeUnsigned > Integer.MAX_VALUE) { 109 throw new ProviderException("Key too large: " + keySizeUnsigned + " bits"); 110 } 111 keySize = (int) keySizeUnsigned; 112 purposes = KeyProperties.Purpose.allFromKeymaster( 113 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE)); 114 115 List<String> encryptionPaddingsList = new ArrayList<String>(); 116 List<String> signaturePaddingsList = new ArrayList<String>(); 117 // Keymaster stores both types of paddings in the same array -- we split it into two. 118 for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) { 119 try { 120 @KeyProperties.EncryptionPaddingEnum String jcaPadding = 121 KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); 122 encryptionPaddingsList.add(jcaPadding); 123 } catch (IllegalArgumentException e) { 124 try { 125 @KeyProperties.SignaturePaddingEnum String padding = 126 KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); 127 signaturePaddingsList.add(padding); 128 } catch (IllegalArgumentException e2) { 129 throw new ProviderException( 130 "Unsupported encryption padding: " + keymasterPadding); 131 } 132 } 133 134 } 135 encryptionPaddings = 136 encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); 137 signaturePaddings = 138 signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); 139 140 digests = KeyProperties.Digest.allFromKeymaster( 141 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST)); 142 blockModes = KeyProperties.BlockMode.allFromKeymaster( 143 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE)); 144 keymasterSwEnforcedUserAuthenticators = 145 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 146 keymasterHwEnforcedUserAuthenticators = 147 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 148 } catch (IllegalArgumentException e) { 149 throw new ProviderException("Unsupported key characteristic", e); 150 } 151 152 Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); 153 Date keyValidityForOriginationEnd = 154 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); 155 Date keyValidityForConsumptionEnd = 156 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); 157 boolean userAuthenticationRequired = 158 !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); 159 long userAuthenticationValidityDurationSeconds = 160 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); 161 if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) { 162 throw new ProviderException("User authentication timeout validity too long: " 163 + userAuthenticationValidityDurationSeconds + " seconds"); 164 } 165 boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) 166 && (keymasterHwEnforcedUserAuthenticators != 0) 167 && (keymasterSwEnforcedUserAuthenticators == 0); 168 169 return new KeyInfo(entryAlias, 170 insideSecureHardware, 171 origin, 172 keySize, 173 keyValidityStart, 174 keyValidityForOriginationEnd, 175 keyValidityForConsumptionEnd, 176 purposes, 177 encryptionPaddings, 178 signaturePaddings, 179 digests, 180 blockModes, 181 userAuthenticationRequired, 182 (int) userAuthenticationValidityDurationSeconds, 183 userAuthenticationRequirementEnforcedBySecureHardware); 184 } 185 186 @Override 187 protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { 188 throw new UnsupportedOperationException( 189 "To generate secret key in Android KeyStore, use KeyGenerator initialized with " 190 + KeyGenParameterSpec.class.getName()); 191 } 192 193 @Override 194 protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { 195 throw new UnsupportedOperationException( 196 "To import a secret key into Android KeyStore, use KeyStore.setEntry with " 197 + KeyProtection.class.getName()); 198 } 199} 200