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