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;
18
19import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
20
21import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
22
23import java.security.InvalidAlgorithmParameterException;
24import java.security.InvalidKeyException;
25import java.security.KeyFactory;
26import java.security.KeyPair;
27import java.security.KeyPairGenerator;
28import java.security.KeyPairGeneratorSpi;
29import java.security.NoSuchAlgorithmException;
30import java.security.PrivateKey;
31import java.security.PublicKey;
32import java.security.SecureRandom;
33import java.security.cert.CertificateEncodingException;
34import java.security.cert.X509Certificate;
35import java.security.spec.AlgorithmParameterSpec;
36import java.security.spec.InvalidKeySpecException;
37import 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@SuppressWarnings("deprecation")
53public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
54    public static final String NAME = "AndroidKeyPairGenerator";
55
56    private android.security.KeyStore mKeyStore;
57
58    private AndroidKeyPairGeneratorSpec 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 AndroidKeyPairGeneratorSpec} 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
76    public KeyPair generateKeyPair() {
77        if (mKeyStore == null || mSpec == null) {
78            throw new IllegalStateException(
79                    "Must call initialize with an AndroidKeyPairGeneratorSpec first");
80        }
81
82        final String alias = mSpec.getKeystoreAlias();
83
84        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
85
86        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
87        mKeyStore.generate(privateKeyAlias);
88
89        final PrivateKey privKey;
90        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
91        try {
92            privKey = engine.getPrivateKeyById(privateKeyAlias);
93        } catch (InvalidKeyException e) {
94            throw new RuntimeException("Can't get key", e);
95        }
96
97        final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
98
99        final PublicKey pubKey;
100        try {
101            final KeyFactory keyFact = KeyFactory.getInstance("RSA");
102            pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
103        } catch (NoSuchAlgorithmException e) {
104            throw new IllegalStateException("Can't instantiate RSA key generator", e);
105        } catch (InvalidKeySpecException e) {
106            throw new IllegalStateException("keystore returned invalid key encoding", e);
107        }
108
109        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
110        certGen.setPublicKey(pubKey);
111        certGen.setSerialNumber(mSpec.getSerialNumber());
112        certGen.setSubjectDN(mSpec.getSubjectDN());
113        certGen.setIssuerDN(mSpec.getSubjectDN());
114        certGen.setNotBefore(mSpec.getStartDate());
115        certGen.setNotAfter(mSpec.getEndDate());
116        certGen.setSignatureAlgorithm("sha1WithRSA");
117
118        final X509Certificate cert;
119        try {
120            cert = certGen.generate(privKey);
121        } catch (Exception e) {
122            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
123            throw new IllegalStateException("Can't generate certificate", e);
124        }
125
126        byte[] certBytes;
127        try {
128            certBytes = cert.getEncoded();
129        } catch (CertificateEncodingException e) {
130            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
131            throw new IllegalStateException("Can't get encoding of certificate", e);
132        }
133
134        if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) {
135            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
136            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
137        }
138
139        return new KeyPair(pubKey, privKey);
140    }
141
142    @Override
143    public void initialize(int keysize, SecureRandom random) {
144        throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
145    }
146
147    @Override
148    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
149            throws InvalidAlgorithmParameterException {
150        if (params == null) {
151            throw new InvalidAlgorithmParameterException(
152                    "must supply params of type AndroidKeyPairGenericSpec");
153        } else if (!(params instanceof AndroidKeyPairGeneratorSpec)) {
154            throw new InvalidAlgorithmParameterException(
155                    "params must be of type AndroidKeyPairGeneratorSpec");
156        }
157
158        AndroidKeyPairGeneratorSpec spec = (AndroidKeyPairGeneratorSpec) params;
159
160        mSpec = spec;
161        mKeyStore = android.security.KeyStore.getInstance();
162    }
163}
164