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
17e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootpackage android.security;
18e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
1912e752225aa96888358294be0d725d499a1c9f03Kenny Rootimport com.android.org.conscrypt.OpenSSLEngine;
2012e752225aa96888358294be0d725d499a1c9f03Kenny Rootimport com.android.org.conscrypt.OpenSSLKeyHolder;
21e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
22e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log;
23e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
24e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream;
25e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException;
26e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream;
27e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream;
28e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.InvalidKeyException;
29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key;
302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.Entry;
312eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.PrivateKeyEntry;
322eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.ProtectionParameter;
332eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore;
34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreException;
35e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreSpi;
36e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.NoSuchAlgorithmException;
37e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.PrivateKey;
38e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.UnrecoverableKeyException;
39e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.Certificate;
40e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateEncodingException;
41e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateException;
42e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateFactory;
43e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.X509Certificate;
44e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.ArrayList;
45e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collection;
46e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collections;
47e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Date;
48e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Enumeration;
49e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.HashSet;
50e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Iterator;
51e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Set;
52e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
53e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/**
54db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * A java.security.KeyStore interface for the Android KeyStore. An instance of
55db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * it can be created via the {@link java.security.KeyStore#getInstance(String)
56e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
57e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
58e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
59e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * This is built on top of Android's keystore daemon. The convention of alias
60e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * use is:
61e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
62e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
63e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
64e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
65e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * entry which will have the rest of the chain concatenated in BER format.
66e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
67e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
68e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * with a single certificate.
69e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
70e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * @hide
71e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */
72e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootpublic class AndroidKeyStore extends KeyStoreSpi {
73e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public static final String NAME = "AndroidKeyStore";
74e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
75e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private android.security.KeyStore mKeyStore;
76e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
77e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
78e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
79e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException {
80e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (!isKeyEntry(alias)) {
81e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
82e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
83e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
84e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
85e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
86e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
87e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (InvalidKeyException e) {
88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            t.initCause(e);
90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw t;
91e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
92e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
93e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
94e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
95e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate[] engineGetCertificateChain(String alias) {
96a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
97a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
98a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
99a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
100e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
101e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (leaf == null) {
102e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Certificate[] caList;
106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
107e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
108e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (caBytes != null) {
109e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Collection<X509Certificate> caChain = toCertificates(caBytes);
110e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
111e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[caChain.size() + 1];
112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Iterator<X509Certificate> it = caChain.iterator();
114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int i = 1;
115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            while (it.hasNext()) {
116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                caList[i++] = it.next();
117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
119e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[1];
120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        caList[0] = leaf;
123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return caList;
125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
126e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
127e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
128e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate engineGetCertificate(String alias) {
129a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
130a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
131a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
132a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (certificate != null) {
135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return toCertificate(certificate);
136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (certificate != null) {
140e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return toCertificate(certificate);
141e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
142e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
143e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
144e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
145e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
146e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static X509Certificate toCertificate(byte[] bytes) {
147e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
149e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return (X509Certificate) certFactory
150e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    .generateCertificate(new ByteArrayInputStream(bytes));
151e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
152e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificate in keystore", e);
153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
154e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
155e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @SuppressWarnings("unchecked")
158e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
159e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
160e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
161e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return (Collection<X509Certificate>) certFactory
162e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    .generateCertificates(new ByteArrayInputStream(bytes));
163e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
164e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificates in keystore", e);
165e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new ArrayList<X509Certificate>();
166e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
167e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
168e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
169e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Date getModificationDate(String alias) {
170e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final long epochMillis = mKeyStore.getmtime(alias);
171e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (epochMillis == -1L) {
172e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
173e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
174e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
175e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return new Date(epochMillis);
176e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
177e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
178e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
179e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Date engineGetCreationDate(String alias) {
180a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
181a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
182a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
183a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
184e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
185e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
186e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
187e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
188e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
189e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
190e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
191e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
192e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
193e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
194e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getModificationDate(Credentials.CA_CERTIFICATE + alias);
195e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
196e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
197e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
198e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
199e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
200e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((password != null) && (password.length > 0)) {
201e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("entries cannot be protected with passwords");
202e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
203e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
204e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (key instanceof PrivateKey) {
2052eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
206e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
207e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Only PrivateKeys are supported");
208e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
209e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
210e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
2112eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
2121c219f619291ba818bc2542390a2988539d94ed0Kenny Root            KeyStoreParameter params) throws KeyStoreException {
213802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        byte[] keyBytes = null;
214802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
215802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        final String pkeyAlias;
216cc1fc6b6adc1edc2acaa42205b4ec5ca00bfd353Kenny Root        if (key instanceof OpenSSLKeyHolder) {
217cc1fc6b6adc1edc2acaa42205b4ec5ca00bfd353Kenny Root            pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
218802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
219802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            pkeyAlias = null;
220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
221e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
222802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        final boolean shouldReplacePrivateKey;
223802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
224802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
225802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if (!alias.equals(keySubalias)) {
226802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException("Can only replace keys with same alias: " + alias
227802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                        + " != " + keySubalias);
228802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
229802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
230802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            shouldReplacePrivateKey = false;
231802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
232802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            // Make sure the PrivateKey format is the one we support.
233802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            final String keyFormat = key.getFormat();
234802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
235802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException(
236802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                        "Only PrivateKeys that can be encoded into PKCS#8 are supported");
237802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
238802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
239802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            // Make sure we can actually encode the key.
240802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            keyBytes = key.getEncoded();
241802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if (keyBytes == null) {
242802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException("PrivateKey has no encoding");
243802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
244802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
245802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            shouldReplacePrivateKey = true;
246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
248e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Make sure the chain exists since this is a PrivateKey
249e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((chain == null) || (chain.length == 0)) {
250e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
251e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
252e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Do chain type checking.
254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        X509Certificate[] x509chain = new X509Certificate[chain.length];
255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (int i = 0; i < chain.length; i++) {
256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!"X.509".equals(chain[i].getType())) {
257e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
258e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
259e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
260e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
261e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!(chain[i] instanceof X509Certificate)) {
262e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            x509chain[i] = (X509Certificate) chain[i];
267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] userCertBytes;
270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            userCertBytes = x509chain[0].getEncoded();
272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't encode certificate #1", e);
274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * If we have a chain, store it in the CA certificate slot for this
278e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * alias as concatenated DER-encoded certificates. These can be
279e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * deserialized by {@link CertificateFactory#generateCertificates}.
280e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
281e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] chainBytes;
282e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (chain.length > 1) {
283e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * so we only need the certificates starting at index 1.
286e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
287e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[][] certsBytes = new byte[x509chain.length - 1][];
288e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int totalCertLength = 0;
289e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
290e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                try {
291e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    certsBytes[i] = x509chain[i + 1].getEncoded();
292e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    totalCertLength += certsBytes[i].length;
293e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                } catch (CertificateEncodingException e) {
294e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    throw new KeyStoreException("Can't encode Certificate #" + i, e);
295e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                }
296e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
297e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
298e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
299e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * Serialize this into one byte array so we can later call
300e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * CertificateFactory#generateCertificates to recover them.
301e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
302e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = new byte[totalCertLength];
303e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int outputOffset = 0;
304e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
305e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                final int certLength = certsBytes[i].length;
306e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
307e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                outputOffset += certLength;
308e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                certsBytes[i] = null;
309e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
310e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
311e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = null;
312e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
313e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
314e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
315802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root         * Make sure we clear out all the appropriate types before trying to
316e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * write.
317e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
318802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (shouldReplacePrivateKey) {
319802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
320802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
321802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
322802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        }
323e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
3242eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        final int flags = (params == null) ? 0 : params.getFlags();
3252eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
326802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (shouldReplacePrivateKey
3272eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
3282eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                        android.security.KeyStore.UID_SELF, flags)) {
329802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
330e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put private key in keystore");
3312eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
3322eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                android.security.KeyStore.UID_SELF, flags)) {
333802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
334e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put certificate #1 in keystore");
335e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else if (chainBytes != null
3362eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
3372eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                        android.security.KeyStore.UID_SELF, flags)) {
338802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
339e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put certificate chain in keystore");
340e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
341e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
342e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
343e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
344e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
345e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
346a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        throw new KeyStoreException("Operation not supported because key encoding is unknown");
347e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
348e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
349e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
350e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
351e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (isKeyEntry(alias)) {
352e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Entry exists and is not a trusted certificate");
353e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
354e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
355a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        // We can't set something to null.
356a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (cert == null) {
357a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("cert == null");
358a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
359a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
360e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] encoded;
361e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            encoded = cert.getEncoded();
363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException(e);
365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
366e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
3672eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
3682eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineDeleteEntry(String alias) throws KeyStoreException {
375a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
376a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            return;
377a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
378a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
379db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("No such entry " + alias);
381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Set<String> getUniqueAliases() {
385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] rawAliases = mKeyStore.saw("");
386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (rawAliases == null) {
387e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new HashSet<String>();
388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
389e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> aliases = new HashSet<String>(rawAliases.length);
391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : rawAliases) {
392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final int idx = alias.indexOf('_');
393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if ((idx == -1) || (alias.length() <= idx)) {
394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                Log.e(NAME, "invalid alias: " + alias);
395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            aliases.add(new String(alias.substring(idx + 1)));
399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return aliases;
402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Enumeration<String> engineAliases() {
406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return Collections.enumeration(getUniqueAliases());
407e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
408e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
409e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
410e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineContainsAlias(String alias) {
411a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
412a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
413a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
414a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
415e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
416e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
417e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
418e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
419e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
420e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
421e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public int engineSize() {
422e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getUniqueAliases().size();
423e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
424e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
425e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
426e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsKeyEntry(String alias) {
427e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return isKeyEntry(alias);
428e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
429e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
430e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private boolean isKeyEntry(String alias) {
431a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
432a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
433a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
434a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
435e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
436e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
437e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
438a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    private boolean isCertificateEntry(String alias) {
439a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
440a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
441a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
442a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
443a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
444a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    }
445a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
446e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
447e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsCertificateEntry(String alias) {
448a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return !isKeyEntry(alias) && isCertificateEntry(alias);
449e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
450e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
451e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
452e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public String engineGetCertificateAlias(Certificate cert) {
453e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (cert == null) {
454e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
455e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
456e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
457e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> nonCaEntries = new HashSet<String>();
458e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
459e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
460e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
461e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * says to only compare the first certificate in the chain which is
462e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
463e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * convention.
464e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
465e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
46678ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
46778ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : certAliases) {
46878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
46978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
47078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
47178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
472e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
47378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final Certificate c = toCertificate(certBytes);
47478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                nonCaEntries.add(alias);
475e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
47678ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (cert.equals(c)) {
47778ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
47878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
479e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
480e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
481e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
482e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
483e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * Look at all the TrustedCertificateEntry types. Skip all the
484e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * PrivateKeyEntry we looked at above.
485e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
486e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
48778ad849163a7b01073b46fbd7d818392720005d1Kenny Root        if (certAliases != null) {
48878ad849163a7b01073b46fbd7d818392720005d1Kenny Root            for (String alias : caAliases) {
48978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (nonCaEntries.contains(alias)) {
49078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
49178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
492e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
49378ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
49478ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (certBytes == null) {
49578ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    continue;
49678ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
497e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
49878ad849163a7b01073b46fbd7d818392720005d1Kenny Root                final Certificate c =
49978ad849163a7b01073b46fbd7d818392720005d1Kenny Root                        toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
50078ad849163a7b01073b46fbd7d818392720005d1Kenny Root                if (cert.equals(c)) {
50178ad849163a7b01073b46fbd7d818392720005d1Kenny Root                    return alias;
50278ad849163a7b01073b46fbd7d818392720005d1Kenny Root                }
503e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
504e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
505e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
506e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
507e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
508e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
509e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
510e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineStore(OutputStream stream, char[] password) throws IOException,
511e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
512e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
513e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
514e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
515e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
516e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineLoad(InputStream stream, char[] password) throws IOException,
517e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
518e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (stream != null) {
519e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("InputStream not supported");
520e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
521e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
522e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (password != null) {
523e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("password not supported");
524e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
525e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
526e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Unfortunate name collision.
527e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        mKeyStore = android.security.KeyStore.getInstance();
528e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
529e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
5302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    @Override
5312eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
5322eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throws KeyStoreException {
5332eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry == null) {
5342eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            throw new KeyStoreException("entry == null");
5352eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
5362eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
5372eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (engineContainsAlias(alias)) {
5382eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            engineDeleteEntry(alias);
5392eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
5402eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
5412eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry instanceof KeyStore.TrustedCertificateEntry) {
5422eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
5432eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
5442eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            return;
5452eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
5462eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
5471c219f619291ba818bc2542390a2988539d94ed0Kenny Root        if (param != null && !(param instanceof KeyStoreParameter)) {
5481c219f619291ba818bc2542390a2988539d94ed0Kenny Root            throw new KeyStoreException(
5491c219f619291ba818bc2542390a2988539d94ed0Kenny Root                    "protParam should be android.security.KeyStoreParameter; was: "
5502eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                    + param.getClass().getName());
5512eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
5522eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
5532eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        if (entry instanceof PrivateKeyEntry) {
5542eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
5552eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
5561c219f619291ba818bc2542390a2988539d94ed0Kenny Root                    (KeyStoreParameter) param);
5572eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root            return;
5582eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        }
5592eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
5602eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root        throw new KeyStoreException(
5612eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root                "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
5622eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root    }
5632eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root
564e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root}
565