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