1e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/* 2e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Copyright (C) 2012 The Android Open Source Project 3e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * 4e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Licensed under the Apache License, Version 2.0 (the "License"); 5e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * you may not use this file except in compliance with the License. 6e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * You may obtain a copy of the License at 7e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * 8e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * http://www.apache.org/licenses/LICENSE-2.0 9e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * 10e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Unless required by applicable law or agreed to in writing, software 11e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * distributed under the License is distributed on an "AS IS" BASIS, 12e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * See the License for the specific language governing permissions and 14e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * limitations under the License. 15e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 16e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 17dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinpackage android.security.keystore; 18e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 195927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport libcore.util.EmptyArray; 20dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.Credentials; 213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubinimport android.security.KeyStore; 22dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStoreParameter; 23baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeyCharacteristics; 24baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterArguments; 25baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterDefs; 263f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProperties; 273f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProtection; 28e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log; 29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 30e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream; 31e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException; 32e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream; 33e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream; 34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key; 352eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.Entry; 363876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubinimport java.security.KeyStore.LoadStoreParameter; 372eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.PrivateKeyEntry; 382eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.ProtectionParameter; 39baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport java.security.KeyStore.SecretKeyEntry; 40e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreException; 41e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.KeyStoreSpi; 42e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.NoSuchAlgorithmException; 43e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.PrivateKey; 44c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubinimport java.security.ProviderException; 454a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubinimport java.security.PublicKey; 46e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.UnrecoverableKeyException; 47e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.Certificate; 48e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateEncodingException; 49e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateException; 50e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.CertificateFactory; 51e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.cert.X509Certificate; 52e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.ArrayList; 535927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport java.util.Arrays; 54e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collection; 55e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Collections; 56e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Date; 57e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Enumeration; 58e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.HashSet; 59e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Iterator; 60e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.util.Set; 61e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 62baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport javax.crypto.SecretKey; 63baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 64e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root/** 65db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * A java.security.KeyStore interface for the Android KeyStore. An instance of 66db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root * it can be created via the {@link java.security.KeyStore#getInstance(String) 67e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a 68e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * java.security.KeyStore backed by this "AndroidKeyStore" implementation. 69e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p> 70e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * This is built on top of Android's keystore daemon. The convention of alias 71e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * use is: 72e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p> 73e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key, 74e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one 75e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE 76e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * entry which will have the rest of the chain concatenated in BER format. 77e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * <p> 78e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry 79e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * with a single certificate. 80e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * 81e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * @hide 82e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 83dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinpublic class AndroidKeyStoreSpi extends KeyStoreSpi { 84e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public static final String NAME = "AndroidKeyStore"; 85e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin private KeyStore mKeyStore; 873876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin private int mUid = KeyStore.UID_SELF; 88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, 91e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root UnrecoverableKeyException { 92baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (isPrivateKeyEntry(alias)) { 934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 944a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore( 953876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mKeyStore, privateKeyAlias, mUid); 96baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (isSecretKeyEntry(alias)) { 974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin String secretKeyAlias = Credentials.USER_SECRET_KEY + alias; 984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore( 993876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mKeyStore, secretKeyAlias, mUid); 1004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } else { 1014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin // Key not found 1024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 107e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Certificate[] engineGetCertificateChain(String alias) { 108a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 109a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 110a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 111a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias); 113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (leaf == null) { 114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Certificate[] caList; 118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 1193876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (caBytes != null) { 121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Collection<X509Certificate> caChain = toCertificates(caBytes); 122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList = new Certificate[caChain.size() + 1]; 124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Iterator<X509Certificate> it = caChain.iterator(); 126e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int i = 1; 127e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root while (it.hasNext()) { 128e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList[i++] = it.next(); 129e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 130e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 131e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList = new Certificate[1]; 132e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList[0] = leaf; 135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return caList; 137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 140e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Certificate engineGetCertificate(String alias) { 141a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 142a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 143a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 144a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 1453876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); 146903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (encodedCert != null) { 147903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return getCertificateForPrivateKeyEntry(alias, encodedCert); 148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 149e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 1503876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 151903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (encodedCert != null) { 152903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return getCertificateForTrustedCertificateEntry(encodedCert); 153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 154e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 155903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // This entry/alias does not contain a certificate. 156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 158e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 159903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) { 160903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // For this certificate there shouldn't be a private key in this KeyStore entry. Thus, 161903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // there's no need to wrap this certificate as opposed to the certificate associated with 162903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // a private key entry. 163903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return toCertificate(encodedCert); 164903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 165903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 166903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) { 167903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // All crypto algorithms offered by Android Keystore for its private keys must also 168903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // be offered for the corresponding public keys stored in the Android Keystore. The 169903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // complication is that the underlying keystore service operates only on full key pairs, 170903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // rather than just public keys or private keys. As a result, Android Keystore-backed 171903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // crypto can only be offered for public keys for which keystore contains the 172903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // corresponding private key. This is not the case for certificate-only entries (e.g., 173903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // trusted certificates). 174903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // 175903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // getCertificate().getPublicKey() is the only way to obtain the public key 176903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // corresponding to the private key stored in the KeyStore. Thus, we need to make sure 177903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // that the returned public key points to the underlying key pair / private key 178903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // when available. 179903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 180903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin X509Certificate cert = toCertificate(encodedCert); 181903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (cert == null) { 182903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // Failed to parse the certificate. 183903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return null; 184903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 185903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 186903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 1873876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin if (mKeyStore.contains(privateKeyAlias, mUid)) { 188903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // As expected, keystore contains the private key corresponding to this public key. Wrap 189903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // the certificate so that its getPublicKey method returns an Android Keystore 190903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // PublicKey. This key will delegate crypto operations involving this public key to 191903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // Android Keystore when higher-priority providers do not offer these crypto 192903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // operations for this key. 1933876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert); 194903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } else { 195903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // This KeyStore entry/alias is supposed to contain the private key corresponding to 196903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // the public key in this certificate, but it does not for some reason. It's probably a 197903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // bug. Let other providers handle crypto operations involving the public key returned 198903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // by this certificate's getPublicKey. 199903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return cert; 200903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 201903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 202903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 2034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin /** 2044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key 2054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * returned by the certificate contains information about the alias of the private key in 2064a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * keystore. This is needed so that Android Keystore crypto operations using public keys can 2074a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * find out which key alias to use. These operations cannot work without an alias. 2084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin */ 2094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate( 2103876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin String privateKeyAlias, int uid, X509Certificate certificate) { 2114a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (certificate != null) 2123876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null; 2134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 2144a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin 215e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private static X509Certificate toCertificate(byte[] bytes) { 216e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 217e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 2184a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (X509Certificate) certFactory.generateCertificate( 2194a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin new ByteArrayInputStream(bytes)); 220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateException e) { 221e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.w(NAME, "Couldn't parse certificate in keystore", e); 222e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 223e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 224e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 225e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 226e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @SuppressWarnings("unchecked") 227e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private static Collection<X509Certificate> toCertificates(byte[] bytes) { 228e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 229e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 2304a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (Collection<X509Certificate>) certFactory.generateCertificates( 2314a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin new ByteArrayInputStream(bytes)); 232e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateException e) { 233e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.w(NAME, "Couldn't parse certificates in keystore", e); 234e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new ArrayList<X509Certificate>(); 235e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 236e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 237e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 238e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private Date getModificationDate(String alias) { 2393876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final long epochMillis = mKeyStore.getmtime(alias, mUid); 240e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (epochMillis == -1L) { 241e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 242e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 243e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 244e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new Date(epochMillis); 245e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 248e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Date engineGetCreationDate(String alias) { 249a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 250a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 251a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 252a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias); 254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (d != null) { 255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return d; 256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 257e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 258baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin d = getModificationDate(Credentials.USER_SECRET_KEY + alias); 259baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (d != null) { 260baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return d; 261baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 262baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root d = getModificationDate(Credentials.USER_CERTIFICATE + alias); 264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (d != null) { 265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return d; 266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return getModificationDate(Credentials.CA_CERTIFICATE + alias); 269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throws KeyStoreException { 274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((password != null) && (password.length > 0)) { 275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("entries cannot be protected with passwords"); 276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 278e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (key instanceof PrivateKey) { 2792eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); 280baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (key instanceof SecretKey) { 281baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin setSecretKeyEntry(alias, (SecretKey) key, null); 282e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 283baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Only PrivateKey and SecretKey are supported"); 284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 286e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 2873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key) 2883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throws KeyStoreException { 2893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin String keyAlgorithm = key.getAlgorithm(); 2903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProtection.Builder specBuilder; 2913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 2923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder = 2933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin new KeyProtection.Builder( 2943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); 295dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin // Authorized to be used with any digest (including no digest). 296e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin // MD5 was never offered for Android Keystore for ECDSA. 297e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin specBuilder.setDigests( 298e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_NONE, 299e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA1, 300e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA224, 301e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA256, 302e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA384, 303e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA512); 3043ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 3053ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder = 3063ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin new KeyProtection.Builder( 3073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.PURPOSE_ENCRYPT 3083ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_DECRYPT 3093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_SIGN 3103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_VERIFY); 311dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin // Authorized to be used with any digest (including no digest). 312e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin specBuilder.setDigests( 313e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_NONE, 314e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_MD5, 315e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA1, 316e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA224, 317e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA256, 318e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA384, 319e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.DIGEST_SHA512); 320e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin // Authorized to be used with any encryption and signature padding 321e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin // schemes (including no padding). 3223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setEncryptionPaddings( 323e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.ENCRYPTION_PADDING_NONE, 324e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, 325e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); 326e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin specBuilder.setSignaturePaddings( 327e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, 328e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin KeyProperties.SIGNATURE_PADDING_RSA_PSS); 329e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin // Disable randomized encryption requirement to support encryption 330e4928a2912297751108c7045ce3343ec63edcc01Alex Klyubin // padding NONE above. 3313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setRandomizedEncryptionRequired(false); 3323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 3333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm); 3343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 3353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setUserAuthenticationRequired(false); 3363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 3373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin return specBuilder.build(); 3383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 3393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 3402eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, 3413f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { 34296481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin int flags = 0; 3433f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyProtection spec; 3443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (param == null) { 3453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec = getLegacyKeyProtectionParameter(key); 3463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else if (param instanceof KeyStoreParameter) { 3473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec = getLegacyKeyProtectionParameter(key); 3483f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyStoreParameter legacySpec = (KeyStoreParameter) param; 3493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (legacySpec.isEncryptionRequired()) { 3503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin flags = KeyStore.FLAG_ENCRYPTED; 3513f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } 3523f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } else if (param instanceof KeyProtection) { 3533f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin spec = (KeyProtection) param; 35412b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu if (spec.isCriticalToDeviceEncryption()) { 35512b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; 35612b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu } 3573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 3583f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin throw new KeyStoreException( 3593f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin "Unsupported protection parameter class:" + param.getClass().getName() 3603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". Supported: " + KeyProtection.class.getName() + ", " 3613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + KeyStoreParameter.class.getName()); 362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Make sure the chain exists since this is a PrivateKey 365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((chain == null) || (chain.length == 0)) { 366e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Must supply at least one Certificate with PrivateKey"); 367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 368e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Do chain type checking. 370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root X509Certificate[] x509chain = new X509Certificate[chain.length]; 371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < chain.length; i++) { 372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (!"X.509".equals(chain[i].getType())) { 373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root + i); 375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (!(chain[i] instanceof X509Certificate)) { 378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root + i); 380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root x509chain[i] = (X509Certificate) chain[i]; 383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] userCertBytes; 386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 387e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root userCertBytes = x509chain[0].getEncoded(); 388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 3893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to encode certificate #0", e); 390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * If we have a chain, store it in the CA certificate slot for this 394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * alias as concatenated DER-encoded certificates. These can be 395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * deserialized by {@link CertificateFactory#generateCertificates}. 396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] chainBytes; 398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (chain.length > 1) { 399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...} 401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * so we only need the certificates starting at index 1. 402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[][] certsBytes = new byte[x509chain.length - 1][]; 404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int totalCertLength = 0; 405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < certsBytes.length; i++) { 406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 407e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root certsBytes[i] = x509chain[i + 1].getEncoded(); 408e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root totalCertLength += certsBytes[i].length; 409e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 4103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to encode certificate #" + i, e); 411e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 412e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 413e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 414e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 415e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Serialize this into one byte array so we can later call 416e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * CertificateFactory#generateCertificates to recover them. 417e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 418e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root chainBytes = new byte[totalCertLength]; 419e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int outputOffset = 0; 420e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < certsBytes.length; i++) { 421e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final int certLength = certsBytes[i].length; 422e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength); 423e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root outputOffset += certLength; 424e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root certsBytes[i] = null; 425e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 426e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 427e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root chainBytes = null; 428e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 429e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 4303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String pkeyAlias; 4314a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (key instanceof AndroidKeyStorePrivateKey) { 4323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkeyAlias = ((AndroidKeyStoreKey) key).getAlias(); 433802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root } else { 4343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkeyAlias = null; 4353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin byte[] pkcs8EncodedPrivateKeyBytes; 4383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterArguments importArgs; 4393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final boolean shouldReplacePrivateKey; 4403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) { 4413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length()); 4423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!alias.equals(keySubalias)) { 4433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Can only replace keys with same alias: " + alias 4443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " != " + keySubalias); 4453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin shouldReplacePrivateKey = false; 4473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs = null; 4483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes = null; 4493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 4503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin shouldReplacePrivateKey = true; 4513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Make sure the PrivateKey format is the one we support. 4523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String keyFormat = key.getFormat(); 4533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) { 4543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException( 4553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin "Unsupported private key export format: " + keyFormat 4563ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". Only private keys which export their key material in PKCS#8 format are" 4573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " supported."); 4583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Make sure we can actually encode the key. 4613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes = key.getEncoded(); 4623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (pkcs8EncodedPrivateKeyBytes == null) { 4633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Private key did not export any key material"); 4643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4653ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs = new KeymasterArguments(); 4673ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin try { 468ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, 4693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( 4703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin key.getAlgorithm())); 4713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin @KeyProperties.PurposeEnum int purposes = spec.getPurposes(); 472ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 4733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.Purpose.allToKeymaster(purposes)); 4743ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (spec.isDigestsSpecified()) { 475ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST, 4763ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.Digest.allToKeymaster(spec.getDigests())); 4773ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 479ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, 4803ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes())); 4813ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int[] keymasterEncryptionPaddings = 4823ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.EncryptionPadding.allToKeymaster( 4833ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec.getEncryptionPaddings()); 4843ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 4853ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin && (spec.isRandomizedEncryptionRequired())) { 4863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin for (int keymasterPadding : keymasterEncryptionPaddings) { 4873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!KeymasterUtils 4883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 4893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin keymasterPadding)) { 4903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException( 4913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin "Randomized encryption (IND-CPA) required but is violated by" 4923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " encryption padding mode: " 4933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + KeyProperties.EncryptionPadding.fromKeymaster( 4943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin keymasterPadding) 4953ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". See KeyProtection documentation."); 4963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 499ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); 500ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, 5013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); 5023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterUtils.addUserAuthArgs(importArgs, 5033ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec.isUserAuthenticationRequired(), 504adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden spec.getUserAuthenticationValidityDurationSeconds(), 505c38eae5229a820966008ae1885af90cd27c265e7Shawn Willden spec.isUserAuthenticationValidWhileOnBody(), 50659ced28f0f722d2517afc65d755ebb388902f76bRubin Xu spec.isInvalidatedByBiometricEnrollment(), 50759ced28f0f722d2517afc65d755ebb388902f76bRubin Xu spec.getBoundToSpecificSecureUserId()); 508d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 509d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityStart()); 510d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 511d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityForOriginationEnd()); 512d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 513d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityForConsumptionEnd()); 514b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } catch (IllegalArgumentException | IllegalStateException e) { 515b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException(e); 5163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin boolean success = false; 5213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin try { 5223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the private key, if necessary 5233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (shouldReplacePrivateKey) { 5243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Delete the stored private key and any related entries before importing the 5253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // provided key 5263876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 5273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); 5283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int errorCode = mKeyStore.importKey( 5293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.USER_PRIVATE_KEY + alias, 5303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs, 5313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterDefs.KM_KEY_FORMAT_PKCS8, 5323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes, 5333876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid, 5343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin flags, 5353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin resultingKeyCharacteristics); 5363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store private key", 5383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 5413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Keep the stored private key around -- delete all other entry types 5423876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 5433876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 5443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5453ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the leaf certificate 5473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes, 5483876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid, flags); 5493ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store certificate #0", 5513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5523ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the certificate chain 5553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes, 5563876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid, flags); 5573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store certificate chain", 5593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin success = true; 5623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } finally { 5633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!success) { 5643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (shouldReplacePrivateKey) { 5653876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 5663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 5673876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid); 5683876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid); 5693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 571e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 572e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 573e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 5743f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin private void setSecretKeyEntry(String entryAlias, SecretKey key, 5753f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin java.security.KeyStore.ProtectionParameter param) 576baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throws KeyStoreException { 5773f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin if ((param != null) && (!(param instanceof KeyProtection))) { 5783f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin throw new KeyStoreException( 5793f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin "Unsupported protection parameter class: " + param.getClass().getName() 5803f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin + ". Supported: " + KeyProtection.class.getName()); 5813f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } 5823f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyProtection params = (KeyProtection) param; 5833f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin 584dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin if (key instanceof AndroidKeyStoreSecretKey) { 585baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot 586baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // overwrite its own entry. 587dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); 588baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyAliasInKeystore == null) { 589baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); 590baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 591baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 592baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " 593baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + keyAliasInKeystore); 594baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 595baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyEntryAlias = 596baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 597baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (!entryAlias.equals(keyEntryAlias)) { 598baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Can only replace KeyStore-backed keys with same" 599baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " alias: " + entryAlias + " != " + keyEntryAlias); 600baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 601baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // This is the entry where this key is already stored. No need to do anything. 602baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (params != null) { 603baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Modifying KeyStore-backed key using protection" 604baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " parameters not supported"); 605baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 606baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return; 607baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 608baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 609baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (params == null) { 610baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 611baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Protection parameters must be specified when importing a symmetric key"); 612baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 613baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 614baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // Not a KeyStore-backed secret key -- import its key material into keystore. 615baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyExportFormat = key.getFormat(); 616baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyExportFormat == null) { 617baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 618baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Only secret keys that export their key material are supported"); 619baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (!"RAW".equals(keyExportFormat)) { 620baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 621baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Unsupported secret key material export format: " + keyExportFormat); 622baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 623baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin byte[] keyMaterial = key.getEncoded(); 624baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyMaterial == null) { 625baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Key did not export its key material despite supporting" 626baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " RAW format export"); 627baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 628baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 629baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin KeymasterArguments args = new KeymasterArguments(); 630b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin try { 631b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int keymasterAlgorithm = 632b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm()); 633b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); 634b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin 635b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterDigests; 636c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { 637c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm 638c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one 639c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // digest, we don't let import parameters override the digest implied by the key. 640c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // If the parameters specify digests at all, they must specify only one digest, the 641c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // only implied by key algorithm. 642c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin int keymasterImpliedDigest = 643c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm()); 644c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (keymasterImpliedDigest == -1) { 645c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin throw new ProviderException( 646c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin "HMAC key algorithm digest unknown for key algorithm " 647c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + key.getAlgorithm()); 648c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin } 649c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin keymasterDigests = new int[] {keymasterImpliedDigest}; 650c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (params.isDigestsSpecified()) { 651c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // Digest(s) explicitly specified in params -- check that the list consists of 652c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // exactly one digest, the one implied by key algorithm. 653c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin int[] keymasterDigestsFromParams = 654c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin KeyProperties.Digest.allToKeymaster(params.getDigests()); 655c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if ((keymasterDigestsFromParams.length != 1) 656c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) { 657c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin throw new KeyStoreException( 658c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin "Unsupported digests specification: " 659c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + Arrays.asList(params.getDigests()) + ". Only " 660c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest) 661c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + " supported for HMAC key algorithm " + key.getAlgorithm()); 662cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin } 663cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin } 664c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin } else { 665c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // Key algorithm does not imply a digest. 666c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (params.isDigestsSpecified()) { 667c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests()); 668b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } else { 669b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin keymasterDigests = EmptyArray.INT; 670b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 671c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin } 672b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); 673baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 674b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin @KeyProperties.PurposeEnum int purposes = params.getPurposes(); 675b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterBlockModes = 676b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); 677b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 678b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin && (params.isRandomizedEncryptionRequired())) { 679b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin for (int keymasterBlockMode : keymasterBlockModes) { 680b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 681b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin keymasterBlockMode)) { 682b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException( 683b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin "Randomized encryption (IND-CPA) required but may be violated by" 684b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + " block mode: " 685b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) 686b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + ". See KeyProtection documentation."); 687b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 6885927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin } 689f853f649981ed3cf2f1fbf1363a0932e9736daf6Alex Klyubin } 690b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 691b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.Purpose.allToKeymaster(purposes)); 692b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); 693b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (params.getSignaturePaddings().length > 0) { 694b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException("Signature paddings not supported for symmetric keys"); 695b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 696b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( 697b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getEncryptionPaddings()); 698b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); 699b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeymasterUtils.addUserAuthArgs(args, 700b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.isUserAuthenticationRequired(), 701adef49640d6339e6b4a6ad736c5815e35d9b8803Shawn Willden params.getUserAuthenticationValidityDurationSeconds(), 702c38eae5229a820966008ae1885af90cd27c265e7Shawn Willden params.isUserAuthenticationValidWhileOnBody(), 70359ced28f0f722d2517afc65d755ebb388902f76bRubin Xu params.isInvalidatedByBiometricEnrollment(), 70459ced28f0f722d2517afc65d755ebb388902f76bRubin Xu params.getBoundToSpecificSecureUserId()); 705a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( 706a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin args, 707a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin keymasterAlgorithm, 708a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin keymasterBlockModes, 709a95550f8016bbb0dba086dbd73eec63e6cdbbe98Alex Klyubin keymasterDigests); 710b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 711b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityStart()); 712b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 713b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityForOriginationEnd()); 714b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 715b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityForConsumptionEnd()); 716b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin 717b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 718b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin && (!params.isRandomizedEncryptionRequired())) { 719b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin // Permit caller-provided IV when encrypting with this key 720b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 721b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 722b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } catch (IllegalArgumentException | IllegalStateException e) { 723b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException(e); 724b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin } 72512b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu int flags = 0; 72612b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu if (params.isCriticalToDeviceEncryption()) { 72712b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; 72812b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu } 729b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin 7303876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid); 731baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; 732baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin int errorCode = mKeyStore.importKey( 733baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyAliasInKeystore, 734baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin args, 735baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin KeymasterDefs.KM_KEY_FORMAT_RAW, 736baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyMaterial, 7373876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid, 73812b644d275b121dd952a4a564fa65b9c18c9b22cRubin Xu flags, 739baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin new KeyCharacteristics()); 7403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 741baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Failed to import secret key. Keystore error code: " 742baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + errorCode); 743baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 744baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 745baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 746e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 747e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) 748e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throws KeyStoreException { 749a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new KeyStoreException("Operation not supported because key encoding is unknown"); 750e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 751e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 752e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 753e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { 754e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (isKeyEntry(alias)) { 755e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Entry exists and is not a trusted certificate"); 756e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 757e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 758a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root // We can't set something to null. 759a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (cert == null) { 760a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("cert == null"); 761a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 762a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 763e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] encoded; 764e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 765e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root encoded = cert.getEncoded(); 766e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 767e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException(e); 768e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 769e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 7703876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) { 771e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); 772e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 773e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 774e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 775e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 776e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineDeleteEntry(String alias) throws KeyStoreException { 7773876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) { 7786c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin throw new KeyStoreException("Failed to delete entry: " + alias); 779e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 780e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 781e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 782e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private Set<String> getUniqueAliases() { 7833876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final String[] rawAliases = mKeyStore.list("", mUid); 784e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (rawAliases == null) { 785e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new HashSet<String>(); 786e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 787e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 788e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Set<String> aliases = new HashSet<String>(rawAliases.length); 789e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (String alias : rawAliases) { 790e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final int idx = alias.indexOf('_'); 791e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((idx == -1) || (alias.length() <= idx)) { 792e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.e(NAME, "invalid alias: " + alias); 793e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root continue; 794e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 795e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 796e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root aliases.add(new String(alias.substring(idx + 1))); 797e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 798e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 799e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return aliases; 800e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 801e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 802e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 803e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Enumeration<String> engineAliases() { 804e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return Collections.enumeration(getUniqueAliases()); 805e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 806e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 807e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 808e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineContainsAlias(String alias) { 809a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 810a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 811a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 812a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 8133876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) 8143876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid) 8153876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid) 8163876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 817e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 818e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 819e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 820e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public int engineSize() { 821e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return getUniqueAliases().size(); 822e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 823e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 824e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 825e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineIsKeyEntry(String alias) { 826e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return isKeyEntry(alias); 827e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 828e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 829e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private boolean isKeyEntry(String alias) { 830baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); 831baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 832baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 833baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin private boolean isPrivateKeyEntry(String alias) { 834a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 835a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 836a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 837a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 8383876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid); 839e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 840e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 841baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin private boolean isSecretKeyEntry(String alias) { 842baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (alias == null) { 843baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new NullPointerException("alias == null"); 844baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 845baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 8463876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid); 847baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 848baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 849a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root private boolean isCertificateEntry(String alias) { 850a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 851a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 852a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 853a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 8543876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid); 855a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 856a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 857e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 858e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineIsCertificateEntry(String alias) { 859a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root return !isKeyEntry(alias) && isCertificateEntry(alias); 860e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 861e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 862e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 863e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public String engineGetCertificateAlias(Certificate cert) { 864e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (cert == null) { 865e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 866e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 8674a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (!"X.509".equalsIgnoreCase(cert.getType())) { 8684a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin // Only X.509 certificates supported 8694a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8704a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 8714a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin byte[] targetCertBytes; 8724a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin try { 8734a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin targetCertBytes = cert.getEncoded(); 8744a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } catch (CertificateEncodingException e) { 8754a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8764a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 8774a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (targetCertBytes == null) { 8784a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8794a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 880e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 881e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Set<String> nonCaEntries = new HashSet<String>(); 882e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 883e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 884e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation 885e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * says to only compare the first certificate in the chain which is 886e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * equivalent to the USER_CERTIFICATE prefix for the Android keystore 887e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * convention. 888e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 8893876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid); 89078ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certAliases != null) { 89178ad849163a7b01073b46fbd7d818392720005d1Kenny Root for (String alias : certAliases) { 8923876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid); 89378ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certBytes == null) { 89478ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 89578ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 896e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 89778ad849163a7b01073b46fbd7d818392720005d1Kenny Root nonCaEntries.add(alias); 898e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 8994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (Arrays.equals(certBytes, targetCertBytes)) { 90078ad849163a7b01073b46fbd7d818392720005d1Kenny Root return alias; 90178ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 902e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 903e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 904e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 905e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 906e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Look at all the TrustedCertificateEntry types. Skip all the 907e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry we looked at above. 908e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 9093876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid); 91078ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certAliases != null) { 91178ad849163a7b01073b46fbd7d818392720005d1Kenny Root for (String alias : caAliases) { 91278ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (nonCaEntries.contains(alias)) { 91378ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 91478ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 915e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 9163876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); 91778ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certBytes == null) { 91878ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 91978ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 920e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 9214a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (Arrays.equals(certBytes, targetCertBytes)) { 92278ad849163a7b01073b46fbd7d818392720005d1Kenny Root return alias; 92378ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 924e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 925e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 926e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 927e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 928e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 929e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 930e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 931e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineStore(OutputStream stream, char[] password) throws IOException, 932e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root NoSuchAlgorithmException, CertificateException { 933e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); 934e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 935e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 936e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 937e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineLoad(InputStream stream, char[] password) throws IOException, 938e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root NoSuchAlgorithmException, CertificateException { 939e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (stream != null) { 940e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new IllegalArgumentException("InputStream not supported"); 941e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 942e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 943e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (password != null) { 944e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new IllegalArgumentException("password not supported"); 945e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 946e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 947e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Unfortunate name collision. 9483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin mKeyStore = KeyStore.getInstance(); 9493876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid = KeyStore.UID_SELF; 9503876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin } 9513876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin 9523876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin @Override 9533876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin public void engineLoad(LoadStoreParameter param) throws IOException, 9543876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin NoSuchAlgorithmException, CertificateException { 9553876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin int uid = KeyStore.UID_SELF; 9563876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin if (param != null) { 9573876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin if (param instanceof AndroidKeyStoreLoadStoreParameter) { 9583876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid(); 9593876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin } else { 9603876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin throw new IllegalArgumentException( 9613876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin "Unsupported param type: " + param.getClass()); 9623876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin } 9633876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin } 9643876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mKeyStore = KeyStore.getInstance(); 9653876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mUid = uid; 966e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 967e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 9682eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root @Override 9692eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) 9702eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root throws KeyStoreException { 9712eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root if (entry == null) { 9722eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root throw new KeyStoreException("entry == null"); 9732eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9742eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9753876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid); 9762eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9773ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) { 9783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin java.security.KeyStore.TrustedCertificateEntry trE = 9793ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin (java.security.KeyStore.TrustedCertificateEntry) entry; 9802eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root engineSetCertificateEntry(alias, trE.getTrustedCertificate()); 9812eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root return; 9822eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9832eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9842eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root if (entry instanceof PrivateKeyEntry) { 9852eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root PrivateKeyEntry prE = (PrivateKeyEntry) entry; 9863f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); 987baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (entry instanceof SecretKeyEntry) { 988baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin SecretKeyEntry secE = (SecretKeyEntry) entry; 9893f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin setSecretKeyEntry(alias, secE.getSecretKey(), param); 990baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else { 991baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 992baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" 993baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + "; was " + entry); 9942eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9952eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9962eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin /** 9984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from 9994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain 10004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * can find out which keystore private key entry to use. This is needed so that Android Keystore 10014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * crypto operations using public keys can find out which key alias to use. These operations 10024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * require an alias. 10034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin */ 10044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin static class KeyStoreX509Certificate extends DelegatingX509Certificate { 10054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin private final String mPrivateKeyAlias; 10063876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin private final int mPrivateKeyUid; 10073876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid, 10083876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin X509Certificate delegate) { 10094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin super(delegate); 10104a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin mPrivateKeyAlias = privateKeyAlias; 10113876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mPrivateKeyUid = privateKeyUid; 10124a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 10134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin 10144a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin @Override 10154a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin public PublicKey getPublicKey() { 10164a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin PublicKey original = super.getPublicKey(); 10174a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( 10183876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin mPrivateKeyAlias, mPrivateKeyUid, 10193876b1be27e3aefde9a72eb2e4f856e94fc5f946Alex Klyubin original.getAlgorithm(), original.getEncoded()); 10204a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 10214a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 1022e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root} 1023