AndroidKeyStoreSpi.java revision c58153b2d7418f44f2b0e397478be808e91decef
1e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/*
2e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Copyright (C) 2012 The Android Open Source Project
3e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
4e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * you may not use this file except in compliance with the License.
6e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * You may obtain a copy of the License at
7e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
8e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
10e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Unless required by applicable law or agreed to in writing, software
11e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * See the License for the specific language governing permissions and
14e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * limitations under the License.
15e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */
16e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
17dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinpackage android.security.keystore;
18e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
195927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport libcore.util.EmptyArray;
205927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin
21dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.Credentials;
223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubinimport android.security.KeyStore;
23dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStoreParameter;
24baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeyCharacteristics;
25baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterArguments;
26baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterDefs;
273f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProperties;
283f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProtection;
29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log;
30e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
31e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream;
32e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException;
33e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream;
34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream;
35e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key;
362eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.Entry;
372eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.PrivateKeyEntry;
382eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.ProtectionParameter;
39baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport java.security.KeyStore.SecretKeyEntry;
40e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreException;
41e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreSpi;
42e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.NoSuchAlgorithmException;
43e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.PrivateKey;
44c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubinimport java.security.ProviderException;
454a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubinimport java.security.PublicKey;
46e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.UnrecoverableKeyException;
47e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.Certificate;
48e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateEncodingException;
49e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateException;
50e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateFactory;
51e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.X509Certificate;
52e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.ArrayList;
535927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport java.util.Arrays;
54e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collection;
55e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collections;
56e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Date;
57e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Enumeration;
58e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.HashSet;
59e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Iterator;
60e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Set;
61e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
62baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport javax.crypto.SecretKey;
63baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
64e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/**
65db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * A java.security.KeyStore interface for the Android KeyStore. An instance of
66db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * it can be created via the {@link java.security.KeyStore#getInstance(String)
67e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
68e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
69e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
70e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * This is built on top of Android's keystore daemon. The convention of alias
71e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * use is:
72e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
73e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
74e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
75e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
76e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * entry which will have the rest of the chain concatenated in BER format.
77e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
78e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
79e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * with a single certificate.
80e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
81e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * @hide
82e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */
83dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinpublic class AndroidKeyStoreSpi extends KeyStoreSpi {
84e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public static final String NAME = "AndroidKeyStore";
85e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin    private KeyStore mKeyStore;
87e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException {
91baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (isPrivateKeyEntry(alias)) {
924a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
944a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                    mKeyStore, privateKeyAlias);
95baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (isSecretKeyEntry(alias)) {
964a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                    mKeyStore, secretKeyAlias);
994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        } else {
1004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            // Key not found
1014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
102e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate[] engineGetCertificateChain(String alias) {
107a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
108a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
109a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
110a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
111e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (leaf == null) {
113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Certificate[] caList;
117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
119e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (caBytes != null) {
120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Collection<X509Certificate> caChain = toCertificates(caBytes);
121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[caChain.size() + 1];
123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Iterator<X509Certificate> it = caChain.iterator();
125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int i = 1;
126e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            while (it.hasNext()) {
127e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                caList[i++] = it.next();
128e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
129e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
130e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[1];
131e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
132e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        caList[0] = leaf;
134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return caList;
136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate engineGetCertificate(String alias) {
140a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
141a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
142a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
143a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
144903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
145903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (encodedCert != null) {
146903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return getCertificateForPrivateKeyEntry(alias, encodedCert);
147e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
149903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
150903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (encodedCert != null) {
151903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return getCertificateForTrustedCertificateEntry(encodedCert);
152e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
154903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // This entry/alias does not contain a certificate.
155e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
158903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
159903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
160903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // there's no need to wrap this certificate as opposed to the certificate associated with
161903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // a private key entry.
162903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        return toCertificate(encodedCert);
163903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    }
164903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
165903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
166903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // All crypto algorithms offered by Android Keystore for its private keys must also
167903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // be offered for the corresponding public keys stored in the Android Keystore. The
168903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // complication is that the underlying keystore service operates only on full key pairs,
169903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // rather than just public keys or private keys. As a result, Android Keystore-backed
170903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // crypto can only be offered for public keys for which keystore contains the
171903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // corresponding private key. This is not the case for certificate-only entries (e.g.,
172903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // trusted certificates).
173903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        //
174903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // getCertificate().getPublicKey() is the only way to obtain the public key
175903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
176903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // that the returned public key points to the underlying key pair / private key
177903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // when available.
178903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
179903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        X509Certificate cert = toCertificate(encodedCert);
180903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (cert == null) {
181903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // Failed to parse the certificate.
182903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return null;
183903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        }
184903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
185903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
186903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (mKeyStore.contains(privateKeyAlias)) {
187903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // As expected, keystore contains the private key corresponding to this public key. Wrap
188903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // the certificate so that its getPublicKey method returns an Android Keystore
189903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // PublicKey. This key will delegate crypto operations involving this public key to
190903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // Android Keystore when higher-priority providers do not offer these crypto
191903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // operations for this key.
192903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
193903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        } else {
194903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // This KeyStore entry/alias is supposed to contain the private key corresponding to
195903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // the public key in this certificate, but it does not for some reason. It's probably a
196903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // bug. Let other providers handle crypto operations involving the public key returned
197903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // by this certificate's getPublicKey.
198903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return cert;
199903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        }
200903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    }
201903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
2024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    /**
2034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
2044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * returned by the certificate contains information about the alias of the private key in
2054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * keystore. This is needed so that Android Keystore crypto operations using public keys can
2064a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * find out which key alias to use. These operations cannot work without an alias.
2074a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     */
2084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
2094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            String privateKeyAlias, X509Certificate certificate) {
2104a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        return (certificate != null)
2114a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                ? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null;
2124a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    }
2134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin
214e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static X509Certificate toCertificate(byte[] bytes) {
215e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
216e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
2174a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return (X509Certificate) certFactory.generateCertificate(
2184a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                    new ByteArrayInputStream(bytes));
219e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificate in keystore", e);
221e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
222e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
223e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
224e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
225e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @SuppressWarnings("unchecked")
226e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
227e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
228e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
2294a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return (Collection<X509Certificate>) certFactory.generateCertificates(
2304a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                            new ByteArrayInputStream(bytes));
231e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
232e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificates in keystore", e);
233e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new ArrayList<X509Certificate>();
234e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
235e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
236e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
237e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Date getModificationDate(String alias) {
238e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final long epochMillis = mKeyStore.getmtime(alias);
239e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (epochMillis == -1L) {
240e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
241e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
242e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
243e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return new Date(epochMillis);
244e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
245e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Date engineGetCreationDate(String alias) {
248a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
249a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
250a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
251a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
252e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
257baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
258baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (d != null) {
259baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            return d;
260baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
261baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
262e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getModificationDate(Credentials.CA_CERTIFICATE + alias);
268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((password != null) && (password.length > 0)) {
274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("entries cannot be protected with passwords");
275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (key instanceof PrivateKey) {
2782eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
279baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (key instanceof SecretKey) {
280baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            setSecretKeyEntry(alias, (SecretKey) key, null);
281e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
282baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
283e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
2863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
2873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throws KeyStoreException {
2883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        String keyAlgorithm = key.getAlgorithm();
2893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        KeyProtection.Builder specBuilder;
2903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
2913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder =
2923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    new KeyProtection.Builder(
2933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
294dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            // Authorized to be used with any digest (including no digest).
295dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            specBuilder.setDigests(KeyProperties.DIGEST_NONE);
2963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
2973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder =
2983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    new KeyProtection.Builder(
2993ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.PURPOSE_ENCRYPT
3003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_DECRYPT
3013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_SIGN
3023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_VERIFY);
303dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            // Authorized to be used with any digest (including no digest).
304dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            specBuilder.setDigests(KeyProperties.DIGEST_NONE);
3052e3aaa7dc1c40c0145e8d52422f2fb65799a3a60Alex Klyubin            // Authorized to be used with any encryption and signature padding scheme (including no
3062e3aaa7dc1c40c0145e8d52422f2fb65799a3a60Alex Klyubin            // padding).
3073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder.setEncryptionPaddings(
308dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin                    KeyProperties.ENCRYPTION_PADDING_NONE);
3093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Disable randomized encryption requirement to support encryption padding NONE
3103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // above.
3113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder.setRandomizedEncryptionRequired(false);
3123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
3133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
3143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
3153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        specBuilder.setUserAuthenticationRequired(false);
3163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
3173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        return specBuilder.build();
3183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin    }
3193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
3202eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
3213f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
32296481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin        int flags = 0;
3233f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        KeyProtection spec;
3243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (param == null) {
3253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            spec = getLegacyKeyProtectionParameter(key);
3263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else if (param instanceof KeyStoreParameter) {
3273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            spec = getLegacyKeyProtectionParameter(key);
3283f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
3293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (legacySpec.isEncryptionRequired()) {
3303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                flags = KeyStore.FLAG_ENCRYPTED;
3313f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            }
3323f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        } else if (param instanceof KeyProtection) {
3333f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            spec = (KeyProtection) param;
3343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
3353f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
3363f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class:" + param.getClass().getName()
3373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName() + ", "
3383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + KeyStoreParameter.class.getName());
339e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
340e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
341e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Make sure the chain exists since this is a PrivateKey
342e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((chain == null) || (chain.length == 0)) {
343e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
344e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
345e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
346e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Do chain type checking.
347e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        X509Certificate[] x509chain = new X509Certificate[chain.length];
348e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (int i = 0; i < chain.length; i++) {
349e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!"X.509".equals(chain[i].getType())) {
350e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
351e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
352e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
353e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
354e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!(chain[i] instanceof X509Certificate)) {
355e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
356e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
357e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
358e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
359e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            x509chain[i] = (X509Certificate) chain[i];
360e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
361e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] userCertBytes;
363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            userCertBytes = x509chain[0].getEncoded();
365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
3663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throw new KeyStoreException("Failed to encode certificate #0", e);
367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
368e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * If we have a chain, store it in the CA certificate slot for this
371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * alias as concatenated DER-encoded certificates. These can be
372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * deserialized by {@link CertificateFactory#generateCertificates}.
373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] chainBytes;
375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (chain.length > 1) {
376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * so we only need the certificates starting at index 1.
379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[][] certsBytes = new byte[x509chain.length - 1][];
381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int totalCertLength = 0;
382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                try {
384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    certsBytes[i] = x509chain[i + 1].getEncoded();
385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    totalCertLength += certsBytes[i].length;
386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                } catch (CertificateEncodingException e) {
3873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                }
389e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * Serialize this into one byte array so we can later call
393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * CertificateFactory#generateCertificates to recover them.
394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = new byte[totalCertLength];
396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int outputOffset = 0;
397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                final int certLength = certsBytes[i].length;
399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                outputOffset += certLength;
401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                certsBytes[i] = null;
402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = null;
405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
4073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final String pkeyAlias;
4084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (key instanceof AndroidKeyStorePrivateKey) {
4093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
410802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
4113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = null;
4123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
4133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        byte[] pkcs8EncodedPrivateKeyBytes;
4153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        KeymasterArguments importArgs;
4163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final boolean shouldReplacePrivateKey;
4173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
4183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
4193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!alias.equals(keySubalias)) {
4203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Can only replace keys with same alias: " + alias
4213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " != " + keySubalias);
4223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = false;
4243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = null;
4253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = null;
4263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
4273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = true;
4283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure the PrivateKey format is the one we support.
4293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keyFormat = key.getFormat();
4303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
4313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException(
4323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        "Unsupported private key export format: " + keyFormat
4333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + ". Only private keys which export their key material in PKCS#8 format are"
4343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " supported.");
4353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure we can actually encode the key.
4383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
4393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (pkcs8EncodedPrivateKeyBytes == null) {
4403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Private key did not export any key material");
4413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = new KeymasterArguments();
4443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            try {
445ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
4463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
4473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                key.getAlgorithm()));
4483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
449ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
4503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.Purpose.allToKeymaster(purposes));
4513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (spec.isDigestsSpecified()) {
452ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                    importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
4533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
4543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
4553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
456ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
4573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
4583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int[] keymasterEncryptionPaddings =
4593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.EncryptionPadding.allToKeymaster(
4603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                spec.getEncryptionPaddings());
4613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
4623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        && (spec.isRandomizedEncryptionRequired())) {
4633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    for (int keymasterPadding : keymasterEncryptionPaddings) {
4643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        if (!KeymasterUtils
4653ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
4663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                        keymasterPadding)) {
4673ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            throw new KeyStoreException(
4683ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    "Randomized encryption (IND-CPA) required but is violated by"
4693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + " encryption padding mode: "
4703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + KeyProperties.EncryptionPadding.fromKeymaster(
4713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                            keymasterPadding)
4723ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + ". See KeyProtection documentation.");
4733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        }
4743ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    }
4753ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
476ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
477ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
4783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
4793ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeymasterUtils.addUserAuthArgs(importArgs,
4803ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        spec.isUserAuthenticationRequired(),
4813ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        spec.getUserAuthenticationValidityDurationSeconds());
482d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
483d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityStart());
484d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
485d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForOriginationEnd());
486d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
487d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForConsumptionEnd());
488b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            } catch (IllegalArgumentException | IllegalStateException e) {
489b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException(e);
4903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
4923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        boolean success = false;
4953ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        try {
4963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the private key, if necessary
4973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (shouldReplacePrivateKey) {
4983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Delete the stored private key and any related entries before importing the
4993ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // provided key
5003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
5013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
5023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int errorCode = mKeyStore.importKey(
5033ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        Credentials.USER_PRIVATE_KEY + alias,
5043ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        importArgs,
5053ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
5063ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        pkcs8EncodedPrivateKeyBytes,
5073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        flags,
5083ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        resultingKeyCharacteristics);
5093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (errorCode != KeyStore.NO_ERROR) {
5103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to store private key",
5113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyStore.getKeyStoreException(errorCode));
5123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            } else {
5143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Keep the stored private key around -- delete all other entry types
5153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
5163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
5173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the leaf certificate
5203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
5213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    KeyStore.UID_SELF, flags);
5223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate #0",
5243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the certificate chain
5283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
5293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    KeyStore.UID_SELF, flags);
5303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate chain",
5323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            success = true;
5353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } finally {
5363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!success) {
5373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (shouldReplacePrivateKey) {
5383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    Credentials.deleteAllTypesForAlias(mKeyStore, alias);
5393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                } else {
5403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
5413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
5423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
544e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
545e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
546e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
5473f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin    private void setSecretKeyEntry(String entryAlias, SecretKey key,
5483f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            java.security.KeyStore.ProtectionParameter param)
549baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throws KeyStoreException {
5503f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        if ((param != null) && (!(param instanceof KeyProtection))) {
5513f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
5523f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class: " + param.getClass().getName()
5533f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName());
5543f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        }
5553f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        KeyProtection params = (KeyProtection) param;
5563f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin
557dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin        if (key instanceof AndroidKeyStoreSecretKey) {
558baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
559baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // overwrite its own entry.
560dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin            String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
561baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (keyAliasInKeystore == null) {
562baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
563baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
564baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
565baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
566baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + keyAliasInKeystore);
567baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
568baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            String keyEntryAlias =
569baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
570baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!entryAlias.equals(keyEntryAlias)) {
571baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
572baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " alias: " + entryAlias + " != " + keyEntryAlias);
573baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
574baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // This is the entry where this key is already stored. No need to do anything.
575baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (params != null) {
576baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
577baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " parameters not supported");
578baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
579baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            return;
580baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
581baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
582baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (params == null) {
583baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
584baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Protection parameters must be specified when importing a symmetric key");
585baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
586baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
587baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        // Not a KeyStore-backed secret key -- import its key material into keystore.
588baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyExportFormat = key.getFormat();
589baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyExportFormat == null) {
590baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
591baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Only secret keys that export their key material are supported");
592baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (!"RAW".equals(keyExportFormat)) {
593baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
594baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Unsupported secret key material export format: " + keyExportFormat);
595baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
596baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        byte[] keyMaterial = key.getEncoded();
597baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyMaterial == null) {
598baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Key did not export its key material despite supporting"
599baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + " RAW format export");
600baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
601baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
602baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        KeymasterArguments args = new KeymasterArguments();
603b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        try {
604b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int keymasterAlgorithm =
605b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
606b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
607b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
608b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterDigests;
609c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin            if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
610c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
611c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
612c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // digest, we don't let import parameters override the digest implied by the key.
613c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // If the parameters specify digests at all, they must specify only one digest, the
614c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // only implied by key algorithm.
615c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                int keymasterImpliedDigest =
616c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
617c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (keymasterImpliedDigest == -1) {
618c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    throw new ProviderException(
619c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            "HMAC key algorithm digest unknown for key algorithm "
620c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                    + key.getAlgorithm());
621c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                }
622c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                keymasterDigests = new int[] {keymasterImpliedDigest};
623c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
624c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // Digest(s) explicitly specified in params -- check that the list consists of
625c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // exactly one digest, the one implied by key algorithm.
626c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    int[] keymasterDigestsFromParams =
627c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            KeyProperties.Digest.allToKeymaster(params.getDigests());
628c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    if ((keymasterDigestsFromParams.length != 1)
629c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
630c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        throw new KeyStoreException(
631c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                "Unsupported digests specification: "
632c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + Arrays.asList(params.getDigests()) + ". Only "
633c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
634c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + " supported for HMAC key algorithm " + key.getAlgorithm());
635cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                    }
636cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                }
637c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            } else {
638c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // Key algorithm does not imply a digest.
639c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
640c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
641b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                } else {
642b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    keymasterDigests = EmptyArray.INT;
643b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                }
644c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            }
645b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
646baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
647b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            @KeyProperties.PurposeEnum int purposes = params.getPurposes();
648b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterBlockModes =
649b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
650b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
651b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (params.isRandomizedEncryptionRequired())) {
652b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                for (int keymasterBlockMode : keymasterBlockModes) {
653b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
654b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                            keymasterBlockMode)) {
655b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                        throw new KeyStoreException(
656b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                "Randomized encryption (IND-CPA) required but may be violated by"
657b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + " block mode: "
658b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
659b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + ". See KeyProtection documentation.");
660b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    }
6615927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                }
662f853f649981ed3cf2f1fbf1363a0932e9736daf6Alex Klyubin            }
663b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
664b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.Purpose.allToKeymaster(purposes));
665b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
666b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (params.getSignaturePaddings().length > 0) {
667b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
668b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
669b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
670b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getEncryptionPaddings());
671b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
672b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            KeymasterUtils.addUserAuthArgs(args,
673b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.isUserAuthenticationRequired(),
674b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getUserAuthenticationValidityDurationSeconds());
675b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
676b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityStart());
677b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
678b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForOriginationEnd());
679b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
680b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForConsumptionEnd());
681b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
682b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
683b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (!params.isRandomizedEncryptionRequired())) {
684b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                // Permit caller-provided IV when encrypting with this key
685b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
686b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
687b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        } catch (IllegalArgumentException | IllegalStateException e) {
688b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            throw new KeyStoreException(e);
689b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
690b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
691baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
692baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
693baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        int errorCode = mKeyStore.importKey(
694baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyAliasInKeystore,
695baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                args,
696baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                KeymasterDefs.KM_KEY_FORMAT_RAW,
697baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyMaterial,
69896481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin                0, // flags
699baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                new KeyCharacteristics());
7003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (errorCode != KeyStore.NO_ERROR) {
701baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
702baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                + errorCode);
703baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
704baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
705baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
706e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
707e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
708e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
709a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        throw new KeyStoreException("Operation not supported because key encoding is unknown");
710e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
711e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
712e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
713e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
714e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (isKeyEntry(alias)) {
715e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Entry exists and is not a trusted certificate");
716e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
717e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
718a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        // We can't set something to null.
719a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (cert == null) {
720a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("cert == null");
721a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
722a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
723e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] encoded;
724e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
725e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            encoded = cert.getEncoded();
726e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
727e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException(e);
728e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
729e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
7302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
7313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
732e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
733e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
734e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
735e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
736e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
737e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineDeleteEntry(String alias) throws KeyStoreException {
7386c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin        if (!engineContainsAlias(alias)) {
739a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            return;
740a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
7416c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin        // At least one entry corresponding to this alias exists in keystore
742a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
743db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
7446c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin            throw new KeyStoreException("Failed to delete entry: " + alias);
745e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
746e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
747e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
748e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Set<String> getUniqueAliases() {
7494350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin        final String[] rawAliases = mKeyStore.list("");
750e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (rawAliases == null) {
751e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new HashSet<String>();
752e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
753e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
754e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> aliases = new HashSet<String>(rawAliases.length);
755e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : rawAliases) {
756e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final int idx = alias.indexOf('_');
757e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if ((idx == -1) || (alias.length() <= idx)) {
758e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                Log.e(NAME, "invalid alias: " + alias);
759e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
760e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
761e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
762e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            aliases.add(new String(alias.substring(idx + 1)));
763e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
764e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
765e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return aliases;
766e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
767e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
768e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
769e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Enumeration<String> engineAliases() {
770e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return Collections.enumeration(getUniqueAliases());
771e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
772e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
773e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
774e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineContainsAlias(String alias) {
775a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
776a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
777a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
778a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
779e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
780baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
781e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
782e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
783e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
784e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
785e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
786e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public int engineSize() {
787e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getUniqueAliases().size();
788e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
789e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
790e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
791e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsKeyEntry(String alias) {
792e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return isKeyEntry(alias);
793e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
794e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
795e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private boolean isKeyEntry(String alias) {
796baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
797baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
798baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
799baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isPrivateKeyEntry(String alias) {
800a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
801a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
802a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
803a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
804e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
805e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
806e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
807baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isSecretKeyEntry(String alias) {
808baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (alias == null) {
809baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new NullPointerException("alias == null");
810baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
811baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
812baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
813baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
814baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
815a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    private boolean isCertificateEntry(String alias) {
816a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
817a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
818a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
819a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
820a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
821a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    }
822a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
823e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
824e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsCertificateEntry(String alias) {
825a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return !isKeyEntry(alias) && isCertificateEntry(alias);
826e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
827e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
828e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
829e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public String engineGetCertificateAlias(Certificate cert) {
830e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (cert == null) {
831e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
832e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
8334a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (!"X.509".equalsIgnoreCase(cert.getType())) {
8344a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            // Only X.509 certificates supported
8354a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8364a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8374a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        byte[] targetCertBytes;
8384a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        try {
8394a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            targetCertBytes = cert.getEncoded();
8404a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        } catch (CertificateEncodingException e) {
8414a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8424a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8434a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (targetCertBytes == null) {
8444a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8454a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
846e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
847e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> nonCaEntries = new HashSet<String>();
848e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
849e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
850e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
851e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * says to only compare the first certificate in the chain which is
852e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
853e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * convention.
854e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
8554350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE);
85678ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
85778ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : certAliases) {
85878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
85978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
86078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
86178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
862e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
86378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                nonCaEntries.add(alias);
864e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
8654a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
86678ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
86778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
868e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
869e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
870e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
871e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
872e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * Look at all the TrustedCertificateEntry types. Skip all the
873e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * PrivateKeyEntry we looked at above.
874e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
8754350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE);
87678ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
87778ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : caAliases) {
87878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (nonCaEntries.contains(alias)) {
87978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
88078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
881e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
88278ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
88378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
88478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
88578ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
886e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
8874a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
88878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
88978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
890e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
891e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
892e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
893e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
894e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
895e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
896e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
897e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineStore(OutputStream stream, char[] password) throws IOException,
898e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
899e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
900e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
901e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
902e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
903e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineLoad(InputStream stream, char[] password) throws IOException,
904e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
905e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (stream != null) {
906e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("InputStream not supported");
907e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
908e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
909e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (password != null) {
910e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("password not supported");
911e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
912e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
913e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Unfortunate name collision.
9143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        mKeyStore = KeyStore.getInstance();
915e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
916e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9172eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    @Override
9182eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
9192eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throws KeyStoreException {
9202eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry == null) {
9212eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throw new KeyStoreException("entry == null");
9222eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9232eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9246c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
9252eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
9273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            java.security.KeyStore.TrustedCertificateEntry trE =
9283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    (java.security.KeyStore.TrustedCertificateEntry) entry;
9292eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
9302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            return;
9312eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9322eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9332eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry instanceof PrivateKeyEntry) {
9342eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
9353f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
936baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (entry instanceof SecretKeyEntry) {
937baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            SecretKeyEntry secE = (SecretKeyEntry) entry;
9383f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setSecretKeyEntry(alias, secE.getSecretKey(), param);
939baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else {
940baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
941baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
942baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + "; was " + entry);
9432eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9442eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    }
9452eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9464a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    /**
9474a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
9484a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
9494a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * can find out which keystore private key entry to use. This is needed so that Android Keystore
9504a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * crypto operations using public keys can find out which key alias to use. These operations
9514a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * require an alias.
9524a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     */
9534a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    static class KeyStoreX509Certificate extends DelegatingX509Certificate {
9544a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        private final String mPrivateKeyAlias;
9554a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) {
9564a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            super(delegate);
9574a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            mPrivateKeyAlias = privateKeyAlias;
9584a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
9594a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin
9604a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        @Override
9614a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        public PublicKey getPublicKey() {
9624a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            PublicKey original = super.getPublicKey();
9634a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
9644a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                    mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded());
9654a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
9664a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    }
967e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root}
968