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