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