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            if (spec.isCriticalToDeviceEncryption()) {
355                flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
356            }
357        } else {
358            throw new KeyStoreException(
359                    "Unsupported protection parameter class:" + param.getClass().getName()
360                    + ". Supported: " + KeyProtection.class.getName() + ", "
361                    + KeyStoreParameter.class.getName());
362        }
363
364        // Make sure the chain exists since this is a PrivateKey
365        if ((chain == null) || (chain.length == 0)) {
366            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
367        }
368
369        // Do chain type checking.
370        X509Certificate[] x509chain = new X509Certificate[chain.length];
371        for (int i = 0; i < chain.length; i++) {
372            if (!"X.509".equals(chain[i].getType())) {
373                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
374                        + i);
375            }
376
377            if (!(chain[i] instanceof X509Certificate)) {
378                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
379                        + i);
380            }
381
382            x509chain[i] = (X509Certificate) chain[i];
383        }
384
385        final byte[] userCertBytes;
386        try {
387            userCertBytes = x509chain[0].getEncoded();
388        } catch (CertificateEncodingException e) {
389            throw new KeyStoreException("Failed to encode certificate #0", e);
390        }
391
392        /*
393         * If we have a chain, store it in the CA certificate slot for this
394         * alias as concatenated DER-encoded certificates. These can be
395         * deserialized by {@link CertificateFactory#generateCertificates}.
396         */
397        final byte[] chainBytes;
398        if (chain.length > 1) {
399            /*
400             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
401             * so we only need the certificates starting at index 1.
402             */
403            final byte[][] certsBytes = new byte[x509chain.length - 1][];
404            int totalCertLength = 0;
405            for (int i = 0; i < certsBytes.length; i++) {
406                try {
407                    certsBytes[i] = x509chain[i + 1].getEncoded();
408                    totalCertLength += certsBytes[i].length;
409                } catch (CertificateEncodingException e) {
410                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
411                }
412            }
413
414            /*
415             * Serialize this into one byte array so we can later call
416             * CertificateFactory#generateCertificates to recover them.
417             */
418            chainBytes = new byte[totalCertLength];
419            int outputOffset = 0;
420            for (int i = 0; i < certsBytes.length; i++) {
421                final int certLength = certsBytes[i].length;
422                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
423                outputOffset += certLength;
424                certsBytes[i] = null;
425            }
426        } else {
427            chainBytes = null;
428        }
429
430        final String pkeyAlias;
431        if (key instanceof AndroidKeyStorePrivateKey) {
432            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
433        } else {
434            pkeyAlias = null;
435        }
436
437        byte[] pkcs8EncodedPrivateKeyBytes;
438        KeymasterArguments importArgs;
439        final boolean shouldReplacePrivateKey;
440        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
441            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
442            if (!alias.equals(keySubalias)) {
443                throw new KeyStoreException("Can only replace keys with same alias: " + alias
444                        + " != " + keySubalias);
445            }
446            shouldReplacePrivateKey = false;
447            importArgs = null;
448            pkcs8EncodedPrivateKeyBytes = null;
449        } else {
450            shouldReplacePrivateKey = true;
451            // Make sure the PrivateKey format is the one we support.
452            final String keyFormat = key.getFormat();
453            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
454                throw new KeyStoreException(
455                        "Unsupported private key export format: " + keyFormat
456                        + ". Only private keys which export their key material in PKCS#8 format are"
457                        + " supported.");
458            }
459
460            // Make sure we can actually encode the key.
461            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
462            if (pkcs8EncodedPrivateKeyBytes == null) {
463                throw new KeyStoreException("Private key did not export any key material");
464            }
465
466            importArgs = new KeymasterArguments();
467            try {
468                importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
469                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
470                                key.getAlgorithm()));
471                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
472                importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
473                        KeyProperties.Purpose.allToKeymaster(purposes));
474                if (spec.isDigestsSpecified()) {
475                    importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
476                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
477                }
478
479                importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
480                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
481                int[] keymasterEncryptionPaddings =
482                        KeyProperties.EncryptionPadding.allToKeymaster(
483                                spec.getEncryptionPaddings());
484                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
485                        && (spec.isRandomizedEncryptionRequired())) {
486                    for (int keymasterPadding : keymasterEncryptionPaddings) {
487                        if (!KeymasterUtils
488                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
489                                        keymasterPadding)) {
490                            throw new KeyStoreException(
491                                    "Randomized encryption (IND-CPA) required but is violated by"
492                                    + " encryption padding mode: "
493                                    + KeyProperties.EncryptionPadding.fromKeymaster(
494                                            keymasterPadding)
495                                    + ". See KeyProtection documentation.");
496                        }
497                    }
498                }
499                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
500                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
501                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
502                KeymasterUtils.addUserAuthArgs(importArgs,
503                        spec.isUserAuthenticationRequired(),
504                        spec.getUserAuthenticationValidityDurationSeconds(),
505                        spec.isUserAuthenticationValidWhileOnBody(),
506                        spec.isInvalidatedByBiometricEnrollment(),
507                        spec.getBoundToSpecificSecureUserId());
508                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
509                        spec.getKeyValidityStart());
510                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
511                        spec.getKeyValidityForOriginationEnd());
512                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
513                        spec.getKeyValidityForConsumptionEnd());
514            } catch (IllegalArgumentException | IllegalStateException e) {
515                throw new KeyStoreException(e);
516            }
517        }
518
519
520        boolean success = false;
521        try {
522            // Store the private key, if necessary
523            if (shouldReplacePrivateKey) {
524                // Delete the stored private key and any related entries before importing the
525                // provided key
526                Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
527                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
528                int errorCode = mKeyStore.importKey(
529                        Credentials.USER_PRIVATE_KEY + alias,
530                        importArgs,
531                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
532                        pkcs8EncodedPrivateKeyBytes,
533                        mUid,
534                        flags,
535                        resultingKeyCharacteristics);
536                if (errorCode != KeyStore.NO_ERROR) {
537                    throw new KeyStoreException("Failed to store private key",
538                            KeyStore.getKeyStoreException(errorCode));
539                }
540            } else {
541                // Keep the stored private key around -- delete all other entry types
542                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
543                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
544            }
545
546            // Store the leaf certificate
547            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
548                    mUid, flags);
549            if (errorCode != KeyStore.NO_ERROR) {
550                throw new KeyStoreException("Failed to store certificate #0",
551                        KeyStore.getKeyStoreException(errorCode));
552            }
553
554            // Store the certificate chain
555            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
556                    mUid, flags);
557            if (errorCode != KeyStore.NO_ERROR) {
558                throw new KeyStoreException("Failed to store certificate chain",
559                        KeyStore.getKeyStoreException(errorCode));
560            }
561            success = true;
562        } finally {
563            if (!success) {
564                if (shouldReplacePrivateKey) {
565                    Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
566                } else {
567                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
568                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
569                }
570            }
571        }
572    }
573
574    private void setSecretKeyEntry(String entryAlias, SecretKey key,
575            java.security.KeyStore.ProtectionParameter param)
576            throws KeyStoreException {
577        if ((param != null) && (!(param instanceof KeyProtection))) {
578            throw new KeyStoreException(
579                    "Unsupported protection parameter class: " + param.getClass().getName()
580                    + ". Supported: " + KeyProtection.class.getName());
581        }
582        KeyProtection params = (KeyProtection) param;
583
584        if (key instanceof AndroidKeyStoreSecretKey) {
585            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
586            // overwrite its own entry.
587            String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
588            if (keyAliasInKeystore == null) {
589                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
590            }
591            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
592                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
593                        + keyAliasInKeystore);
594            }
595            String keyEntryAlias =
596                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
597            if (!entryAlias.equals(keyEntryAlias)) {
598                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
599                        + " alias: " + entryAlias + " != " + keyEntryAlias);
600            }
601            // This is the entry where this key is already stored. No need to do anything.
602            if (params != null) {
603                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
604                        + " parameters not supported");
605            }
606            return;
607        }
608
609        if (params == null) {
610            throw new KeyStoreException(
611                    "Protection parameters must be specified when importing a symmetric key");
612        }
613
614        // Not a KeyStore-backed secret key -- import its key material into keystore.
615        String keyExportFormat = key.getFormat();
616        if (keyExportFormat == null) {
617            throw new KeyStoreException(
618                    "Only secret keys that export their key material are supported");
619        } else if (!"RAW".equals(keyExportFormat)) {
620            throw new KeyStoreException(
621                    "Unsupported secret key material export format: " + keyExportFormat);
622        }
623        byte[] keyMaterial = key.getEncoded();
624        if (keyMaterial == null) {
625            throw new KeyStoreException("Key did not export its key material despite supporting"
626                    + " RAW format export");
627        }
628
629        KeymasterArguments args = new KeymasterArguments();
630        try {
631            int keymasterAlgorithm =
632                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
633            args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
634
635            int[] keymasterDigests;
636            if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
637                // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
638                // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
639                // digest, we don't let import parameters override the digest implied by the key.
640                // If the parameters specify digests at all, they must specify only one digest, the
641                // only implied by key algorithm.
642                int keymasterImpliedDigest =
643                        KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
644                if (keymasterImpliedDigest == -1) {
645                    throw new ProviderException(
646                            "HMAC key algorithm digest unknown for key algorithm "
647                                    + key.getAlgorithm());
648                }
649                keymasterDigests = new int[] {keymasterImpliedDigest};
650                if (params.isDigestsSpecified()) {
651                    // Digest(s) explicitly specified in params -- check that the list consists of
652                    // exactly one digest, the one implied by key algorithm.
653                    int[] keymasterDigestsFromParams =
654                            KeyProperties.Digest.allToKeymaster(params.getDigests());
655                    if ((keymasterDigestsFromParams.length != 1)
656                            || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
657                        throw new KeyStoreException(
658                                "Unsupported digests specification: "
659                                + Arrays.asList(params.getDigests()) + ". Only "
660                                + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
661                                + " supported for HMAC key algorithm " + key.getAlgorithm());
662                    }
663                }
664            } else {
665                // Key algorithm does not imply a digest.
666                if (params.isDigestsSpecified()) {
667                    keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
668                } else {
669                    keymasterDigests = EmptyArray.INT;
670                }
671            }
672            args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
673
674            @KeyProperties.PurposeEnum int purposes = params.getPurposes();
675            int[] keymasterBlockModes =
676                    KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
677            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
678                    && (params.isRandomizedEncryptionRequired())) {
679                for (int keymasterBlockMode : keymasterBlockModes) {
680                    if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
681                            keymasterBlockMode)) {
682                        throw new KeyStoreException(
683                                "Randomized encryption (IND-CPA) required but may be violated by"
684                                + " block mode: "
685                                + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
686                                + ". See KeyProtection documentation.");
687                    }
688                }
689            }
690            args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
691                    KeyProperties.Purpose.allToKeymaster(purposes));
692            args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
693            if (params.getSignaturePaddings().length > 0) {
694                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
695            }
696            int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
697                    params.getEncryptionPaddings());
698            args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
699            KeymasterUtils.addUserAuthArgs(args,
700                    params.isUserAuthenticationRequired(),
701                    params.getUserAuthenticationValidityDurationSeconds(),
702                    params.isUserAuthenticationValidWhileOnBody(),
703                    params.isInvalidatedByBiometricEnrollment(),
704                    params.getBoundToSpecificSecureUserId());
705            KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
706                    args,
707                    keymasterAlgorithm,
708                    keymasterBlockModes,
709                    keymasterDigests);
710            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
711                    params.getKeyValidityStart());
712            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
713                    params.getKeyValidityForOriginationEnd());
714            args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
715                    params.getKeyValidityForConsumptionEnd());
716
717            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
718                    && (!params.isRandomizedEncryptionRequired())) {
719                // Permit caller-provided IV when encrypting with this key
720                args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
721            }
722        } catch (IllegalArgumentException | IllegalStateException e) {
723            throw new KeyStoreException(e);
724        }
725        int flags = 0;
726        if (params.isCriticalToDeviceEncryption()) {
727            flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
728        }
729
730        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
731        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
732        int errorCode = mKeyStore.importKey(
733                keyAliasInKeystore,
734                args,
735                KeymasterDefs.KM_KEY_FORMAT_RAW,
736                keyMaterial,
737                mUid,
738                flags,
739                new KeyCharacteristics());
740        if (errorCode != KeyStore.NO_ERROR) {
741            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
742                + errorCode);
743        }
744    }
745
746    @Override
747    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
748            throws KeyStoreException {
749        throw new KeyStoreException("Operation not supported because key encoding is unknown");
750    }
751
752    @Override
753    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
754        if (isKeyEntry(alias)) {
755            throw new KeyStoreException("Entry exists and is not a trusted certificate");
756        }
757
758        // We can't set something to null.
759        if (cert == null) {
760            throw new NullPointerException("cert == null");
761        }
762
763        final byte[] encoded;
764        try {
765            encoded = cert.getEncoded();
766        } catch (CertificateEncodingException e) {
767            throw new KeyStoreException(e);
768        }
769
770        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
771            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
772        }
773    }
774
775    @Override
776    public void engineDeleteEntry(String alias) throws KeyStoreException {
777        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
778            throw new KeyStoreException("Failed to delete entry: " + alias);
779        }
780    }
781
782    private Set<String> getUniqueAliases() {
783        final String[] rawAliases = mKeyStore.list("", mUid);
784        if (rawAliases == null) {
785            return new HashSet<String>();
786        }
787
788        final Set<String> aliases = new HashSet<String>(rawAliases.length);
789        for (String alias : rawAliases) {
790            final int idx = alias.indexOf('_');
791            if ((idx == -1) || (alias.length() <= idx)) {
792                Log.e(NAME, "invalid alias: " + alias);
793                continue;
794            }
795
796            aliases.add(new String(alias.substring(idx + 1)));
797        }
798
799        return aliases;
800    }
801
802    @Override
803    public Enumeration<String> engineAliases() {
804        return Collections.enumeration(getUniqueAliases());
805    }
806
807    @Override
808    public boolean engineContainsAlias(String alias) {
809        if (alias == null) {
810            throw new NullPointerException("alias == null");
811        }
812
813        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
814                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
815                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
816                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
817    }
818
819    @Override
820    public int engineSize() {
821        return getUniqueAliases().size();
822    }
823
824    @Override
825    public boolean engineIsKeyEntry(String alias) {
826        return isKeyEntry(alias);
827    }
828
829    private boolean isKeyEntry(String alias) {
830        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
831    }
832
833    private boolean isPrivateKeyEntry(String alias) {
834        if (alias == null) {
835            throw new NullPointerException("alias == null");
836        }
837
838        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
839    }
840
841    private boolean isSecretKeyEntry(String alias) {
842        if (alias == null) {
843            throw new NullPointerException("alias == null");
844        }
845
846        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
847    }
848
849    private boolean isCertificateEntry(String alias) {
850        if (alias == null) {
851            throw new NullPointerException("alias == null");
852        }
853
854        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
855    }
856
857    @Override
858    public boolean engineIsCertificateEntry(String alias) {
859        return !isKeyEntry(alias) && isCertificateEntry(alias);
860    }
861
862    @Override
863    public String engineGetCertificateAlias(Certificate cert) {
864        if (cert == null) {
865            return null;
866        }
867        if (!"X.509".equalsIgnoreCase(cert.getType())) {
868            // Only X.509 certificates supported
869            return null;
870        }
871        byte[] targetCertBytes;
872        try {
873            targetCertBytes = cert.getEncoded();
874        } catch (CertificateEncodingException e) {
875            return null;
876        }
877        if (targetCertBytes == null) {
878            return null;
879        }
880
881        final Set<String> nonCaEntries = new HashSet<String>();
882
883        /*
884         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
885         * says to only compare the first certificate in the chain which is
886         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
887         * convention.
888         */
889        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
890        if (certAliases != null) {
891            for (String alias : certAliases) {
892                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
893                if (certBytes == null) {
894                    continue;
895                }
896
897                nonCaEntries.add(alias);
898
899                if (Arrays.equals(certBytes, targetCertBytes)) {
900                    return alias;
901                }
902            }
903        }
904
905        /*
906         * Look at all the TrustedCertificateEntry types. Skip all the
907         * PrivateKeyEntry we looked at above.
908         */
909        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
910        if (certAliases != null) {
911            for (String alias : caAliases) {
912                if (nonCaEntries.contains(alias)) {
913                    continue;
914                }
915
916                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
917                if (certBytes == null) {
918                    continue;
919                }
920
921                if (Arrays.equals(certBytes, targetCertBytes)) {
922                    return alias;
923                }
924            }
925        }
926
927        return null;
928    }
929
930    @Override
931    public void engineStore(OutputStream stream, char[] password) throws IOException,
932            NoSuchAlgorithmException, CertificateException {
933        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
934    }
935
936    @Override
937    public void engineLoad(InputStream stream, char[] password) throws IOException,
938            NoSuchAlgorithmException, CertificateException {
939        if (stream != null) {
940            throw new IllegalArgumentException("InputStream not supported");
941        }
942
943        if (password != null) {
944            throw new IllegalArgumentException("password not supported");
945        }
946
947        // Unfortunate name collision.
948        mKeyStore = KeyStore.getInstance();
949        mUid = KeyStore.UID_SELF;
950    }
951
952    @Override
953    public void engineLoad(LoadStoreParameter param) throws IOException,
954            NoSuchAlgorithmException, CertificateException {
955        int uid = KeyStore.UID_SELF;
956        if (param != null) {
957            if (param instanceof AndroidKeyStoreLoadStoreParameter) {
958                uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
959            } else {
960                throw new IllegalArgumentException(
961                        "Unsupported param type: " + param.getClass());
962            }
963        }
964        mKeyStore = KeyStore.getInstance();
965        mUid = uid;
966    }
967
968    @Override
969    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
970            throws KeyStoreException {
971        if (entry == null) {
972            throw new KeyStoreException("entry == null");
973        }
974
975        Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
976
977        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
978            java.security.KeyStore.TrustedCertificateEntry trE =
979                    (java.security.KeyStore.TrustedCertificateEntry) entry;
980            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
981            return;
982        }
983
984        if (entry instanceof PrivateKeyEntry) {
985            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
986            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
987        } else if (entry instanceof SecretKeyEntry) {
988            SecretKeyEntry secE = (SecretKeyEntry) entry;
989            setSecretKeyEntry(alias, secE.getSecretKey(), param);
990        } else {
991            throw new KeyStoreException(
992                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
993                    + "; was " + entry);
994        }
995    }
996
997    /**
998     * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
999     * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
1000     * can find out which keystore private key entry to use. This is needed so that Android Keystore
1001     * crypto operations using public keys can find out which key alias to use. These operations
1002     * require an alias.
1003     */
1004    static class KeyStoreX509Certificate extends DelegatingX509Certificate {
1005        private final String mPrivateKeyAlias;
1006        private final int mPrivateKeyUid;
1007        KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
1008                X509Certificate delegate) {
1009            super(delegate);
1010            mPrivateKeyAlias = privateKeyAlias;
1011            mPrivateKeyUid = privateKeyUid;
1012        }
1013
1014        @Override
1015        public PublicKey getPublicKey() {
1016            PublicKey original = super.getPublicKey();
1017            return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
1018                    mPrivateKeyAlias, mPrivateKeyUid,
1019                    original.getAlgorithm(), original.getEncoded());
1020        }
1021    }
1022}
1023