AndroidKeyStoreSpi.java revision 903d0fb98817dca284a640dbc853c7fcbbdc8313
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 libcore.util.EmptyArray;
20
21import android.security.Credentials;
22import android.security.KeyStore;
23import android.security.KeyStoreParameter;
24import android.security.keymaster.KeyCharacteristics;
25import android.security.keymaster.KeymasterArguments;
26import android.security.keymaster.KeymasterDefs;
27import android.security.keystore.KeyProperties;
28import android.security.keystore.KeyProtection;
29import android.util.Log;
30
31import java.io.ByteArrayInputStream;
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.OutputStream;
35import java.security.Key;
36import java.security.KeyStore.Entry;
37import java.security.KeyStore.PrivateKeyEntry;
38import java.security.KeyStore.ProtectionParameter;
39import java.security.KeyStore.SecretKeyEntry;
40import java.security.KeyStoreException;
41import java.security.KeyStoreSpi;
42import java.security.NoSuchAlgorithmException;
43import java.security.PrivateKey;
44import java.security.PublicKey;
45import java.security.UnrecoverableKeyException;
46import java.security.cert.Certificate;
47import java.security.cert.CertificateEncodingException;
48import java.security.cert.CertificateException;
49import java.security.cert.CertificateFactory;
50import java.security.cert.X509Certificate;
51import java.util.ArrayList;
52import java.util.Arrays;
53import java.util.Collection;
54import java.util.Collections;
55import java.util.Date;
56import java.util.Enumeration;
57import java.util.HashSet;
58import java.util.Iterator;
59import java.util.Set;
60
61import javax.crypto.SecretKey;
62
63/**
64 * A java.security.KeyStore interface for the Android KeyStore. An instance of
65 * it can be created via the {@link java.security.KeyStore#getInstance(String)
66 * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
67 * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
68 * <p>
69 * This is built on top of Android's keystore daemon. The convention of alias
70 * use is:
71 * <p>
72 * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
73 * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
74 * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
75 * entry which will have the rest of the chain concatenated in BER format.
76 * <p>
77 * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
78 * with a single certificate.
79 *
80 * @hide
81 */
82public class AndroidKeyStoreSpi extends KeyStoreSpi {
83    public static final String NAME = "AndroidKeyStore";
84
85    private KeyStore mKeyStore;
86
87    @Override
88    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
89            UnrecoverableKeyException {
90        if (isPrivateKeyEntry(alias)) {
91            String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
92            return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
93                    mKeyStore, privateKeyAlias);
94        } else if (isSecretKeyEntry(alias)) {
95            String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
96            return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
97                    mKeyStore, secretKeyAlias);
98        } else {
99            // Key not found
100            return null;
101        }
102    }
103
104    @Override
105    public Certificate[] engineGetCertificateChain(String alias) {
106        if (alias == null) {
107            throw new NullPointerException("alias == null");
108        }
109
110        final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
111        if (leaf == null) {
112            return null;
113        }
114
115        final Certificate[] caList;
116
117        final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
118        if (caBytes != null) {
119            final Collection<X509Certificate> caChain = toCertificates(caBytes);
120
121            caList = new Certificate[caChain.size() + 1];
122
123            final Iterator<X509Certificate> it = caChain.iterator();
124            int i = 1;
125            while (it.hasNext()) {
126                caList[i++] = it.next();
127            }
128        } else {
129            caList = new Certificate[1];
130        }
131
132        caList[0] = leaf;
133
134        return caList;
135    }
136
137    @Override
138    public Certificate engineGetCertificate(String alias) {
139        if (alias == null) {
140            throw new NullPointerException("alias == null");
141        }
142
143        byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
144        if (encodedCert != null) {
145            return getCertificateForPrivateKeyEntry(alias, encodedCert);
146        }
147
148        encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
149        if (encodedCert != null) {
150            return getCertificateForTrustedCertificateEntry(encodedCert);
151        }
152
153        // This entry/alias does not contain a certificate.
154        return null;
155    }
156
157    private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
158        // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
159        // there's no need to wrap this certificate as opposed to the certificate associated with
160        // a private key entry.
161        return toCertificate(encodedCert);
162    }
163
164    private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
165        // All crypto algorithms offered by Android Keystore for its private keys must also
166        // be offered for the corresponding public keys stored in the Android Keystore. The
167        // complication is that the underlying keystore service operates only on full key pairs,
168        // rather than just public keys or private keys. As a result, Android Keystore-backed
169        // crypto can only be offered for public keys for which keystore contains the
170        // corresponding private key. This is not the case for certificate-only entries (e.g.,
171        // trusted certificates).
172        //
173        // getCertificate().getPublicKey() is the only way to obtain the public key
174        // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
175        // that the returned public key points to the underlying key pair / private key
176        // when available.
177
178        X509Certificate cert = toCertificate(encodedCert);
179        if (cert == null) {
180            // Failed to parse the certificate.
181            return null;
182        }
183
184        String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
185        if (mKeyStore.contains(privateKeyAlias)) {
186            // As expected, keystore contains the private key corresponding to this public key. Wrap
187            // the certificate so that its getPublicKey method returns an Android Keystore
188            // PublicKey. This key will delegate crypto operations involving this public key to
189            // Android Keystore when higher-priority providers do not offer these crypto
190            // operations for this key.
191            return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
192        } else {
193            // This KeyStore entry/alias is supposed to contain the private key corresponding to
194            // the public key in this certificate, but it does not for some reason. It's probably a
195            // bug. Let other providers handle crypto operations involving the public key returned
196            // by this certificate's getPublicKey.
197            return cert;
198        }
199    }
200
201    /**
202     * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
203     * returned by the certificate contains information about the alias of the private key in
204     * keystore. This is needed so that Android Keystore crypto operations using public keys can
205     * find out which key alias to use. These operations cannot work without an alias.
206     */
207    private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
208            String privateKeyAlias, X509Certificate certificate) {
209        return (certificate != null)
210                ? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null;
211    }
212
213    private static X509Certificate toCertificate(byte[] bytes) {
214        try {
215            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
216            return (X509Certificate) certFactory.generateCertificate(
217                    new ByteArrayInputStream(bytes));
218        } catch (CertificateException e) {
219            Log.w(NAME, "Couldn't parse certificate in keystore", e);
220            return null;
221        }
222    }
223
224    @SuppressWarnings("unchecked")
225    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
226        try {
227            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
228            return (Collection<X509Certificate>) certFactory.generateCertificates(
229                            new ByteArrayInputStream(bytes));
230        } catch (CertificateException e) {
231            Log.w(NAME, "Couldn't parse certificates in keystore", e);
232            return new ArrayList<X509Certificate>();
233        }
234    }
235
236    private Date getModificationDate(String alias) {
237        final long epochMillis = mKeyStore.getmtime(alias);
238        if (epochMillis == -1L) {
239            return null;
240        }
241
242        return new Date(epochMillis);
243    }
244
245    @Override
246    public Date engineGetCreationDate(String alias) {
247        if (alias == null) {
248            throw new NullPointerException("alias == null");
249        }
250
251        Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
252        if (d != null) {
253            return d;
254        }
255
256        d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
257        if (d != null) {
258            return d;
259        }
260
261        d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
262        if (d != null) {
263            return d;
264        }
265
266        return getModificationDate(Credentials.CA_CERTIFICATE + alias);
267    }
268
269    @Override
270    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
271            throws KeyStoreException {
272        if ((password != null) && (password.length > 0)) {
273            throw new KeyStoreException("entries cannot be protected with passwords");
274        }
275
276        if (key instanceof PrivateKey) {
277            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
278        } else if (key instanceof SecretKey) {
279            setSecretKeyEntry(alias, (SecretKey) key, null);
280        } else {
281            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
282        }
283    }
284
285    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
286            throws KeyStoreException {
287        String keyAlgorithm = key.getAlgorithm();
288        KeyProtection.Builder specBuilder;
289        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
290            specBuilder =
291                    new KeyProtection.Builder(
292                            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
293            // Authorized to be used with any digest (including no digest).
294            specBuilder.setDigests(KeyProperties.DIGEST_NONE);
295        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
296            specBuilder =
297                    new KeyProtection.Builder(
298                            KeyProperties.PURPOSE_ENCRYPT
299                            | KeyProperties.PURPOSE_DECRYPT
300                            | KeyProperties.PURPOSE_SIGN
301                            | KeyProperties.PURPOSE_VERIFY);
302            // Authorized to be used with any digest (including no digest).
303            specBuilder.setDigests(KeyProperties.DIGEST_NONE);
304            // Authorized to be used with any encryption and signature padding scheme (including no
305            // padding).
306            specBuilder.setEncryptionPaddings(
307                    KeyProperties.ENCRYPTION_PADDING_NONE);
308            // Disable randomized encryption requirement to support encryption padding NONE
309            // above.
310            specBuilder.setRandomizedEncryptionRequired(false);
311        } else {
312            throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
313        }
314        specBuilder.setUserAuthenticationRequired(false);
315
316        return specBuilder.build();
317    }
318
319    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
320            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
321        int flags = 0;
322        KeyProtection spec;
323        if (param == null) {
324            spec = getLegacyKeyProtectionParameter(key);
325        } else if (param instanceof KeyStoreParameter) {
326            spec = getLegacyKeyProtectionParameter(key);
327            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
328            if (legacySpec.isEncryptionRequired()) {
329                flags = KeyStore.FLAG_ENCRYPTED;
330            }
331        } else if (param instanceof KeyProtection) {
332            spec = (KeyProtection) param;
333        } else {
334            throw new KeyStoreException(
335                    "Unsupported protection parameter class:" + param.getClass().getName()
336                    + ". Supported: " + KeyProtection.class.getName() + ", "
337                    + KeyStoreParameter.class.getName());
338        }
339
340        // Make sure the chain exists since this is a PrivateKey
341        if ((chain == null) || (chain.length == 0)) {
342            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
343        }
344
345        // Do chain type checking.
346        X509Certificate[] x509chain = new X509Certificate[chain.length];
347        for (int i = 0; i < chain.length; i++) {
348            if (!"X.509".equals(chain[i].getType())) {
349                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
350                        + i);
351            }
352
353            if (!(chain[i] instanceof X509Certificate)) {
354                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
355                        + i);
356            }
357
358            x509chain[i] = (X509Certificate) chain[i];
359        }
360
361        final byte[] userCertBytes;
362        try {
363            userCertBytes = x509chain[0].getEncoded();
364        } catch (CertificateEncodingException e) {
365            throw new KeyStoreException("Failed to encode certificate #0", e);
366        }
367
368        /*
369         * If we have a chain, store it in the CA certificate slot for this
370         * alias as concatenated DER-encoded certificates. These can be
371         * deserialized by {@link CertificateFactory#generateCertificates}.
372         */
373        final byte[] chainBytes;
374        if (chain.length > 1) {
375            /*
376             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
377             * so we only need the certificates starting at index 1.
378             */
379            final byte[][] certsBytes = new byte[x509chain.length - 1][];
380            int totalCertLength = 0;
381            for (int i = 0; i < certsBytes.length; i++) {
382                try {
383                    certsBytes[i] = x509chain[i + 1].getEncoded();
384                    totalCertLength += certsBytes[i].length;
385                } catch (CertificateEncodingException e) {
386                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
387                }
388            }
389
390            /*
391             * Serialize this into one byte array so we can later call
392             * CertificateFactory#generateCertificates to recover them.
393             */
394            chainBytes = new byte[totalCertLength];
395            int outputOffset = 0;
396            for (int i = 0; i < certsBytes.length; i++) {
397                final int certLength = certsBytes[i].length;
398                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
399                outputOffset += certLength;
400                certsBytes[i] = null;
401            }
402        } else {
403            chainBytes = null;
404        }
405
406        final String pkeyAlias;
407        if (key instanceof AndroidKeyStorePrivateKey) {
408            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
409        } else {
410            pkeyAlias = null;
411        }
412
413        byte[] pkcs8EncodedPrivateKeyBytes;
414        KeymasterArguments importArgs;
415        final boolean shouldReplacePrivateKey;
416        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
417            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
418            if (!alias.equals(keySubalias)) {
419                throw new KeyStoreException("Can only replace keys with same alias: " + alias
420                        + " != " + keySubalias);
421            }
422            shouldReplacePrivateKey = false;
423            importArgs = null;
424            pkcs8EncodedPrivateKeyBytes = null;
425        } else {
426            shouldReplacePrivateKey = true;
427            // Make sure the PrivateKey format is the one we support.
428            final String keyFormat = key.getFormat();
429            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
430                throw new KeyStoreException(
431                        "Unsupported private key export format: " + keyFormat
432                        + ". Only private keys which export their key material in PKCS#8 format are"
433                        + " supported.");
434            }
435
436            // Make sure we can actually encode the key.
437            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
438            if (pkcs8EncodedPrivateKeyBytes == null) {
439                throw new KeyStoreException("Private key did not export any key material");
440            }
441
442            importArgs = new KeymasterArguments();
443            try {
444                importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
445                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
446                                key.getAlgorithm()));
447                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
448                importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
449                        KeyProperties.Purpose.allToKeymaster(purposes));
450                if (spec.isDigestsSpecified()) {
451                    importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
452                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
453                }
454
455                importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
456                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
457                int[] keymasterEncryptionPaddings =
458                        KeyProperties.EncryptionPadding.allToKeymaster(
459                                spec.getEncryptionPaddings());
460                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
461                        && (spec.isRandomizedEncryptionRequired())) {
462                    for (int keymasterPadding : keymasterEncryptionPaddings) {
463                        if (!KeymasterUtils
464                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
465                                        keymasterPadding)) {
466                            throw new KeyStoreException(
467                                    "Randomized encryption (IND-CPA) required but is violated by"
468                                    + " encryption padding mode: "
469                                    + KeyProperties.EncryptionPadding.fromKeymaster(
470                                            keymasterPadding)
471                                    + ". See KeyProtection documentation.");
472                        }
473                    }
474                }
475                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
476                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
477                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
478                KeymasterUtils.addUserAuthArgs(importArgs,
479                        spec.isUserAuthenticationRequired(),
480                        spec.getUserAuthenticationValidityDurationSeconds());
481                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
482                        spec.getKeyValidityStart());
483                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
484                        spec.getKeyValidityForOriginationEnd());
485                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
486                        spec.getKeyValidityForConsumptionEnd());
487            } catch (IllegalArgumentException e) {
488                throw new KeyStoreException("Invalid parameter", e);
489            }
490        }
491
492
493        boolean success = false;
494        try {
495            // Store the private key, if necessary
496            if (shouldReplacePrivateKey) {
497                // Delete the stored private key and any related entries before importing the
498                // provided key
499                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
500                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
501                int errorCode = mKeyStore.importKey(
502                        Credentials.USER_PRIVATE_KEY + alias,
503                        importArgs,
504                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
505                        pkcs8EncodedPrivateKeyBytes,
506                        flags,
507                        resultingKeyCharacteristics);
508                if (errorCode != KeyStore.NO_ERROR) {
509                    throw new KeyStoreException("Failed to store private key",
510                            KeyStore.getKeyStoreException(errorCode));
511                }
512            } else {
513                // Keep the stored private key around -- delete all other entry types
514                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
515                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
516            }
517
518            // Store the leaf certificate
519            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
520                    KeyStore.UID_SELF, flags);
521            if (errorCode != KeyStore.NO_ERROR) {
522                throw new KeyStoreException("Failed to store certificate #0",
523                        KeyStore.getKeyStoreException(errorCode));
524            }
525
526            // Store the certificate chain
527            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
528                    KeyStore.UID_SELF, flags);
529            if (errorCode != KeyStore.NO_ERROR) {
530                throw new KeyStoreException("Failed to store certificate chain",
531                        KeyStore.getKeyStoreException(errorCode));
532            }
533            success = true;
534        } finally {
535            if (!success) {
536                if (shouldReplacePrivateKey) {
537                    Credentials.deleteAllTypesForAlias(mKeyStore, alias);
538                } else {
539                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
540                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
541                }
542            }
543        }
544    }
545
546    private void setSecretKeyEntry(String entryAlias, SecretKey key,
547            java.security.KeyStore.ProtectionParameter param)
548            throws KeyStoreException {
549        if ((param != null) && (!(param instanceof KeyProtection))) {
550            throw new KeyStoreException(
551                    "Unsupported protection parameter class: " + param.getClass().getName()
552                    + ". Supported: " + KeyProtection.class.getName());
553        }
554        KeyProtection params = (KeyProtection) param;
555
556        if (key instanceof AndroidKeyStoreSecretKey) {
557            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
558            // overwrite its own entry.
559            String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
560            if (keyAliasInKeystore == null) {
561                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
562            }
563            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
564                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
565                        + keyAliasInKeystore);
566            }
567            String keyEntryAlias =
568                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
569            if (!entryAlias.equals(keyEntryAlias)) {
570                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
571                        + " alias: " + entryAlias + " != " + keyEntryAlias);
572            }
573            // This is the entry where this key is already stored. No need to do anything.
574            if (params != null) {
575                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
576                        + " parameters not supported");
577            }
578            return;
579        }
580
581        if (params == null) {
582            throw new KeyStoreException(
583                    "Protection parameters must be specified when importing a symmetric key");
584        }
585
586        // Not a KeyStore-backed secret key -- import its key material into keystore.
587        String keyExportFormat = key.getFormat();
588        if (keyExportFormat == null) {
589            throw new KeyStoreException(
590                    "Only secret keys that export their key material are supported");
591        } else if (!"RAW".equals(keyExportFormat)) {
592            throw new KeyStoreException(
593                    "Unsupported secret key material export format: " + keyExportFormat);
594        }
595        byte[] keyMaterial = key.getEncoded();
596        if (keyMaterial == null) {
597            throw new KeyStoreException("Key did not export its key material despite supporting"
598                    + " RAW format export");
599        }
600
601        String keyAlgorithmString = key.getAlgorithm();
602        int keymasterAlgorithm;
603        int keymasterDigest;
604        try {
605            keymasterAlgorithm =
606                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(keyAlgorithmString);
607            keymasterDigest = KeyProperties.KeyAlgorithm.toKeymasterDigest(keyAlgorithmString);
608        } catch (IllegalArgumentException e) {
609            throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
610        }
611
612        KeymasterArguments args = new KeymasterArguments();
613        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
614
615        int[] keymasterDigests;
616        if (params.isDigestsSpecified()) {
617            // Digest(s) specified in parameters
618            keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
619            if (keymasterDigest != -1) {
620                // Digest also specified in the JCA key algorithm name.
621                if (!com.android.internal.util.ArrayUtils.contains(
622                        keymasterDigests, keymasterDigest)) {
623                    throw new KeyStoreException("Key digest mismatch"
624                            + ". Key: " + keyAlgorithmString
625                            + ", parameter spec: " + Arrays.asList(params.getDigests()));
626                }
627                // When the key is read back from keystore we reconstruct the JCA key algorithm
628                // name from the KM_TAG_ALGORITHM and the first KM_TAG_DIGEST. Thus we need to
629                // ensure that the digest reflected in the JCA key algorithm name is the first
630                // KM_TAG_DIGEST tag.
631                if (keymasterDigests[0] != keymasterDigest) {
632                    // The first digest is not the one implied by the JCA key algorithm name.
633                    // Swap the implied digest with the first one.
634                    for (int i = 0; i < keymasterDigests.length; i++) {
635                        if (keymasterDigests[i] == keymasterDigest) {
636                            keymasterDigests[i] = keymasterDigests[0];
637                            keymasterDigests[0] = keymasterDigest;
638                            break;
639                        }
640                    }
641                }
642            }
643        } else {
644            // No digest specified in parameters
645            if (keymasterDigest != -1) {
646                // Digest specified in the JCA key algorithm name.
647                keymasterDigests = new int[] {keymasterDigest};
648            } else {
649                keymasterDigests = EmptyArray.INT;
650            }
651        }
652        args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
653        if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
654            if (keymasterDigests.length == 0) {
655                throw new KeyStoreException("At least one digest algorithm must be specified"
656                        + " for key algorithm " + keyAlgorithmString);
657            }
658        }
659
660        @KeyProperties.PurposeEnum int purposes = params.getPurposes();
661        int[] keymasterBlockModes =
662                KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
663        if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
664                && (params.isRandomizedEncryptionRequired())) {
665            for (int keymasterBlockMode : keymasterBlockModes) {
666                if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
667                        keymasterBlockMode)) {
668                    throw new KeyStoreException(
669                            "Randomized encryption (IND-CPA) required but may be violated by block"
670                            + " mode: "
671                            + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
672                            + ". See KeyProtection documentation.");
673                }
674            }
675        }
676        args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
677        args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
678        if (params.getSignaturePaddings().length > 0) {
679            throw new KeyStoreException("Signature paddings not supported for symmetric keys");
680        }
681        int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
682                params.getEncryptionPaddings());
683        args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
684        KeymasterUtils.addUserAuthArgs(args,
685                params.isUserAuthenticationRequired(),
686                params.getUserAuthenticationValidityDurationSeconds());
687        args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart());
688        args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
689                params.getKeyValidityForOriginationEnd());
690        args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
691                params.getKeyValidityForConsumptionEnd());
692
693        if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
694                && (!params.isRandomizedEncryptionRequired())) {
695            // Permit caller-provided IV when encrypting with this key
696            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
697        }
698
699        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
700        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
701        int errorCode = mKeyStore.importKey(
702                keyAliasInKeystore,
703                args,
704                KeymasterDefs.KM_KEY_FORMAT_RAW,
705                keyMaterial,
706                0, // flags
707                new KeyCharacteristics());
708        if (errorCode != KeyStore.NO_ERROR) {
709            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
710                + errorCode);
711        }
712    }
713
714    @Override
715    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
716            throws KeyStoreException {
717        throw new KeyStoreException("Operation not supported because key encoding is unknown");
718    }
719
720    @Override
721    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
722        if (isKeyEntry(alias)) {
723            throw new KeyStoreException("Entry exists and is not a trusted certificate");
724        }
725
726        // We can't set something to null.
727        if (cert == null) {
728            throw new NullPointerException("cert == null");
729        }
730
731        final byte[] encoded;
732        try {
733            encoded = cert.getEncoded();
734        } catch (CertificateEncodingException e) {
735            throw new KeyStoreException(e);
736        }
737
738        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
739                KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
740            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
741        }
742    }
743
744    @Override
745    public void engineDeleteEntry(String alias) throws KeyStoreException {
746        if (!engineContainsAlias(alias)) {
747            return;
748        }
749        // At least one entry corresponding to this alias exists in keystore
750
751        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
752            throw new KeyStoreException("Failed to delete entry: " + alias);
753        }
754    }
755
756    private Set<String> getUniqueAliases() {
757        final String[] rawAliases = mKeyStore.list("");
758        if (rawAliases == null) {
759            return new HashSet<String>();
760        }
761
762        final Set<String> aliases = new HashSet<String>(rawAliases.length);
763        for (String alias : rawAliases) {
764            final int idx = alias.indexOf('_');
765            if ((idx == -1) || (alias.length() <= idx)) {
766                Log.e(NAME, "invalid alias: " + alias);
767                continue;
768            }
769
770            aliases.add(new String(alias.substring(idx + 1)));
771        }
772
773        return aliases;
774    }
775
776    @Override
777    public Enumeration<String> engineAliases() {
778        return Collections.enumeration(getUniqueAliases());
779    }
780
781    @Override
782    public boolean engineContainsAlias(String alias) {
783        if (alias == null) {
784            throw new NullPointerException("alias == null");
785        }
786
787        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
788                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
789                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
790                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
791    }
792
793    @Override
794    public int engineSize() {
795        return getUniqueAliases().size();
796    }
797
798    @Override
799    public boolean engineIsKeyEntry(String alias) {
800        return isKeyEntry(alias);
801    }
802
803    private boolean isKeyEntry(String alias) {
804        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
805    }
806
807    private boolean isPrivateKeyEntry(String alias) {
808        if (alias == null) {
809            throw new NullPointerException("alias == null");
810        }
811
812        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
813    }
814
815    private boolean isSecretKeyEntry(String alias) {
816        if (alias == null) {
817            throw new NullPointerException("alias == null");
818        }
819
820        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
821    }
822
823    private boolean isCertificateEntry(String alias) {
824        if (alias == null) {
825            throw new NullPointerException("alias == null");
826        }
827
828        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
829    }
830
831    @Override
832    public boolean engineIsCertificateEntry(String alias) {
833        return !isKeyEntry(alias) && isCertificateEntry(alias);
834    }
835
836    @Override
837    public String engineGetCertificateAlias(Certificate cert) {
838        if (cert == null) {
839            return null;
840        }
841        if (!"X.509".equalsIgnoreCase(cert.getType())) {
842            // Only X.509 certificates supported
843            return null;
844        }
845        byte[] targetCertBytes;
846        try {
847            targetCertBytes = cert.getEncoded();
848        } catch (CertificateEncodingException e) {
849            return null;
850        }
851        if (targetCertBytes == null) {
852            return null;
853        }
854
855        final Set<String> nonCaEntries = new HashSet<String>();
856
857        /*
858         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
859         * says to only compare the first certificate in the chain which is
860         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
861         * convention.
862         */
863        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE);
864        if (certAliases != null) {
865            for (String alias : certAliases) {
866                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
867                if (certBytes == null) {
868                    continue;
869                }
870
871                nonCaEntries.add(alias);
872
873                if (Arrays.equals(certBytes, targetCertBytes)) {
874                    return alias;
875                }
876            }
877        }
878
879        /*
880         * Look at all the TrustedCertificateEntry types. Skip all the
881         * PrivateKeyEntry we looked at above.
882         */
883        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE);
884        if (certAliases != null) {
885            for (String alias : caAliases) {
886                if (nonCaEntries.contains(alias)) {
887                    continue;
888                }
889
890                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
891                if (certBytes == null) {
892                    continue;
893                }
894
895                if (Arrays.equals(certBytes, targetCertBytes)) {
896                    return alias;
897                }
898            }
899        }
900
901        return null;
902    }
903
904    @Override
905    public void engineStore(OutputStream stream, char[] password) throws IOException,
906            NoSuchAlgorithmException, CertificateException {
907        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
908    }
909
910    @Override
911    public void engineLoad(InputStream stream, char[] password) throws IOException,
912            NoSuchAlgorithmException, CertificateException {
913        if (stream != null) {
914            throw new IllegalArgumentException("InputStream not supported");
915        }
916
917        if (password != null) {
918            throw new IllegalArgumentException("password not supported");
919        }
920
921        // Unfortunate name collision.
922        mKeyStore = KeyStore.getInstance();
923    }
924
925    @Override
926    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
927            throws KeyStoreException {
928        if (entry == null) {
929            throw new KeyStoreException("entry == null");
930        }
931
932        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
933
934        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
935            java.security.KeyStore.TrustedCertificateEntry trE =
936                    (java.security.KeyStore.TrustedCertificateEntry) entry;
937            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
938            return;
939        }
940
941        if (entry instanceof PrivateKeyEntry) {
942            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
943            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
944        } else if (entry instanceof SecretKeyEntry) {
945            SecretKeyEntry secE = (SecretKeyEntry) entry;
946            setSecretKeyEntry(alias, secE.getSecretKey(), param);
947        } else {
948            throw new KeyStoreException(
949                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
950                    + "; was " + entry);
951        }
952    }
953
954    /**
955     * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
956     * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
957     * can find out which keystore private key entry to use. This is needed so that Android Keystore
958     * crypto operations using public keys can find out which key alias to use. These operations
959     * require an alias.
960     */
961    static class KeyStoreX509Certificate extends DelegatingX509Certificate {
962        private final String mPrivateKeyAlias;
963        KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) {
964            super(delegate);
965            mPrivateKeyAlias = privateKeyAlias;
966        }
967
968        @Override
969        public PublicKey getPublicKey() {
970            PublicKey original = super.getPublicKey();
971            return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
972                    mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded());
973        }
974    }
975}
976