AndroidKeyStoreKeyPairGeneratorSpi.java revision 4350babc028822e8905190d88a9f5b8c6ffce8ec
1/* 2 * Copyright (C) 2012 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.annotation.NonNull; 20import android.security.Credentials; 21import android.security.KeyPairGeneratorSpec; 22import android.security.KeyStore; 23import android.security.keymaster.ExportResult; 24import android.security.keymaster.KeymasterDefs; 25 26import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; 27import com.android.org.conscrypt.NativeConstants; 28import com.android.org.conscrypt.OpenSSLEngine; 29 30import java.security.InvalidAlgorithmParameterException; 31import java.security.InvalidKeyException; 32import java.security.KeyFactory; 33import java.security.KeyPair; 34import java.security.KeyPairGenerator; 35import java.security.KeyPairGeneratorSpi; 36import java.security.NoSuchAlgorithmException; 37import java.security.PrivateKey; 38import java.security.ProviderException; 39import java.security.PublicKey; 40import java.security.SecureRandom; 41import java.security.cert.CertificateEncodingException; 42import java.security.cert.X509Certificate; 43import java.security.spec.AlgorithmParameterSpec; 44import java.security.spec.InvalidKeySpecException; 45import java.security.spec.RSAKeyGenParameterSpec; 46import java.security.spec.X509EncodedKeySpec; 47import java.util.Locale; 48 49/** 50 * Provides a way to create instances of a KeyPair which will be placed in the 51 * Android keystore service usable only by the application that called it. This 52 * can be used in conjunction with 53 * {@link java.security.KeyStore#getInstance(String)} using the 54 * {@code "AndroidKeyStore"} type. 55 * <p> 56 * This class can not be directly instantiated and must instead be used via the 57 * {@link KeyPairGenerator#getInstance(String) 58 * KeyPairGenerator.getInstance("AndroidKeyStore")} API. 59 * 60 * @hide 61 */ 62public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi { 63 64 public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi { 65 public RSA() { 66 super(KeyProperties.KEY_ALGORITHM_RSA); 67 } 68 } 69 70 public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi { 71 public EC() { 72 super(KeyProperties.KEY_ALGORITHM_EC); 73 } 74 } 75 76 /* 77 * These must be kept in sync with system/security/keystore/defaults.h 78 */ 79 80 /* EC */ 81 private static final int EC_DEFAULT_KEY_SIZE = 256; 82 private static final int EC_MIN_KEY_SIZE = 192; 83 private static final int EC_MAX_KEY_SIZE = 521; 84 85 /* RSA */ 86 private static final int RSA_DEFAULT_KEY_SIZE = 2048; 87 private static final int RSA_MIN_KEY_SIZE = 512; 88 private static final int RSA_MAX_KEY_SIZE = 8192; 89 90 private final String mAlgorithm; 91 92 private KeyStore mKeyStore; 93 94 private KeyGenParameterSpec mSpec; 95 private boolean mEncryptionAtRestRequired; 96 private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm; 97 private int mKeyType; 98 private int mKeySize; 99 100 protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) { 101 mAlgorithm = algorithm; 102 } 103 104 @KeyProperties.KeyAlgorithmEnum String getAlgorithm() { 105 return mAlgorithm; 106 } 107 108 /** 109 * Generate a KeyPair which is backed by the Android keystore service. You 110 * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)} 111 * with an {@link KeyPairGeneratorSpec} as the {@code params} 112 * argument before calling this otherwise an {@code IllegalStateException} 113 * will be thrown. 114 * <p> 115 * This will create an entry in the Android keystore service with a 116 * self-signed certificate using the {@code params} specified in the 117 * {@code initialize(params)} call. 118 * 119 * @throws IllegalStateException when called before calling 120 * {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)} 121 * @see java.security.KeyPairGeneratorSpi#generateKeyPair() 122 */ 123 @Override 124 public KeyPair generateKeyPair() { 125 if (mKeyStore == null || mSpec == null) { 126 throw new IllegalStateException("Not initialized"); 127 } 128 129 final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0; 130 if (((flags & KeyStore.FLAG_ENCRYPTED) != 0) 131 && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { 132 throw new IllegalStateException( 133 "Encryption at rest using secure lock screen credential requested for key pair" 134 + ", but the user has not yet entered the credential"); 135 } 136 137 final String alias = mSpec.getKeystoreAlias(); 138 139 byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec()); 140 141 final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 142 143 boolean success = false; 144 try { 145 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 146 if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize, 147 flags, args)) { 148 throw new IllegalStateException("could not generate key in keystore"); 149 } 150 151 final PrivateKey privKey; 152 final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); 153 try { 154 privKey = engine.getPrivateKeyById(privateKeyAlias); 155 } catch (InvalidKeyException e) { 156 throw new RuntimeException("Can't get key", e); 157 } 158 159 ExportResult exportResult = 160 mKeyStore.exportKey( 161 privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null); 162 if (exportResult == null) { 163 throw new KeyStoreConnectException(); 164 } else if (exportResult.resultCode != KeyStore.NO_ERROR) { 165 throw new ProviderException( 166 "Failed to obtain public key in X.509 format", 167 KeyStore.getKeyStoreException(exportResult.resultCode)); 168 } 169 final byte[] pubKeyBytes = exportResult.exportData; 170 171 172 final PublicKey pubKey; 173 try { 174 final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm); 175 pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes)); 176 } catch (NoSuchAlgorithmException e) { 177 throw new IllegalStateException("Can't instantiate key generator", e); 178 } catch (InvalidKeySpecException e) { 179 throw new IllegalStateException("keystore returned invalid key encoding", e); 180 } 181 182 final X509Certificate cert; 183 try { 184 cert = generateCertificate(privKey, pubKey); 185 } catch (Exception e) { 186 throw new IllegalStateException("Can't generate certificate", e); 187 } 188 189 byte[] certBytes; 190 try { 191 certBytes = cert.getEncoded(); 192 } catch (CertificateEncodingException e) { 193 throw new IllegalStateException("Can't get encoding of certificate", e); 194 } 195 196 if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF, 197 flags)) { 198 throw new IllegalStateException("Can't store certificate in AndroidKeyStore"); 199 } 200 201 KeyPair result = new KeyPair(pubKey, privKey); 202 success = true; 203 return result; 204 } finally { 205 if (!success) { 206 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 207 } 208 } 209 } 210 211 @SuppressWarnings("deprecation") 212 private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey) 213 throws Exception { 214 final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 215 certGen.setPublicKey(publicKey); 216 certGen.setSerialNumber(mSpec.getCertificateSerialNumber()); 217 certGen.setSubjectDN(mSpec.getCertificateSubject()); 218 certGen.setIssuerDN(mSpec.getCertificateSubject()); 219 certGen.setNotBefore(mSpec.getCertificateNotBefore()); 220 certGen.setNotAfter(mSpec.getCertificateNotAfter()); 221 certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm)); 222 return certGen.generate(privateKey); 223 } 224 225 @NonNull 226 private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) { 227 String result = spec.getKeyType(); 228 if (result != null) { 229 return result; 230 } 231 return getAlgorithm(); 232 } 233 234 private static int getDefaultKeySize(int keyType) { 235 if (keyType == NativeConstants.EVP_PKEY_EC) { 236 return EC_DEFAULT_KEY_SIZE; 237 } else if (keyType == NativeConstants.EVP_PKEY_RSA) { 238 return RSA_DEFAULT_KEY_SIZE; 239 } 240 return -1; 241 } 242 243 private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize) 244 throws InvalidAlgorithmParameterException { 245 if (keyType == NativeConstants.EVP_PKEY_EC) { 246 if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) { 247 throw new InvalidAlgorithmParameterException("EC keys must be >= " 248 + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE); 249 } 250 } else if (keyType == NativeConstants.EVP_PKEY_RSA) { 251 if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) { 252 throw new InvalidAlgorithmParameterException("RSA keys must be >= " 253 + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE); 254 } 255 } else { 256 throw new InvalidAlgorithmParameterException( 257 "Unsupported key algorithm: " + keyAlgorithm); 258 } 259 } 260 261 private static void checkCorrectParametersSpec(int keyType, int keySize, 262 AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException { 263 if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) { 264 if (spec instanceof RSAKeyGenParameterSpec) { 265 RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec; 266 if (keySize != -1 && keySize != rsaSpec.getKeysize()) { 267 throw new InvalidAlgorithmParameterException("RSA key size must match: " 268 + keySize + " vs " + rsaSpec.getKeysize()); 269 } 270 } else { 271 throw new InvalidAlgorithmParameterException( 272 "RSA may only use RSAKeyGenParameterSpec"); 273 } 274 } 275 } 276 277 private static String getDefaultSignatureAlgorithmForKeyAlgorithm( 278 @KeyProperties.KeyAlgorithmEnum String algorithm) { 279 if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) { 280 return "sha256WithRSA"; 281 } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) { 282 return "sha256WithECDSA"; 283 } else { 284 throw new IllegalArgumentException("Unsupported key type " + algorithm); 285 } 286 } 287 288 private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) { 289 switch (keyType) { 290 case NativeConstants.EVP_PKEY_RSA: 291 if (spec instanceof RSAKeyGenParameterSpec) { 292 RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec; 293 return new byte[][] { rsaSpec.getPublicExponent().toByteArray() }; 294 } 295 break; 296 } 297 return null; 298 } 299 300 @Override 301 public void initialize(int keysize, SecureRandom random) { 302 throw new IllegalArgumentException( 303 "cannot specify keysize with AndroidKeyStore KeyPairGenerator"); 304 } 305 306 @Override 307 public void initialize(AlgorithmParameterSpec params, SecureRandom random) 308 throws InvalidAlgorithmParameterException { 309 if (params == null) { 310 throw new InvalidAlgorithmParameterException( 311 "Must supply params of type " + KeyGenParameterSpec.class.getName() 312 + " or " + KeyPairGeneratorSpec.class.getName()); 313 } 314 315 String keyAlgorithm; 316 KeyGenParameterSpec spec; 317 boolean encryptionAtRestRequired = false; 318 if (params instanceof KeyPairGeneratorSpec) { 319 KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params; 320 try { 321 KeyGenParameterSpec.Builder specBuilder; 322 keyAlgorithm = getKeyAlgorithm(legacySpec).toUpperCase(Locale.US); 323 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 324 specBuilder = new KeyGenParameterSpec.Builder( 325 legacySpec.getKeystoreAlias(), 326 KeyProperties.PURPOSE_SIGN 327 | KeyProperties.PURPOSE_VERIFY); 328 specBuilder.setDigests( 329 KeyProperties.DIGEST_NONE, 330 KeyProperties.DIGEST_MD5, 331 KeyProperties.DIGEST_SHA1, 332 KeyProperties.DIGEST_SHA224, 333 KeyProperties.DIGEST_SHA256, 334 KeyProperties.DIGEST_SHA384, 335 KeyProperties.DIGEST_SHA512); 336 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 337 specBuilder = new KeyGenParameterSpec.Builder( 338 legacySpec.getKeystoreAlias(), 339 KeyProperties.PURPOSE_ENCRYPT 340 | KeyProperties.PURPOSE_DECRYPT 341 | KeyProperties.PURPOSE_SIGN 342 | KeyProperties.PURPOSE_VERIFY); 343 specBuilder.setDigests( 344 KeyProperties.DIGEST_NONE, 345 KeyProperties.DIGEST_MD5, 346 KeyProperties.DIGEST_SHA1, 347 KeyProperties.DIGEST_SHA224, 348 KeyProperties.DIGEST_SHA256, 349 KeyProperties.DIGEST_SHA384, 350 KeyProperties.DIGEST_SHA512); 351 specBuilder.setSignaturePaddings( 352 KeyProperties.SIGNATURE_PADDING_RSA_PKCS1); 353 specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB); 354 specBuilder.setEncryptionPaddings( 355 KeyProperties.ENCRYPTION_PADDING_NONE, 356 KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1); 357 // Disable randomized encryption requirement to support encryption padding NONE 358 // above. 359 specBuilder.setRandomizedEncryptionRequired(false); 360 } else { 361 throw new InvalidAlgorithmParameterException( 362 "Unsupported key algorithm: " + keyAlgorithm); 363 } 364 365 if (legacySpec.getKeySize() != -1) { 366 specBuilder.setKeySize(legacySpec.getKeySize()); 367 } 368 if (legacySpec.getAlgorithmParameterSpec() != null) { 369 specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec()); 370 } 371 specBuilder.setCertificateSubject(legacySpec.getSubjectDN()); 372 specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber()); 373 specBuilder.setCertificateNotBefore(legacySpec.getStartDate()); 374 specBuilder.setCertificateNotAfter(legacySpec.getEndDate()); 375 encryptionAtRestRequired = legacySpec.isEncryptionRequired(); 376 specBuilder.setUserAuthenticationRequired(false); 377 378 spec = specBuilder.build(); 379 } catch (NullPointerException | IllegalArgumentException e) { 380 throw new InvalidAlgorithmParameterException(e); 381 } 382 } else if (params instanceof KeyGenParameterSpec) { 383 spec = (KeyGenParameterSpec) params; 384 keyAlgorithm = getAlgorithm(); 385 } else { 386 throw new InvalidAlgorithmParameterException( 387 "Unsupported params class: " + params.getClass().getName() 388 + ". Supported: " + KeyGenParameterSpec.class.getName() 389 + ", " + KeyPairGeneratorSpec.class); 390 } 391 392 int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm); 393 if (keyType == -1) { 394 throw new InvalidAlgorithmParameterException( 395 "Unsupported key algorithm: " + keyAlgorithm); 396 } 397 int keySize = spec.getKeySize(); 398 if (keySize == -1) { 399 keySize = getDefaultKeySize(keyType); 400 if (keySize == -1) { 401 throw new InvalidAlgorithmParameterException( 402 "Unsupported key algorithm: " + keyAlgorithm); 403 } 404 } 405 checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec()); 406 checkValidKeySize(keyAlgorithm, keyType, keySize); 407 408 mKeyAlgorithm = keyAlgorithm; 409 mKeyType = keyType; 410 mKeySize = keySize; 411 mSpec = spec; 412 mEncryptionAtRestRequired = encryptionAtRestRequired; 413 mKeyStore = KeyStore.getInstance(); 414 } 415} 416