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