AndroidKeyStoreSpi.java revision adef49640d6339e6b4a6ad736c5815e35d9b8803
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;
20dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.Credentials;
213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubinimport android.security.KeyStore;
22dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStoreParameter;
23baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeyCharacteristics;
24baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterArguments;
25baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterDefs;
263f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProperties;
273f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProtection;
28e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log;
29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
30e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream;
31e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException;
32e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream;
33e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream;
34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key;
352eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.Entry;
363876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubinimport java.security.KeyStore.LoadStoreParameter;
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;
873876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    private int mUid = KeyStore.UID_SELF;
88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
91e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException {
92baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (isPrivateKeyEntry(alias)) {
934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
944a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
953876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mKeyStore, privateKeyAlias, mUid);
96baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (isSecretKeyEntry(alias)) {
974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
993876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mKeyStore, secretKeyAlias, mUid);
1004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        } else {
1014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            // Key not found
1024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
107e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate[] engineGetCertificateChain(String alias) {
108a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
109a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
110a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
111a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (leaf == null) {
114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Certificate[] caList;
118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
1193876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (caBytes != null) {
121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Collection<X509Certificate> caChain = toCertificates(caBytes);
122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[caChain.size() + 1];
124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Iterator<X509Certificate> it = caChain.iterator();
126e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int i = 1;
127e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            while (it.hasNext()) {
128e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                caList[i++] = it.next();
129e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
130e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
131e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[1];
132e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        caList[0] = leaf;
135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return caList;
137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
140e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate engineGetCertificate(String alias) {
141a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
142a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
143a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
144a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
1453876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
146903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (encodedCert != null) {
147903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return getCertificateForPrivateKeyEntry(alias, encodedCert);
148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
149e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
1503876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
151903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (encodedCert != null) {
152903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return getCertificateForTrustedCertificateEntry(encodedCert);
153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
154e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
155903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // This entry/alias does not contain a certificate.
156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
158e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
159903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
160903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
161903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // there's no need to wrap this certificate as opposed to the certificate associated with
162903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // a private key entry.
163903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        return toCertificate(encodedCert);
164903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    }
165903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
166903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
167903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // All crypto algorithms offered by Android Keystore for its private keys must also
168903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // be offered for the corresponding public keys stored in the Android Keystore. The
169903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // complication is that the underlying keystore service operates only on full key pairs,
170903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // rather than just public keys or private keys. As a result, Android Keystore-backed
171903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // crypto can only be offered for public keys for which keystore contains the
172903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // corresponding private key. This is not the case for certificate-only entries (e.g.,
173903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // trusted certificates).
174903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        //
175903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // getCertificate().getPublicKey() is the only way to obtain the public key
176903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
177903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // that the returned public key points to the underlying key pair / private key
178903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        // when available.
179903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
180903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        X509Certificate cert = toCertificate(encodedCert);
181903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        if (cert == null) {
182903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // Failed to parse the certificate.
183903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return null;
184903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        }
185903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
186903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
1873876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (mKeyStore.contains(privateKeyAlias, mUid)) {
188903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // As expected, keystore contains the private key corresponding to this public key. Wrap
189903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // the certificate so that its getPublicKey method returns an Android Keystore
190903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // PublicKey. This key will delegate crypto operations involving this public key to
191903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // Android Keystore when higher-priority providers do not offer these crypto
192903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // operations for this key.
1933876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
194903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        } else {
195903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // This KeyStore entry/alias is supposed to contain the private key corresponding to
196903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // the public key in this certificate, but it does not for some reason. It's probably a
197903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // bug. Let other providers handle crypto operations involving the public key returned
198903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            // by this certificate's getPublicKey.
199903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin            return cert;
200903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin        }
201903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin    }
202903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin
2034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    /**
2044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
2054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * returned by the certificate contains information about the alias of the private key in
2064a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * keystore. This is needed so that Android Keystore crypto operations using public keys can
2074a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * find out which key alias to use. These operations cannot work without an alias.
2084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     */
2094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
2103876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            String privateKeyAlias, int uid, X509Certificate certificate) {
2114a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        return (certificate != null)
2123876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
2134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    }
2144a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin
215e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static X509Certificate toCertificate(byte[] bytes) {
216e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
217e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
2184a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return (X509Certificate) certFactory.generateCertificate(
2194a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                    new ByteArrayInputStream(bytes));
220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
221e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificate in keystore", e);
222e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
223e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
224e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
225e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
226e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @SuppressWarnings("unchecked")
227e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
228e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
229e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
2304a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return (Collection<X509Certificate>) certFactory.generateCertificates(
2314a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                            new ByteArrayInputStream(bytes));
232e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
233e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificates in keystore", e);
234e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new ArrayList<X509Certificate>();
235e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
236e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
237e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
238e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Date getModificationDate(String alias) {
2393876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final long epochMillis = mKeyStore.getmtime(alias, mUid);
240e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (epochMillis == -1L) {
241e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
242e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
243e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
244e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return new Date(epochMillis);
245e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
248e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Date engineGetCreationDate(String alias) {
249a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
250a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
251a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
252a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
257e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
258baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
259baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (d != null) {
260baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            return d;
261baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
262baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getModificationDate(Credentials.CA_CERTIFICATE + alias);
269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((password != null) && (password.length > 0)) {
275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("entries cannot be protected with passwords");
276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
278e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (key instanceof PrivateKey) {
2792eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
280baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (key instanceof SecretKey) {
281baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            setSecretKeyEntry(alias, (SecretKey) key, null);
282e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
283baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
286e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
2873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
2883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throws KeyStoreException {
2893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        String keyAlgorithm = key.getAlgorithm();
2903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        KeyProtection.Builder specBuilder;
2913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
2923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder =
2933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    new KeyProtection.Builder(
2943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
295dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            // Authorized to be used with any digest (including no digest).
296e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            // MD5 was never offered for Android Keystore for ECDSA.
297e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            specBuilder.setDigests(
298e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_NONE,
299e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA1,
300e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA224,
301e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA256,
302e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA384,
303e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA512);
3043ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
3053ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder =
3063ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    new KeyProtection.Builder(
3073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.PURPOSE_ENCRYPT
3083ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_DECRYPT
3093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_SIGN
3103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            | KeyProperties.PURPOSE_VERIFY);
311dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin            // Authorized to be used with any digest (including no digest).
312e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            specBuilder.setDigests(
313e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_NONE,
314e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_MD5,
315e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA1,
316e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA224,
317e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA256,
318e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA384,
319e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.DIGEST_SHA512);
320e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            // Authorized to be used with any encryption and signature padding
321e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            // schemes (including no padding).
3223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder.setEncryptionPaddings(
323e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.ENCRYPTION_PADDING_NONE,
324e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
325e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
326e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            specBuilder.setSignaturePaddings(
327e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
328e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin                    KeyProperties.SIGNATURE_PADDING_RSA_PSS);
329e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            // Disable randomized encryption requirement to support encryption
330e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin            // padding NONE above.
3313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            specBuilder.setRandomizedEncryptionRequired(false);
3323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
3333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
3343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
3353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        specBuilder.setUserAuthenticationRequired(false);
3363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
3373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        return specBuilder.build();
3383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin    }
3393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
3402eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
3413f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
34296481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin        int flags = 0;
3433f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        KeyProtection spec;
3443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (param == null) {
3453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            spec = getLegacyKeyProtectionParameter(key);
3463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else if (param instanceof KeyStoreParameter) {
3473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            spec = getLegacyKeyProtectionParameter(key);
3483f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
3493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (legacySpec.isEncryptionRequired()) {
3503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                flags = KeyStore.FLAG_ENCRYPTED;
3513f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            }
3523f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        } else if (param instanceof KeyProtection) {
3533f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            spec = (KeyProtection) param;
3543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
3553f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
3563f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class:" + param.getClass().getName()
3573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName() + ", "
3583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + KeyStoreParameter.class.getName());
359e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
360e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
361e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Make sure the chain exists since this is a PrivateKey
362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((chain == null) || (chain.length == 0)) {
363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
366e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Do chain type checking.
367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        X509Certificate[] x509chain = new X509Certificate[chain.length];
368e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (int i = 0; i < chain.length; i++) {
369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!"X.509".equals(chain[i].getType())) {
370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!(chain[i] instanceof X509Certificate)) {
375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            x509chain[i] = (X509Certificate) chain[i];
380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] userCertBytes;
383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            userCertBytes = x509chain[0].getEncoded();
385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
3863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throw new KeyStoreException("Failed to encode certificate #0", e);
387e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
389e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * If we have a chain, store it in the CA certificate slot for this
391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * alias as concatenated DER-encoded certificates. These can be
392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * deserialized by {@link CertificateFactory#generateCertificates}.
393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] chainBytes;
395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (chain.length > 1) {
396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * so we only need the certificates starting at index 1.
399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[][] certsBytes = new byte[x509chain.length - 1][];
401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int totalCertLength = 0;
402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                try {
404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    certsBytes[i] = x509chain[i + 1].getEncoded();
405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    totalCertLength += certsBytes[i].length;
406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                } catch (CertificateEncodingException e) {
4073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
408e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                }
409e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
410e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
411e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
412e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * Serialize this into one byte array so we can later call
413e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * CertificateFactory#generateCertificates to recover them.
414e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
415e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = new byte[totalCertLength];
416e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int outputOffset = 0;
417e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
418e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                final int certLength = certsBytes[i].length;
419e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
420e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                outputOffset += certLength;
421e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                certsBytes[i] = null;
422e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
423e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
424e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = null;
425e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
426e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
4273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final String pkeyAlias;
4284a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (key instanceof AndroidKeyStorePrivateKey) {
4293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
430802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
4313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = null;
4323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
4333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        byte[] pkcs8EncodedPrivateKeyBytes;
4353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        KeymasterArguments importArgs;
4363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final boolean shouldReplacePrivateKey;
4373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
4383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
4393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!alias.equals(keySubalias)) {
4403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Can only replace keys with same alias: " + alias
4413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " != " + keySubalias);
4423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = false;
4443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = null;
4453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = null;
4463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
4473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = true;
4483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure the PrivateKey format is the one we support.
4493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keyFormat = key.getFormat();
4503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
4513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException(
4523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        "Unsupported private key export format: " + keyFormat
4533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + ". Only private keys which export their key material in PKCS#8 format are"
4543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " supported.");
4553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4563ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure we can actually encode the key.
4583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
4593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (pkcs8EncodedPrivateKeyBytes == null) {
4603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Private key did not export any key material");
4613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = new KeymasterArguments();
4643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            try {
465ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
4663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
4673ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                key.getAlgorithm()));
4683ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
469ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
4703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.Purpose.allToKeymaster(purposes));
4713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (spec.isDigestsSpecified()) {
472ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                    importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
4733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
4743ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
4753ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
476ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
4773ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
4783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int[] keymasterEncryptionPaddings =
4793ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.EncryptionPadding.allToKeymaster(
4803ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                spec.getEncryptionPaddings());
4813ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
4823ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        && (spec.isRandomizedEncryptionRequired())) {
4833ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    for (int keymasterPadding : keymasterEncryptionPaddings) {
4843ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        if (!KeymasterUtils
4853ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
4863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                        keymasterPadding)) {
4873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            throw new KeyStoreException(
4883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    "Randomized encryption (IND-CPA) required but is violated by"
4893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + " encryption padding mode: "
4903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + KeyProperties.EncryptionPadding.fromKeymaster(
4913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                            keymasterPadding)
4923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + ". See KeyProtection documentation.");
4933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        }
4943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    }
4953ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
496ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
497ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
4983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
4993ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeymasterUtils.addUserAuthArgs(importArgs,
5003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        spec.isUserAuthenticationRequired(),
501adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                        spec.getUserAuthenticationValidityDurationSeconds(),
502adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                        spec.isUserAuthenticationValidWhileOnBody());
503d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
504d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityStart());
505d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
506d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForOriginationEnd());
507d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
508d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForConsumptionEnd());
509b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            } catch (IllegalArgumentException | IllegalStateException e) {
510b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException(e);
5113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
5133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        boolean success = false;
5163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        try {
5173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the private key, if necessary
5183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (shouldReplacePrivateKey) {
5193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Delete the stored private key and any related entries before importing the
5203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // provided key
5213876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
5223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
5233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int errorCode = mKeyStore.importKey(
5243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        Credentials.USER_PRIVATE_KEY + alias,
5253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        importArgs,
5263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
5273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        pkcs8EncodedPrivateKeyBytes,
5283876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                        mUid,
5293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        flags,
5303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        resultingKeyCharacteristics);
5313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (errorCode != KeyStore.NO_ERROR) {
5323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to store private key",
5333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyStore.getKeyStoreException(errorCode));
5343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            } else {
5363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Keep the stored private key around -- delete all other entry types
5373876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
5383876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
5393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the leaf certificate
5423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
5433876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mUid, flags);
5443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate #0",
5463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the certificate chain
5503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
5513876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mUid, flags);
5523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate chain",
5543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5563ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            success = true;
5573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } finally {
5583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!success) {
5593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (shouldReplacePrivateKey) {
5603876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
5613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                } else {
5623876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
5633876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
5643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5653ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
566e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
567e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
568e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
5693f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin    private void setSecretKeyEntry(String entryAlias, SecretKey key,
5703f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            java.security.KeyStore.ProtectionParameter param)
571baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throws KeyStoreException {
5723f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        if ((param != null) && (!(param instanceof KeyProtection))) {
5733f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
5743f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class: " + param.getClass().getName()
5753f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName());
5763f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        }
5773f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        KeyProtection params = (KeyProtection) param;
5783f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin
579dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin        if (key instanceof AndroidKeyStoreSecretKey) {
580baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
581baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // overwrite its own entry.
582dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin            String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
583baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (keyAliasInKeystore == null) {
584baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
585baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
586baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
587baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
588baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + keyAliasInKeystore);
589baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
590baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            String keyEntryAlias =
591baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
592baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!entryAlias.equals(keyEntryAlias)) {
593baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
594baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " alias: " + entryAlias + " != " + keyEntryAlias);
595baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
596baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // This is the entry where this key is already stored. No need to do anything.
597baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (params != null) {
598baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
599baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " parameters not supported");
600baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
601baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            return;
602baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
603baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
604baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (params == null) {
605baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
606baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Protection parameters must be specified when importing a symmetric key");
607baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
608baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
609baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        // Not a KeyStore-backed secret key -- import its key material into keystore.
610baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyExportFormat = key.getFormat();
611baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyExportFormat == null) {
612baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
613baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Only secret keys that export their key material are supported");
614baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (!"RAW".equals(keyExportFormat)) {
615baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
616baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Unsupported secret key material export format: " + keyExportFormat);
617baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
618baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        byte[] keyMaterial = key.getEncoded();
619baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyMaterial == null) {
620baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Key did not export its key material despite supporting"
621baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + " RAW format export");
622baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
623baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
624baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        KeymasterArguments args = new KeymasterArguments();
625b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        try {
626b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int keymasterAlgorithm =
627b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
628b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
629b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
630b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterDigests;
631c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin            if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
632c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
633c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
634c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // digest, we don't let import parameters override the digest implied by the key.
635c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // If the parameters specify digests at all, they must specify only one digest, the
636c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // only implied by key algorithm.
637c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                int keymasterImpliedDigest =
638c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
639c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (keymasterImpliedDigest == -1) {
640c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    throw new ProviderException(
641c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            "HMAC key algorithm digest unknown for key algorithm "
642c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                    + key.getAlgorithm());
643c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                }
644c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                keymasterDigests = new int[] {keymasterImpliedDigest};
645c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
646c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // Digest(s) explicitly specified in params -- check that the list consists of
647c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // exactly one digest, the one implied by key algorithm.
648c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    int[] keymasterDigestsFromParams =
649c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            KeyProperties.Digest.allToKeymaster(params.getDigests());
650c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    if ((keymasterDigestsFromParams.length != 1)
651c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
652c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        throw new KeyStoreException(
653c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                "Unsupported digests specification: "
654c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + Arrays.asList(params.getDigests()) + ". Only "
655c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
656c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + " supported for HMAC key algorithm " + key.getAlgorithm());
657cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                    }
658cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                }
659c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            } else {
660c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // Key algorithm does not imply a digest.
661c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
662c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
663b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                } else {
664b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    keymasterDigests = EmptyArray.INT;
665b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                }
666c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            }
667b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
668baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
669b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            @KeyProperties.PurposeEnum int purposes = params.getPurposes();
670b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterBlockModes =
671b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
672b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
673b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (params.isRandomizedEncryptionRequired())) {
674b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                for (int keymasterBlockMode : keymasterBlockModes) {
675b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
676b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                            keymasterBlockMode)) {
677b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                        throw new KeyStoreException(
678b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                "Randomized encryption (IND-CPA) required but may be violated by"
679b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + " block mode: "
680b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
681b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + ". See KeyProtection documentation.");
682b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    }
6835927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                }
684f853f649981ed3cf2f1fbf1363a0932e9736daf6Alex Klyubin            }
685b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
686b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.Purpose.allToKeymaster(purposes));
687b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
688b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (params.getSignaturePaddings().length > 0) {
689b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
690b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
691b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
692b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getEncryptionPaddings());
693b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
694b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            KeymasterUtils.addUserAuthArgs(args,
695b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.isUserAuthenticationRequired(),
696adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                    params.getUserAuthenticationValidityDurationSeconds(),
697adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                    params.isUserAuthenticationValidWhileOnBody());
698a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin            KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
699a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    args,
700a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterAlgorithm,
701a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterBlockModes,
702a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterDigests);
703b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
704b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityStart());
705b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
706b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForOriginationEnd());
707b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
708b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForConsumptionEnd());
709b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
710b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
711b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (!params.isRandomizedEncryptionRequired())) {
712b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                // Permit caller-provided IV when encrypting with this key
713b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
714b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
715b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        } catch (IllegalArgumentException | IllegalStateException e) {
716b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            throw new KeyStoreException(e);
717b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
718b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
7193876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
720baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
721baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        int errorCode = mKeyStore.importKey(
722baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyAliasInKeystore,
723baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                args,
724baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                KeymasterDefs.KM_KEY_FORMAT_RAW,
725baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyMaterial,
7263876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                mUid,
72796481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin                0, // flags
728baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                new KeyCharacteristics());
7293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (errorCode != KeyStore.NO_ERROR) {
730baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
731baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                + errorCode);
732baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
733baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
734baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
735e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
736e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
737e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
738a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        throw new KeyStoreException("Operation not supported because key encoding is unknown");
739e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
740e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
741e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
742e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
743e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (isKeyEntry(alias)) {
744e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Entry exists and is not a trusted certificate");
745e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
746e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
747a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        // We can't set something to null.
748a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (cert == null) {
749a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("cert == null");
750a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
751a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
752e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] encoded;
753e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
754e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            encoded = cert.getEncoded();
755e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
756e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException(e);
757e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
758e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
7593876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
760e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
761e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
762e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
763e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
764e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
765e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineDeleteEntry(String alias) throws KeyStoreException {
7666c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin        if (!engineContainsAlias(alias)) {
767a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            return;
768a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
7696c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin        // At least one entry corresponding to this alias exists in keystore
770a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
7713876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
7726c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin            throw new KeyStoreException("Failed to delete entry: " + alias);
773e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
774e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
775e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
776e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Set<String> getUniqueAliases() {
7773876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] rawAliases = mKeyStore.list("", mUid);
778e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (rawAliases == null) {
779e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new HashSet<String>();
780e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
781e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
782e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> aliases = new HashSet<String>(rawAliases.length);
783e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : rawAliases) {
784e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final int idx = alias.indexOf('_');
785e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if ((idx == -1) || (alias.length() <= idx)) {
786e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                Log.e(NAME, "invalid alias: " + alias);
787e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
788e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
789e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
790e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            aliases.add(new String(alias.substring(idx + 1)));
791e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
792e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
793e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return aliases;
794e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
795e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
796e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
797e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Enumeration<String> engineAliases() {
798e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return Collections.enumeration(getUniqueAliases());
799e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
800e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
801e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
802e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineContainsAlias(String alias) {
803a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
804a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
805a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
806a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8073876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
8083876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
8093876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
8103876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
811e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
812e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
813e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
814e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public int engineSize() {
815e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getUniqueAliases().size();
816e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
817e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
818e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
819e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsKeyEntry(String alias) {
820e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return isKeyEntry(alias);
821e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
822e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
823e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private boolean isKeyEntry(String alias) {
824baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
825baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
826baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
827baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isPrivateKeyEntry(String alias) {
828a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
829a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
830a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
831a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8323876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
833e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
834e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
835baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isSecretKeyEntry(String alias) {
836baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (alias == null) {
837baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new NullPointerException("alias == null");
838baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
839baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
8403876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
841baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
842baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
843a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    private boolean isCertificateEntry(String alias) {
844a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
845a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
846a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
847a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8483876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
849a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    }
850a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
851e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
852e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsCertificateEntry(String alias) {
853a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return !isKeyEntry(alias) && isCertificateEntry(alias);
854e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
855e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
856e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
857e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public String engineGetCertificateAlias(Certificate cert) {
858e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (cert == null) {
859e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
860e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
8614a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (!"X.509".equalsIgnoreCase(cert.getType())) {
8624a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            // Only X.509 certificates supported
8634a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8644a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8654a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        byte[] targetCertBytes;
8664a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        try {
8674a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            targetCertBytes = cert.getEncoded();
8684a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        } catch (CertificateEncodingException e) {
8694a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8704a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8714a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (targetCertBytes == null) {
8724a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8734a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
874e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
875e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> nonCaEntries = new HashSet<String>();
876e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
877e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
878e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
879e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * says to only compare the first certificate in the chain which is
880e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
881e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * convention.
882e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
8833876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
88478ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
88578ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : certAliases) {
8863876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
88778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
88878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
88978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
890e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
89178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                nonCaEntries.add(alias);
892e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
8934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
89478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
89578ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
896e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
897e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
898e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
899e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
900e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * Look at all the TrustedCertificateEntry types. Skip all the
901e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * PrivateKeyEntry we looked at above.
902e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
9033876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
90478ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
90578ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : caAliases) {
90678ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (nonCaEntries.contains(alias)) {
90778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
90878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
909e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9103876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
91178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
91278ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
91378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
914e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9154a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
91678ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
91778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
918e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
919e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
920e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
921e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
922e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
923e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
924e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
925e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineStore(OutputStream stream, char[] password) throws IOException,
926e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
927e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
928e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
929e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
930e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
931e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineLoad(InputStream stream, char[] password) throws IOException,
932e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
933e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (stream != null) {
934e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("InputStream not supported");
935e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
936e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
937e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (password != null) {
938e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("password not supported");
939e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
940e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
941e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Unfortunate name collision.
9423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        mKeyStore = KeyStore.getInstance();
9433876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mUid = KeyStore.UID_SELF;
9443876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    }
9453876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin
9463876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    @Override
9473876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    public void engineLoad(LoadStoreParameter param) throws IOException,
9483876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            NoSuchAlgorithmException, CertificateException {
9493876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        int uid = KeyStore.UID_SELF;
9503876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (param != null) {
9513876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            if (param instanceof AndroidKeyStoreLoadStoreParameter) {
9523876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
9533876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            } else {
9543876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                throw new IllegalArgumentException(
9553876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                        "Unsupported param type: " + param.getClass());
9563876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            }
9573876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        }
9583876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mKeyStore = KeyStore.getInstance();
9593876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mUid = uid;
960e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
961e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9622eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    @Override
9632eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
9642eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throws KeyStoreException {
9652eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry == null) {
9662eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throw new KeyStoreException("entry == null");
9672eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9682eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9693876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
9702eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
9723ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            java.security.KeyStore.TrustedCertificateEntry trE =
9733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    (java.security.KeyStore.TrustedCertificateEntry) entry;
9742eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
9752eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            return;
9762eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9772eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9782eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry instanceof PrivateKeyEntry) {
9792eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
9803f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
981baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (entry instanceof SecretKeyEntry) {
982baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            SecretKeyEntry secE = (SecretKeyEntry) entry;
9833f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setSecretKeyEntry(alias, secE.getSecretKey(), param);
984baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else {
985baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
986baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
987baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + "; was " + entry);
9882eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9892eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    }
9902eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9914a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    /**
9924a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
9934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
9944a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * can find out which keystore private key entry to use. This is needed so that Android Keystore
9954a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * crypto operations using public keys can find out which key alias to use. These operations
9964a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * require an alias.
9974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     */
9984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    static class KeyStoreX509Certificate extends DelegatingX509Certificate {
9994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        private final String mPrivateKeyAlias;
10003876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        private final int mPrivateKeyUid;
10013876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
10023876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                X509Certificate delegate) {
10034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            super(delegate);
10044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            mPrivateKeyAlias = privateKeyAlias;
10053876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            mPrivateKeyUid = privateKeyUid;
10064a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
10074a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin
10084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        @Override
10094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        public PublicKey getPublicKey() {
10104a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            PublicKey original = super.getPublicKey();
10114a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
10123876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mPrivateKeyAlias, mPrivateKeyUid,
10133876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    original.getAlgorithm(), original.getEncoded());
10144a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
10154a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    }
1016e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root}
1017