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;
35412b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu            if (spec.isCriticalToDeviceEncryption()) {
35512b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu                flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
35612b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu            }
3573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
3583f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
3593f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class:" + param.getClass().getName()
3603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName() + ", "
3613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    + KeyStoreParameter.class.getName());
362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Make sure the chain exists since this is a PrivateKey
365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((chain == null) || (chain.length == 0)) {
366e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
368e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Do chain type checking.
370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        X509Certificate[] x509chain = new X509Certificate[chain.length];
371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (int i = 0; i < chain.length; i++) {
372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!"X.509".equals(chain[i].getType())) {
373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!(chain[i] instanceof X509Certificate)) {
378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            x509chain[i] = (X509Certificate) chain[i];
383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] userCertBytes;
386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
387e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            userCertBytes = x509chain[0].getEncoded();
388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
3893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            throw new KeyStoreException("Failed to encode certificate #0", e);
390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * If we have a chain, store it in the CA certificate slot for this
394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * alias as concatenated DER-encoded certificates. These can be
395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * deserialized by {@link CertificateFactory#generateCertificates}.
396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] chainBytes;
398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (chain.length > 1) {
399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * so we only need the certificates starting at index 1.
402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[][] certsBytes = new byte[x509chain.length - 1][];
404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int totalCertLength = 0;
405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                try {
407e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    certsBytes[i] = x509chain[i + 1].getEncoded();
408e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    totalCertLength += certsBytes[i].length;
409e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                } catch (CertificateEncodingException e) {
4103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
411e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                }
412e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
413e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
414e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
415e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * Serialize this into one byte array so we can later call
416e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * CertificateFactory#generateCertificates to recover them.
417e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
418e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = new byte[totalCertLength];
419e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int outputOffset = 0;
420e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
421e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                final int certLength = certsBytes[i].length;
422e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
423e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                outputOffset += certLength;
424e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                certsBytes[i] = null;
425e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
426e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
427e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = null;
428e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
429e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
4303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final String pkeyAlias;
4314a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (key instanceof AndroidKeyStorePrivateKey) {
4323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
433802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
4343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkeyAlias = null;
4353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
4363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        byte[] pkcs8EncodedPrivateKeyBytes;
4383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        KeymasterArguments importArgs;
4393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        final boolean shouldReplacePrivateKey;
4403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
4413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
4423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!alias.equals(keySubalias)) {
4433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Can only replace keys with same alias: " + alias
4443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " != " + keySubalias);
4453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = false;
4473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = null;
4483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = null;
4493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } else {
4503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            shouldReplacePrivateKey = true;
4513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure the PrivateKey format is the one we support.
4523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            final String keyFormat = key.getFormat();
4533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
4543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException(
4553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        "Unsupported private key export format: " + keyFormat
4563ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + ". Only private keys which export their key material in PKCS#8 format are"
4573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        + " supported.");
4583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Make sure we can actually encode the key.
4613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            pkcs8EncodedPrivateKeyBytes = key.getEncoded();
4623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (pkcs8EncodedPrivateKeyBytes == null) {
4633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Private key did not export any key material");
4643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
4653ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
4663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            importArgs = new KeymasterArguments();
4673ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            try {
468ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
4693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
4703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                key.getAlgorithm()));
4713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
472ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
4733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.Purpose.allToKeymaster(purposes));
4743ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (spec.isDigestsSpecified()) {
475ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                    importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
4763ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyProperties.Digest.allToKeymaster(spec.getDigests()));
4773ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
4783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
479ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
4803ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
4813ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int[] keymasterEncryptionPaddings =
4823ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.EncryptionPadding.allToKeymaster(
4833ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                spec.getEncryptionPaddings());
4843ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
4853ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        && (spec.isRandomizedEncryptionRequired())) {
4863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    for (int keymasterPadding : keymasterEncryptionPaddings) {
4873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        if (!KeymasterUtils
4883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
4893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                        keymasterPadding)) {
4903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            throw new KeyStoreException(
4913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    "Randomized encryption (IND-CPA) required but is violated by"
4923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + " encryption padding mode: "
4933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + KeyProperties.EncryptionPadding.fromKeymaster(
4943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                            keymasterPadding)
4953ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                                    + ". See KeyProtection documentation.");
4963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        }
4973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    }
4983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
499ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
500ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin                importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
5013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
5023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeymasterUtils.addUserAuthArgs(importArgs,
5033ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        spec.isUserAuthenticationRequired(),
504adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                        spec.getUserAuthenticationValidityDurationSeconds(),
505c38eae5229a820966008ae1885af90cd27c265e7Shawn Willden                        spec.isUserAuthenticationValidWhileOnBody(),
50659ced28f0f722d2517afc65d755ebb388902f76bRubin Xu                        spec.isInvalidatedByBiometricEnrollment(),
50759ced28f0f722d2517afc65d755ebb388902f76bRubin Xu                        spec.getBoundToSpecificSecureUserId());
508d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
509d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityStart());
510d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
511d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForOriginationEnd());
512d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
513d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin                        spec.getKeyValidityForConsumptionEnd());
514b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            } catch (IllegalArgumentException | IllegalStateException e) {
515b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException(e);
5163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        }
5183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        boolean success = false;
5213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        try {
5223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the private key, if necessary
5233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (shouldReplacePrivateKey) {
5243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Delete the stored private key and any related entries before importing the
5253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // provided key
5263876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
5273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
5283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                int errorCode = mKeyStore.importKey(
5293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        Credentials.USER_PRIVATE_KEY + alias,
5303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        importArgs,
5313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeymasterDefs.KM_KEY_FORMAT_PKCS8,
5323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        pkcs8EncodedPrivateKeyBytes,
5333876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                        mUid,
5343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        flags,
5353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        resultingKeyCharacteristics);
5363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (errorCode != KeyStore.NO_ERROR) {
5373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    throw new KeyStoreException("Failed to store private key",
5383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                            KeyStore.getKeyStoreException(errorCode));
5393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            } else {
5413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                // Keep the stored private key around -- delete all other entry types
5423876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
5433876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
5443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the leaf certificate
5473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
5483876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mUid, flags);
5493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate #0",
5513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin
5543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            // Store the certificate chain
5553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
5563876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mUid, flags);
5573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (errorCode != KeyStore.NO_ERROR) {
5583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                throw new KeyStoreException("Failed to store certificate chain",
5593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                        KeyStore.getKeyStoreException(errorCode));
5603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
5613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            success = true;
5623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        } finally {
5633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            if (!success) {
5643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                if (shouldReplacePrivateKey) {
5653876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
5663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                } else {
5673876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
5683876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
5693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                }
5703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            }
571e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
572e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
573e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
5743f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin    private void setSecretKeyEntry(String entryAlias, SecretKey key,
5753f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            java.security.KeyStore.ProtectionParameter param)
576baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throws KeyStoreException {
5773f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        if ((param != null) && (!(param instanceof KeyProtection))) {
5783f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            throw new KeyStoreException(
5793f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    "Unsupported protection parameter class: " + param.getClass().getName()
5803f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin                    + ". Supported: " + KeyProtection.class.getName());
5813f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        }
5823f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin        KeyProtection params = (KeyProtection) param;
5833f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin
584dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin        if (key instanceof AndroidKeyStoreSecretKey) {
585baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
586baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // overwrite its own entry.
587dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin            String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
588baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (keyAliasInKeystore == null) {
589baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
590baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
591baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
592baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
593baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + keyAliasInKeystore);
594baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
595baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            String keyEntryAlias =
596baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
597baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (!entryAlias.equals(keyEntryAlias)) {
598baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
599baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " alias: " + entryAlias + " != " + keyEntryAlias);
600baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
601baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            // This is the entry where this key is already stored. No need to do anything.
602baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            if (params != null) {
603baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
604baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                        + " parameters not supported");
605baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            }
606baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            return;
607baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
608baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
609baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (params == null) {
610baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
611baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Protection parameters must be specified when importing a symmetric key");
612baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
613baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
614baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        // Not a KeyStore-backed secret key -- import its key material into keystore.
615baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyExportFormat = key.getFormat();
616baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyExportFormat == null) {
617baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
618baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Only secret keys that export their key material are supported");
619baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (!"RAW".equals(keyExportFormat)) {
620baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
621baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Unsupported secret key material export format: " + keyExportFormat);
622baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
623baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        byte[] keyMaterial = key.getEncoded();
624baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (keyMaterial == null) {
625baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Key did not export its key material despite supporting"
626baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + " RAW format export");
627baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
628baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
629baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        KeymasterArguments args = new KeymasterArguments();
630b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        try {
631b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int keymasterAlgorithm =
632b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
633b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
634b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
635b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterDigests;
636c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin            if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
637c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
638c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
639c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // digest, we don't let import parameters override the digest implied by the key.
640c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // If the parameters specify digests at all, they must specify only one digest, the
641c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // only implied by key algorithm.
642c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                int keymasterImpliedDigest =
643c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
644c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (keymasterImpliedDigest == -1) {
645c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    throw new ProviderException(
646c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            "HMAC key algorithm digest unknown for key algorithm "
647c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                    + key.getAlgorithm());
648c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                }
649c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                keymasterDigests = new int[] {keymasterImpliedDigest};
650c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
651c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // Digest(s) explicitly specified in params -- check that the list consists of
652c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    // exactly one digest, the one implied by key algorithm.
653c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    int[] keymasterDigestsFromParams =
654c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            KeyProperties.Digest.allToKeymaster(params.getDigests());
655c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    if ((keymasterDigestsFromParams.length != 1)
656c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                            || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
657c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                        throw new KeyStoreException(
658c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                "Unsupported digests specification: "
659c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + Arrays.asList(params.getDigests()) + ". Only "
660c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
661c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                                + " supported for HMAC key algorithm " + key.getAlgorithm());
662cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                    }
663cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin                }
664c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            } else {
665c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                // Key algorithm does not imply a digest.
666c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                if (params.isDigestsSpecified()) {
667c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin                    keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
668b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                } else {
669b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    keymasterDigests = EmptyArray.INT;
670b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                }
671c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin            }
672b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
673baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
674b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            @KeyProperties.PurposeEnum int purposes = params.getPurposes();
675b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterBlockModes =
676b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
677b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
678b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (params.isRandomizedEncryptionRequired())) {
679b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                for (int keymasterBlockMode : keymasterBlockModes) {
680b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
681b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                            keymasterBlockMode)) {
682b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                        throw new KeyStoreException(
683b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                "Randomized encryption (IND-CPA) required but may be violated by"
684b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + " block mode: "
685b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
686b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                                + ". See KeyProtection documentation.");
687b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    }
6885927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                }
689f853f649981ed3cf2f1fbf1363a0932e9736daf6Alex Klyubin            }
690b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
691b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    KeyProperties.Purpose.allToKeymaster(purposes));
692b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
693b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (params.getSignaturePaddings().length > 0) {
694b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
695b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
696b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
697b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getEncryptionPaddings());
698b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
699b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            KeymasterUtils.addUserAuthArgs(args,
700b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.isUserAuthenticationRequired(),
701adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden                    params.getUserAuthenticationValidityDurationSeconds(),
702c38eae5229a820966008ae1885af90cd27c265e7Shawn Willden                    params.isUserAuthenticationValidWhileOnBody(),
70359ced28f0f722d2517afc65d755ebb388902f76bRubin Xu                    params.isInvalidatedByBiometricEnrollment(),
70459ced28f0f722d2517afc65d755ebb388902f76bRubin Xu                    params.getBoundToSpecificSecureUserId());
705a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin            KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
706a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    args,
707a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterAlgorithm,
708a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterBlockModes,
709a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin                    keymasterDigests);
710b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
711b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityStart());
712b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
713b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForOriginationEnd());
714b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
715b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    params.getKeyValidityForConsumptionEnd());
716b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin
717b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
718b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                    && (!params.isRandomizedEncryptionRequired())) {
719b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                // Permit caller-provided IV when encrypting with this key
720b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin                args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
721b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            }
722b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin        } catch (IllegalArgumentException | IllegalStateException e) {
723b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin            throw new KeyStoreException(e);
724b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
72512b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu        int flags = 0;
72612b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu        if (params.isCriticalToDeviceEncryption()) {
72712b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu            flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
72812b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu        }
729b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
7303876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
731baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
732baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        int errorCode = mKeyStore.importKey(
733baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyAliasInKeystore,
734baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                args,
735baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                KeymasterDefs.KM_KEY_FORMAT_RAW,
736baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                keyMaterial,
7373876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                mUid,
73812b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu                flags,
739baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                new KeyCharacteristics());
7403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (errorCode != KeyStore.NO_ERROR) {
741baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
742baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                + errorCode);
743baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
744baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
745baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
746e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
747e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
748e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
749a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        throw new KeyStoreException("Operation not supported because key encoding is unknown");
750e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
751e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
752e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
753e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
754e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (isKeyEntry(alias)) {
755e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Entry exists and is not a trusted certificate");
756e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
757e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
758a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        // We can't set something to null.
759a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (cert == null) {
760a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("cert == null");
761a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
762a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
763e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] encoded;
764e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
765e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            encoded = cert.getEncoded();
766e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
767e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException(e);
768e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
769e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
7703876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
771e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
772e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
773e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
774e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
775e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
776e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineDeleteEntry(String alias) throws KeyStoreException {
7773876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
7786c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin            throw new KeyStoreException("Failed to delete entry: " + alias);
779e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
780e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
781e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
782e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Set<String> getUniqueAliases() {
7833876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] rawAliases = mKeyStore.list("", mUid);
784e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (rawAliases == null) {
785e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new HashSet<String>();
786e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
787e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
788e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> aliases = new HashSet<String>(rawAliases.length);
789e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : rawAliases) {
790e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final int idx = alias.indexOf('_');
791e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if ((idx == -1) || (alias.length() <= idx)) {
792e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                Log.e(NAME, "invalid alias: " + alias);
793e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
794e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
795e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
796e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            aliases.add(new String(alias.substring(idx + 1)));
797e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
798e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
799e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return aliases;
800e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
801e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
802e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
803e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Enumeration<String> engineAliases() {
804e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return Collections.enumeration(getUniqueAliases());
805e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
806e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
807e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
808e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineContainsAlias(String alias) {
809a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
810a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
811a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
812a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8133876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
8143876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
8153876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
8163876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
817e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
818e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
819e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
820e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public int engineSize() {
821e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getUniqueAliases().size();
822e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
823e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
824e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
825e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsKeyEntry(String alias) {
826e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return isKeyEntry(alias);
827e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
828e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
829e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private boolean isKeyEntry(String alias) {
830baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
831baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
832baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
833baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isPrivateKeyEntry(String alias) {
834a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
835a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
836a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
837a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8383876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
839e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
840e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
841baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    private boolean isSecretKeyEntry(String alias) {
842baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        if (alias == null) {
843baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new NullPointerException("alias == null");
844baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        }
845baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
8463876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
847baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin    }
848baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin
849a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    private boolean isCertificateEntry(String alias) {
850a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
851a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
852a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
853a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
8543876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
855a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    }
856a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
857e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
858e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsCertificateEntry(String alias) {
859a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return !isKeyEntry(alias) && isCertificateEntry(alias);
860e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
861e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
862e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
863e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public String engineGetCertificateAlias(Certificate cert) {
864e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (cert == null) {
865e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
866e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
8674a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (!"X.509".equalsIgnoreCase(cert.getType())) {
8684a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            // Only X.509 certificates supported
8694a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8704a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8714a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        byte[] targetCertBytes;
8724a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        try {
8734a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            targetCertBytes = cert.getEncoded();
8744a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        } catch (CertificateEncodingException e) {
8754a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8764a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
8774a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        if (targetCertBytes == null) {
8784a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return null;
8794a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
880e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
881e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> nonCaEntries = new HashSet<String>();
882e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
883e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
884e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
885e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * says to only compare the first certificate in the chain which is
886e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
887e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * convention.
888e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
8893876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
89078ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
89178ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : certAliases) {
8923876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
89378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
89478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
89578ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
896e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
89778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                nonCaEntries.add(alias);
898e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
8994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
90078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
90178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
902e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
903e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
904e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
905e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
906e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * Look at all the TrustedCertificateEntry types. Skip all the
907e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * PrivateKeyEntry we looked at above.
908e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
9093876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
91078ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
91178ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : caAliases) {
91278ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (nonCaEntries.contains(alias)) {
91378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
91478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
915e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9163876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
91778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
91878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
91978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
920e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9214a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin                if (Arrays.equals(certBytes, targetCertBytes)) {
92278ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
92378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
924e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
925e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
926e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
927e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
928e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
929e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
930e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
931e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineStore(OutputStream stream, char[] password) throws IOException,
932e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
933e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
934e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
935e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
936e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
937e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineLoad(InputStream stream, char[] password) throws IOException,
938e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
939e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (stream != null) {
940e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("InputStream not supported");
941e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
942e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
943e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (password != null) {
944e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("password not supported");
945e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
946e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
947e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Unfortunate name collision.
9483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        mKeyStore = KeyStore.getInstance();
9493876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mUid = KeyStore.UID_SELF;
9503876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    }
9513876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin
9523876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    @Override
9533876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin    public void engineLoad(LoadStoreParameter param) throws IOException,
9543876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            NoSuchAlgorithmException, CertificateException {
9553876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        int uid = KeyStore.UID_SELF;
9563876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        if (param != null) {
9573876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            if (param instanceof AndroidKeyStoreLoadStoreParameter) {
9583876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
9593876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            } else {
9603876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                throw new IllegalArgumentException(
9613876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                        "Unsupported param type: " + param.getClass());
9623876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            }
9633876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        }
9643876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mKeyStore = KeyStore.getInstance();
9653876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        mUid = uid;
966e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
967e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
9682eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    @Override
9692eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
9702eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throws KeyStoreException {
9712eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry == null) {
9722eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throw new KeyStoreException("entry == null");
9732eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9742eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9753876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
9762eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9773ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
9783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin            java.security.KeyStore.TrustedCertificateEntry trE =
9793ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin                    (java.security.KeyStore.TrustedCertificateEntry) entry;
9802eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
9812eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            return;
9822eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9832eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9842eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry instanceof PrivateKeyEntry) {
9852eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
9863f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
987baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else if (entry instanceof SecretKeyEntry) {
988baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            SecretKeyEntry secE = (SecretKeyEntry) entry;
9893f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin            setSecretKeyEntry(alias, secE.getSecretKey(), param);
990baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin        } else {
991baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin            throw new KeyStoreException(
992baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
993baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin                    + "; was " + entry);
9942eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
9952eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    }
9962eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
9974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    /**
9984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
9994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
10004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * can find out which keystore private key entry to use. This is needed so that Android Keystore
10014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * crypto operations using public keys can find out which key alias to use. These operations
10024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     * require an alias.
10034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin     */
10044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    static class KeyStoreX509Certificate extends DelegatingX509Certificate {
10054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        private final String mPrivateKeyAlias;
10063876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        private final int mPrivateKeyUid;
10073876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin        KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
10083876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                X509Certificate delegate) {
10094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            super(delegate);
10104a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            mPrivateKeyAlias = privateKeyAlias;
10113876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin            mPrivateKeyUid = privateKeyUid;
10124a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
10134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin
10144a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        @Override
10154a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        public PublicKey getPublicKey() {
10164a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            PublicKey original = super.getPublicKey();
10174a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin            return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
10183876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    mPrivateKeyAlias, mPrivateKeyUid,
10193876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin                    original.getAlgorithm(), original.getEncoded());
10204a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin        }
10214a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin    }
1022e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root}
1023