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