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
19802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Rootimport org.apache.harmony.xnet.provider.jsse.OpenSSLDSAPrivateKey;
20e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
21802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Rootimport org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey;
22e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
23e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log;
24e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
25e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream;
26e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException;
27e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream;
28e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream;
29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.InvalidKeyException;
30e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key;
31e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreException;
32e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreSpi;
33e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.NoSuchAlgorithmException;
34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.PrivateKey;
35e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.UnrecoverableKeyException;
36e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.Certificate;
37e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateEncodingException;
38e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateException;
39e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateFactory;
40e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.X509Certificate;
41e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.ArrayList;
42e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collection;
43e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collections;
44e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Date;
45e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Enumeration;
46e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.HashSet;
47e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Iterator;
48e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Set;
49e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
50e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/**
51db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * A java.security.KeyStore interface for the Android KeyStore. An instance of
52db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * it can be created via the {@link java.security.KeyStore#getInstance(String)
53e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
54e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
55e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
56e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * This is built on top of Android's keystore daemon. The convention of alias
57e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * use is:
58e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
59e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
60e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
61e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
62e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * entry which will have the rest of the chain concatenated in BER format.
63e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p>
64e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
65e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * with a single certificate.
66e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root *
67e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * @hide
68e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */
69e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootpublic class AndroidKeyStore extends KeyStoreSpi {
70e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public static final String NAME = "AndroidKeyStore";
71e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
72e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private android.security.KeyStore mKeyStore;
73e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
74e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
75e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
76e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException {
77e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (!isKeyEntry(alias)) {
78e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
79e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
80e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
81e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
82e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
83e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
84e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (InvalidKeyException e) {
85e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
86e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            t.initCause(e);
87e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw t;
88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
91e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
92e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate[] engineGetCertificateChain(String alias) {
93a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
94a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
95a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
96a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
97e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
98e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (leaf == null) {
99e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
100e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
101e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
102e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Certificate[] caList;
103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (caBytes != null) {
106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Collection<X509Certificate> caChain = toCertificates(caBytes);
107e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
108e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[caChain.size() + 1];
109e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
110e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Iterator<X509Certificate> it = caChain.iterator();
111e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int i = 1;
112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            while (it.hasNext()) {
113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                caList[i++] = it.next();
114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            caList = new Certificate[1];
117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
119e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        caList[0] = leaf;
120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return caList;
122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Certificate engineGetCertificate(String alias) {
126a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
127a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
128a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
129a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
130e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
131e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (certificate != null) {
132e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return toCertificate(certificate);
133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (certificate != null) {
137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return toCertificate(certificate);
138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
140e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
141e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
142e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
143e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static X509Certificate toCertificate(byte[] bytes) {
144e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
145e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
146e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return (X509Certificate) certFactory
147e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    .generateCertificate(new ByteArrayInputStream(bytes));
148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
149e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificate in keystore", e);
150e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
151e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
152e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
154e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @SuppressWarnings("unchecked")
155e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
158e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return (Collection<X509Certificate>) certFactory
159e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    .generateCertificates(new ByteArrayInputStream(bytes));
160e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateException e) {
161e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            Log.w(NAME, "Couldn't parse certificates in keystore", e);
162e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new ArrayList<X509Certificate>();
163e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
164e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
165e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
166e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Date getModificationDate(String alias) {
167e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final long epochMillis = mKeyStore.getmtime(alias);
168e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (epochMillis == -1L) {
169e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
170e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
171e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
172e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return new Date(epochMillis);
173e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
174e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
175e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
176e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Date engineGetCreationDate(String alias) {
177a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
178a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
179a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
180a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
181e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
182e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
183e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
184e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
185e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
186e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
187e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (d != null) {
188e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return d;
189e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
190e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
191e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getModificationDate(Credentials.CA_CERTIFICATE + alias);
192e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
193e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
194e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
195e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
196e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
197e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((password != null) && (password.length > 0)) {
198e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("entries cannot be protected with passwords");
199e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
200e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
201e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (key instanceof PrivateKey) {
202e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            setPrivateKeyEntry(alias, (PrivateKey) key, chain);
203e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
204e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Only PrivateKeys are supported");
205e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
206e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
207e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
208e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain)
209e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
210802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        byte[] keyBytes = null;
211802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
212802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        final String pkeyAlias;
213802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (key instanceof OpenSSLRSAPrivateKey) {
214802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            pkeyAlias = ((OpenSSLRSAPrivateKey) key).getPkeyAlias();
215802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else if (key instanceof OpenSSLDSAPrivateKey) {
216802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            pkeyAlias = ((OpenSSLDSAPrivateKey) key).getPkeyAlias();
217802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
218802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            pkeyAlias = null;
219e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
221802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        final boolean shouldReplacePrivateKey;
222802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
223802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
224802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if (!alias.equals(keySubalias)) {
225802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException("Can only replace keys with same alias: " + alias
226802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                        + " != " + keySubalias);
227802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
228802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
229802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            shouldReplacePrivateKey = false;
230802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
231802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            // Make sure the PrivateKey format is the one we support.
232802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            final String keyFormat = key.getFormat();
233802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
234802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException(
235802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                        "Only PrivateKeys that can be encoded into PKCS#8 are supported");
236802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
237802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
238802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            // Make sure we can actually encode the key.
239802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            keyBytes = key.getEncoded();
240802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            if (keyBytes == null) {
241802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                throw new KeyStoreException("PrivateKey has no encoding");
242802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            }
243802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root
244802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            shouldReplacePrivateKey = true;
245e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Make sure the chain exists since this is a PrivateKey
248e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if ((chain == null) || (chain.length == 0)) {
249e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
250e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
251e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
252e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Do chain type checking.
253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        X509Certificate[] x509chain = new X509Certificate[chain.length];
254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (int i = 0; i < chain.length; i++) {
255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!"X.509".equals(chain[i].getType())) {
256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
257e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
258e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
259e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
260e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (!(chain[i] instanceof X509Certificate)) {
261e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
262e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                        + i);
263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            x509chain[i] = (X509Certificate) chain[i];
266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] userCertBytes;
269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            userCertBytes = x509chain[0].getEncoded();
271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't encode certificate #1", e);
273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * If we have a chain, store it in the CA certificate slot for this
277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * alias as concatenated DER-encoded certificates. These can be
278e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * deserialized by {@link CertificateFactory#generateCertificates}.
279e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
280e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] chainBytes;
281e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (chain.length > 1) {
282e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
283e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * so we only need the certificates starting at index 1.
285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
286e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[][] certsBytes = new byte[x509chain.length - 1][];
287e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int totalCertLength = 0;
288e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
289e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                try {
290e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    certsBytes[i] = x509chain[i + 1].getEncoded();
291e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    totalCertLength += certsBytes[i].length;
292e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                } catch (CertificateEncodingException e) {
293e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                    throw new KeyStoreException("Can't encode Certificate #" + i, e);
294e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                }
295e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
296e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
297e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            /*
298e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * Serialize this into one byte array so we can later call
299e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             * CertificateFactory#generateCertificates to recover them.
300e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root             */
301e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = new byte[totalCertLength];
302e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            int outputOffset = 0;
303e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            for (int i = 0; i < certsBytes.length; i++) {
304e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                final int certLength = certsBytes[i].length;
305e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
306e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                outputOffset += certLength;
307e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                certsBytes[i] = null;
308e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
309e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else {
310e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            chainBytes = null;
311e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
312e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
313e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
314802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root         * Make sure we clear out all the appropriate types before trying to
315e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * write.
316e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
317802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (shouldReplacePrivateKey) {
318802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
319802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        } else {
320802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
321802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        }
322e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
323802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root        if (shouldReplacePrivateKey
324802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root                && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
325802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
326e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put private key in keystore");
327e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes)) {
328802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
329e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put certificate #1 in keystore");
330e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } else if (chainBytes != null
331e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes)) {
332802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
333e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't put certificate chain in keystore");
334e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
335e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
336e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
337e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
338e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
339e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throws KeyStoreException {
340a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        throw new KeyStoreException("Operation not supported because key encoding is unknown");
341e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
342e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
343e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
344e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
345e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (isKeyEntry(alias)) {
346e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Entry exists and is not a trusted certificate");
347e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
348e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
349a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        // We can't set something to null.
350a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (cert == null) {
351a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("cert == null");
352a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
353a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
354e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final byte[] encoded;
355e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        try {
356e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            encoded = cert.getEncoded();
357e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        } catch (CertificateEncodingException e) {
358e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException(e);
359e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
360e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
361e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded)) {
362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
366e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineDeleteEntry(String alias) throws KeyStoreException {
368a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
369a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            return;
370a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
371a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
372db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new KeyStoreException("No such entry " + alias);
374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private Set<String> getUniqueAliases() {
378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] rawAliases = mKeyStore.saw("");
379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (rawAliases == null) {
380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return new HashSet<String>();
381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> aliases = new HashSet<String>(rawAliases.length);
384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : rawAliases) {
385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final int idx = alias.indexOf('_');
386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if ((idx == -1) || (alias.length() <= idx)) {
387e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                Log.e(NAME, "invalid alias: " + alias);
388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
389e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            aliases.add(new String(alias.substring(idx + 1)));
392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return aliases;
395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public Enumeration<String> engineAliases() {
399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return Collections.enumeration(getUniqueAliases());
400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineContainsAlias(String alias) {
404a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
405a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
406a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
407a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
408e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
409e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
410e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
411e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
412e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
413e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
414e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public int engineSize() {
415e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return getUniqueAliases().size();
416e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
417e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
418e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
419e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsKeyEntry(String alias) {
420e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return isKeyEntry(alias);
421e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
422e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
423e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    private boolean isKeyEntry(String alias) {
424a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
425a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
426a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
427a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
428e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
429e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
430e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
431a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    private boolean isCertificateEntry(String alias) {
432a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        if (alias == null) {
433a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root            throw new NullPointerException("alias == null");
434a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        }
435a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
436a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
437a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root    }
438a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root
439e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
440e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public boolean engineIsCertificateEntry(String alias) {
441a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root        return !isKeyEntry(alias) && isCertificateEntry(alias);
442e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
443e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
444e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
445e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public String engineGetCertificateAlias(Certificate cert) {
446e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (cert == null) {
447e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            return null;
448e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
449e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
450e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final Set<String> nonCaEntries = new HashSet<String>();
451e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
452e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
453e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
454e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * says to only compare the first certificate in the chain which is
455e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * equivalent to the USER_CERTIFICATE prefix for the Android keystore
456e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * convention.
457e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
458e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
459e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : certAliases) {
460e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
461e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (certBytes == null) {
462e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
463e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
464e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
465e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Certificate c = toCertificate(certBytes);
466e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            nonCaEntries.add(alias);
467e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
468e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (cert.equals(c)) {
469e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                return alias;
470e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
471e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
472e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
473e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        /*
474e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * Look at all the TrustedCertificateEntry types. Skip all the
475e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         * PrivateKeyEntry we looked at above.
476e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root         */
477e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
478e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        for (String alias : caAliases) {
479e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (nonCaEntries.contains(alias)) {
480e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
481e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
482e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
483e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
484e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (certBytes == null) {
485e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                continue;
486e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
487e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
488e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            final Certificate c = toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
489e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            if (cert.equals(c)) {
490e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root                return alias;
491e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            }
492e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
493e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
494e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        return null;
495e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
496e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
497e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
498e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineStore(OutputStream stream, char[] password) throws IOException,
499e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
500e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
501e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
502e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
503e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    @Override
504e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    public void engineLoad(InputStream stream, char[] password) throws IOException,
505e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            NoSuchAlgorithmException, CertificateException {
506e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (stream != null) {
507e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("InputStream not supported");
508e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
509e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
510e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        if (password != null) {
511e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root            throw new IllegalArgumentException("password not supported");
512e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        }
513e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
514e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        // Unfortunate name collision.
515e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root        mKeyStore = android.security.KeyStore.getInstance();
516e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root    }
517e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root
518e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root}
519