AndroidKeyStoreKeyPairGeneratorSpi.java revision 4350babc028822e8905190d88a9f5b8c6ffce8ec
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;
23import android.security.keymaster.ExportResult;
24import android.security.keymaster.KeymasterDefs;
25
26import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
27import com.android.org.conscrypt.NativeConstants;
28import com.android.org.conscrypt.OpenSSLEngine;
29
30import java.security.InvalidAlgorithmParameterException;
31import java.security.InvalidKeyException;
32import java.security.KeyFactory;
33import java.security.KeyPair;
34import java.security.KeyPairGenerator;
35import java.security.KeyPairGeneratorSpi;
36import java.security.NoSuchAlgorithmException;
37import java.security.PrivateKey;
38import java.security.ProviderException;
39import java.security.PublicKey;
40import java.security.SecureRandom;
41import java.security.cert.CertificateEncodingException;
42import java.security.cert.X509Certificate;
43import java.security.spec.AlgorithmParameterSpec;
44import java.security.spec.InvalidKeySpecException;
45import java.security.spec.RSAKeyGenParameterSpec;
46import java.security.spec.X509EncodedKeySpec;
47import java.util.Locale;
48
49/**
50 * Provides a way to create instances of a KeyPair which will be placed in the
51 * Android keystore service usable only by the application that called it. This
52 * can be used in conjunction with
53 * {@link java.security.KeyStore#getInstance(String)} using the
54 * {@code "AndroidKeyStore"} type.
55 * <p>
56 * This class can not be directly instantiated and must instead be used via the
57 * {@link KeyPairGenerator#getInstance(String)
58 * KeyPairGenerator.getInstance("AndroidKeyStore")} API.
59 *
60 * @hide
61 */
62public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
63
64    public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
65        public RSA() {
66            super(KeyProperties.KEY_ALGORITHM_RSA);
67        }
68    }
69
70    public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
71        public EC() {
72            super(KeyProperties.KEY_ALGORITHM_EC);
73        }
74    }
75
76    /*
77     * These must be kept in sync with system/security/keystore/defaults.h
78     */
79
80    /* EC */
81    private static final int EC_DEFAULT_KEY_SIZE = 256;
82    private static final int EC_MIN_KEY_SIZE = 192;
83    private static final int EC_MAX_KEY_SIZE = 521;
84
85    /* RSA */
86    private static final int RSA_DEFAULT_KEY_SIZE = 2048;
87    private static final int RSA_MIN_KEY_SIZE = 512;
88    private static final int RSA_MAX_KEY_SIZE = 8192;
89
90    private final String mAlgorithm;
91
92    private KeyStore mKeyStore;
93
94    private KeyGenParameterSpec mSpec;
95    private boolean mEncryptionAtRestRequired;
96    private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
97    private int mKeyType;
98    private int mKeySize;
99
100    protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
101        mAlgorithm = algorithm;
102    }
103
104    @KeyProperties.KeyAlgorithmEnum String getAlgorithm() {
105        return mAlgorithm;
106    }
107
108    /**
109     * Generate a KeyPair which is backed by the Android keystore service. You
110     * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
111     * with an {@link KeyPairGeneratorSpec} as the {@code params}
112     * argument before calling this otherwise an {@code IllegalStateException}
113     * will be thrown.
114     * <p>
115     * This will create an entry in the Android keystore service with a
116     * self-signed certificate using the {@code params} specified in the
117     * {@code initialize(params)} call.
118     *
119     * @throws IllegalStateException when called before calling
120     *             {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
121     * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
122     */
123    @Override
124    public KeyPair generateKeyPair() {
125        if (mKeyStore == null || mSpec == null) {
126            throw new IllegalStateException("Not initialized");
127        }
128
129        final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
130        if (((flags & KeyStore.FLAG_ENCRYPTED) != 0)
131                && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
132            throw new IllegalStateException(
133                    "Encryption at rest using secure lock screen credential requested for key pair"
134                    + ", but the user has not yet entered the credential");
135        }
136
137        final String alias = mSpec.getKeystoreAlias();
138
139        byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
140
141        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
142
143        boolean success = false;
144        try {
145            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
146            if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
147                    flags, args)) {
148                throw new IllegalStateException("could not generate key in keystore");
149            }
150
151            final PrivateKey privKey;
152            final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
153            try {
154                privKey = engine.getPrivateKeyById(privateKeyAlias);
155            } catch (InvalidKeyException e) {
156                throw new RuntimeException("Can't get key", e);
157            }
158
159            ExportResult exportResult =
160                    mKeyStore.exportKey(
161                            privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
162            if (exportResult == null) {
163                throw new KeyStoreConnectException();
164            } else if (exportResult.resultCode != KeyStore.NO_ERROR) {
165                throw new ProviderException(
166                        "Failed to obtain public key in X.509 format",
167                        KeyStore.getKeyStoreException(exportResult.resultCode));
168            }
169            final byte[] pubKeyBytes = exportResult.exportData;
170
171
172            final PublicKey pubKey;
173            try {
174                final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
175                pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
176            } catch (NoSuchAlgorithmException e) {
177                throw new IllegalStateException("Can't instantiate key generator", e);
178            } catch (InvalidKeySpecException e) {
179                throw new IllegalStateException("keystore returned invalid key encoding", e);
180            }
181
182            final X509Certificate cert;
183            try {
184                cert = generateCertificate(privKey, pubKey);
185            } catch (Exception e) {
186                throw new IllegalStateException("Can't generate certificate", e);
187            }
188
189            byte[] certBytes;
190            try {
191                certBytes = cert.getEncoded();
192            } catch (CertificateEncodingException e) {
193                throw new IllegalStateException("Can't get encoding of certificate", e);
194            }
195
196            if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
197                    flags)) {
198                throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
199            }
200
201            KeyPair result = new KeyPair(pubKey, privKey);
202            success = true;
203            return result;
204        } finally {
205            if (!success) {
206                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
207            }
208        }
209    }
210
211    @SuppressWarnings("deprecation")
212    private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
213            throws Exception {
214        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
215        certGen.setPublicKey(publicKey);
216        certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
217        certGen.setSubjectDN(mSpec.getCertificateSubject());
218        certGen.setIssuerDN(mSpec.getCertificateSubject());
219        certGen.setNotBefore(mSpec.getCertificateNotBefore());
220        certGen.setNotAfter(mSpec.getCertificateNotAfter());
221        certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
222        return certGen.generate(privateKey);
223    }
224
225    @NonNull
226    private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
227        String result = spec.getKeyType();
228        if (result != null) {
229            return result;
230        }
231        return getAlgorithm();
232    }
233
234    private static int getDefaultKeySize(int keyType) {
235        if (keyType == NativeConstants.EVP_PKEY_EC) {
236            return EC_DEFAULT_KEY_SIZE;
237        } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
238            return RSA_DEFAULT_KEY_SIZE;
239        }
240        return -1;
241    }
242
243    private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
244            throws InvalidAlgorithmParameterException {
245        if (keyType == NativeConstants.EVP_PKEY_EC) {
246            if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
247                throw new InvalidAlgorithmParameterException("EC keys must be >= "
248                        + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
249            }
250        } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
251            if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
252                throw new InvalidAlgorithmParameterException("RSA keys must be >= "
253                        + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
254            }
255        } else {
256            throw new InvalidAlgorithmParameterException(
257                "Unsupported key algorithm: " + keyAlgorithm);
258        }
259    }
260
261    private static void checkCorrectParametersSpec(int keyType, int keySize,
262            AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
263        if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) {
264            if (spec instanceof RSAKeyGenParameterSpec) {
265                RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
266                if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
267                    throw new InvalidAlgorithmParameterException("RSA key size must match: "
268                            + keySize + " vs " + rsaSpec.getKeysize());
269                }
270            } else {
271                throw new InvalidAlgorithmParameterException(
272                    "RSA may only use RSAKeyGenParameterSpec");
273            }
274        }
275    }
276
277    private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
278            @KeyProperties.KeyAlgorithmEnum String algorithm) {
279        if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
280            return "sha256WithRSA";
281        } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
282            return "sha256WithECDSA";
283        } else {
284            throw new IllegalArgumentException("Unsupported key type " + algorithm);
285        }
286    }
287
288    private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
289        switch (keyType) {
290            case NativeConstants.EVP_PKEY_RSA:
291                if (spec instanceof RSAKeyGenParameterSpec) {
292                    RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
293                    return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
294                }
295                break;
296        }
297        return null;
298    }
299
300    @Override
301    public void initialize(int keysize, SecureRandom random) {
302        throw new IllegalArgumentException(
303                "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
304    }
305
306    @Override
307    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
308            throws InvalidAlgorithmParameterException {
309        if (params == null) {
310            throw new InvalidAlgorithmParameterException(
311                    "Must supply params of type " + KeyGenParameterSpec.class.getName()
312                    + " or " + KeyPairGeneratorSpec.class.getName());
313        }
314
315        String keyAlgorithm;
316        KeyGenParameterSpec spec;
317        boolean encryptionAtRestRequired = false;
318        if (params instanceof KeyPairGeneratorSpec) {
319            KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
320            try {
321                KeyGenParameterSpec.Builder specBuilder;
322                keyAlgorithm = getKeyAlgorithm(legacySpec).toUpperCase(Locale.US);
323                if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
324                    specBuilder = new KeyGenParameterSpec.Builder(
325                            legacySpec.getKeystoreAlias(),
326                            KeyProperties.PURPOSE_SIGN
327                            | KeyProperties.PURPOSE_VERIFY);
328                    specBuilder.setDigests(
329                            KeyProperties.DIGEST_NONE,
330                            KeyProperties.DIGEST_MD5,
331                            KeyProperties.DIGEST_SHA1,
332                            KeyProperties.DIGEST_SHA224,
333                            KeyProperties.DIGEST_SHA256,
334                            KeyProperties.DIGEST_SHA384,
335                            KeyProperties.DIGEST_SHA512);
336                } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
337                    specBuilder = new KeyGenParameterSpec.Builder(
338                            legacySpec.getKeystoreAlias(),
339                            KeyProperties.PURPOSE_ENCRYPT
340                            | KeyProperties.PURPOSE_DECRYPT
341                            | KeyProperties.PURPOSE_SIGN
342                            | KeyProperties.PURPOSE_VERIFY);
343                    specBuilder.setDigests(
344                            KeyProperties.DIGEST_NONE,
345                            KeyProperties.DIGEST_MD5,
346                            KeyProperties.DIGEST_SHA1,
347                            KeyProperties.DIGEST_SHA224,
348                            KeyProperties.DIGEST_SHA256,
349                            KeyProperties.DIGEST_SHA384,
350                            KeyProperties.DIGEST_SHA512);
351                    specBuilder.setSignaturePaddings(
352                            KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
353                    specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
354                    specBuilder.setEncryptionPaddings(
355                            KeyProperties.ENCRYPTION_PADDING_NONE,
356                            KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
357                    // Disable randomized encryption requirement to support encryption padding NONE
358                    // above.
359                    specBuilder.setRandomizedEncryptionRequired(false);
360                } else {
361                    throw new InvalidAlgorithmParameterException(
362                            "Unsupported key algorithm: " + keyAlgorithm);
363                }
364
365                if (legacySpec.getKeySize() != -1) {
366                    specBuilder.setKeySize(legacySpec.getKeySize());
367                }
368                if (legacySpec.getAlgorithmParameterSpec() != null) {
369                    specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
370                }
371                specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
372                specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
373                specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
374                specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
375                encryptionAtRestRequired = legacySpec.isEncryptionRequired();
376                specBuilder.setUserAuthenticationRequired(false);
377
378                spec = specBuilder.build();
379            } catch (NullPointerException | IllegalArgumentException e) {
380                throw new InvalidAlgorithmParameterException(e);
381            }
382        } else if (params instanceof KeyGenParameterSpec) {
383            spec = (KeyGenParameterSpec) params;
384            keyAlgorithm = getAlgorithm();
385        } else {
386            throw new InvalidAlgorithmParameterException(
387                    "Unsupported params class: " + params.getClass().getName()
388                    + ". Supported: " + KeyGenParameterSpec.class.getName()
389                    + ", " + KeyPairGeneratorSpec.class);
390        }
391
392        int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
393        if (keyType == -1) {
394            throw new InvalidAlgorithmParameterException(
395                    "Unsupported key algorithm: " + keyAlgorithm);
396        }
397        int keySize = spec.getKeySize();
398        if (keySize == -1) {
399            keySize = getDefaultKeySize(keyType);
400            if (keySize == -1) {
401                throw new InvalidAlgorithmParameterException(
402                        "Unsupported key algorithm: " + keyAlgorithm);
403            }
404        }
405        checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
406        checkValidKeySize(keyAlgorithm, keyType, keySize);
407
408        mKeyAlgorithm = keyAlgorithm;
409        mKeyType = keyType;
410        mKeySize = keySize;
411        mSpec = spec;
412        mEncryptionAtRestRequired = encryptionAtRestRequired;
413        mKeyStore = KeyStore.getInstance();
414    }
415}
416