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 17 package android.security; 18 19 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; 20 21 import com.android.org.conscrypt.NativeCrypto; 22 import com.android.org.conscrypt.OpenSSLEngine; 23 24 import java.security.InvalidAlgorithmParameterException; 25 import java.security.InvalidKeyException; 26 import java.security.KeyFactory; 27 import java.security.KeyPair; 28 import java.security.KeyPairGenerator; 29 import java.security.KeyPairGeneratorSpi; 30 import java.security.NoSuchAlgorithmException; 31 import java.security.PrivateKey; 32 import java.security.PublicKey; 33 import java.security.SecureRandom; 34 import java.security.cert.CertificateEncodingException; 35 import java.security.cert.X509Certificate; 36 import java.security.spec.AlgorithmParameterSpec; 37 import java.security.spec.DSAParameterSpec; 38 import java.security.spec.ECParameterSpec; 39 import java.security.spec.InvalidKeySpecException; 40 import java.security.spec.RSAKeyGenParameterSpec; 41 import java.security.spec.X509EncodedKeySpec; 42 43 /** 44 * Provides a way to create instances of a KeyPair which will be placed in the 45 * Android keystore service usable only by the application that called it. This 46 * can be used in conjunction with 47 * {@link java.security.KeyStore#getInstance(String)} using the 48 * {@code "AndroidKeyStore"} type. 49 * <p> 50 * This class can not be directly instantiated and must instead be used via the 51 * {@link KeyPairGenerator#getInstance(String) 52 * KeyPairGenerator.getInstance("AndroidKeyPairGenerator")} API. 53 * 54 * {@hide} 55 */ 56 public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { 57 private android.security.KeyStore mKeyStore; 58 59 private KeyPairGeneratorSpec mSpec; 60 61 /** 62 * Generate a KeyPair which is backed by the Android keystore service. You 63 * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)} 64 * with an {@link KeyPairGeneratorSpec} as the {@code params} 65 * argument before calling this otherwise an {@code IllegalStateException} 66 * will be thrown. 67 * <p> 68 * This will create an entry in the Android keystore service with a 69 * self-signed certificate using the {@code params} specified in the 70 * {@code initialize(params)} call. 71 * 72 * @throws IllegalStateException when called before calling 73 * {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)} 74 * @see java.security.KeyPairGeneratorSpi#generateKeyPair() 75 */ 76 @Override 77 public KeyPair generateKeyPair() { 78 if (mKeyStore == null || mSpec == null) { 79 throw new IllegalStateException( 80 "Must call initialize with an android.security.KeyPairGeneratorSpec first"); 81 } 82 83 if (((mSpec.getFlags() & KeyStore.FLAG_ENCRYPTED) != 0) 84 && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { 85 throw new IllegalStateException( 86 "Android keystore must be in initialized and unlocked state " 87 + "if encryption is required"); 88 } 89 90 final String alias = mSpec.getKeystoreAlias(); 91 92 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 93 94 final int keyType = KeyStore.getKeyTypeForAlgorithm(mSpec.getKeyType()); 95 byte[][] args = getArgsForKeyType(keyType, mSpec.getAlgorithmParameterSpec()); 96 97 final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 98 if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, keyType, 99 mSpec.getKeySize(), mSpec.getFlags(), args)) { 100 throw new IllegalStateException("could not generate key in keystore"); 101 } 102 103 final PrivateKey privKey; 104 final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); 105 try { 106 privKey = engine.getPrivateKeyById(privateKeyAlias); 107 } catch (InvalidKeyException e) { 108 throw new RuntimeException("Can't get key", e); 109 } 110 111 final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias); 112 113 final PublicKey pubKey; 114 try { 115 final KeyFactory keyFact = KeyFactory.getInstance(mSpec.getKeyType()); 116 pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes)); 117 } catch (NoSuchAlgorithmException e) { 118 throw new IllegalStateException("Can't instantiate key generator", e); 119 } catch (InvalidKeySpecException e) { 120 throw new IllegalStateException("keystore returned invalid key encoding", e); 121 } 122 123 final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 124 certGen.setPublicKey(pubKey); 125 certGen.setSerialNumber(mSpec.getSerialNumber()); 126 certGen.setSubjectDN(mSpec.getSubjectDN()); 127 certGen.setIssuerDN(mSpec.getSubjectDN()); 128 certGen.setNotBefore(mSpec.getStartDate()); 129 certGen.setNotAfter(mSpec.getEndDate()); 130 certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyType(mSpec.getKeyType())); 131 132 final X509Certificate cert; 133 try { 134 cert = certGen.generate(privKey); 135 } catch (Exception e) { 136 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 137 throw new IllegalStateException("Can't generate certificate", e); 138 } 139 140 byte[] certBytes; 141 try { 142 certBytes = cert.getEncoded(); 143 } catch (CertificateEncodingException e) { 144 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 145 throw new IllegalStateException("Can't get encoding of certificate", e); 146 } 147 148 if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF, 149 mSpec.getFlags())) { 150 Credentials.deleteAllTypesForAlias(mKeyStore, alias); 151 throw new IllegalStateException("Can't store certificate in AndroidKeyStore"); 152 } 153 154 return new KeyPair(pubKey, privKey); 155 } 156 157 private static String getDefaultSignatureAlgorithmForKeyType(String keyType) { 158 if ("RSA".equalsIgnoreCase(keyType)) { 159 return "sha256WithRSA"; 160 } else if ("DSA".equalsIgnoreCase(keyType)) { 161 return "sha1WithDSA"; 162 } else if ("EC".equalsIgnoreCase(keyType)) { 163 return "sha256WithECDSA"; 164 } else { 165 throw new IllegalArgumentException("Unsupported key type " + keyType); 166 } 167 } 168 169 private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) { 170 switch (keyType) { 171 case NativeCrypto.EVP_PKEY_RSA: 172 if (spec instanceof RSAKeyGenParameterSpec) { 173 RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec; 174 return new byte[][] { rsaSpec.getPublicExponent().toByteArray() }; 175 } 176 break; 177 case NativeCrypto.EVP_PKEY_DSA: 178 if (spec instanceof DSAParameterSpec) { 179 DSAParameterSpec dsaSpec = (DSAParameterSpec) spec; 180 return new byte[][] { dsaSpec.getG().toByteArray(), 181 dsaSpec.getP().toByteArray(), dsaSpec.getQ().toByteArray() }; 182 } 183 break; 184 } 185 return null; 186 } 187 188 @Override 189 public void initialize(int keysize, SecureRandom random) { 190 throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator"); 191 } 192 193 @Override 194 public void initialize(AlgorithmParameterSpec params, SecureRandom random) 195 throws InvalidAlgorithmParameterException { 196 if (params == null) { 197 throw new InvalidAlgorithmParameterException( 198 "must supply params of type android.security.KeyPairGeneratorSpec"); 199 } else if (!(params instanceof KeyPairGeneratorSpec)) { 200 throw new InvalidAlgorithmParameterException( 201 "params must be of type android.security.KeyPairGeneratorSpec"); 202 } 203 204 KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params; 205 206 mSpec = spec; 207 mKeyStore = android.security.KeyStore.getInstance(); 208 } 209 } 210