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