AndroidKeyStoreSpi.java revision c58153b2d7418f44f2b0e397478be808e91decef
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; 205927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin 21dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.Credentials; 223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubinimport android.security.KeyStore; 23dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStoreParameter; 24baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeyCharacteristics; 25baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterArguments; 26baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubinimport android.security.keymaster.KeymasterDefs; 273f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProperties; 283f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubinimport android.security.keystore.KeyProtection; 29e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport android.util.Log; 30e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 31e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.ByteArrayInputStream; 32e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.IOException; 33e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.InputStream; 34e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.io.OutputStream; 35e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Rootimport java.security.Key; 362eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Rootimport java.security.KeyStore.Entry; 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; 87e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 88e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 89e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, 90e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root UnrecoverableKeyException { 91baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (isPrivateKeyEntry(alias)) { 924a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 934a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore( 944a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin mKeyStore, privateKeyAlias); 95baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (isSecretKeyEntry(alias)) { 964a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin String secretKeyAlias = Credentials.USER_SECRET_KEY + alias; 974a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore( 984a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin mKeyStore, secretKeyAlias); 994a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } else { 1004a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin // Key not found 1014a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 102e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 103e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 104e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 105e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 106e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Certificate[] engineGetCertificateChain(String alias) { 107a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 108a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 109a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 110a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 111e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias); 112e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (leaf == null) { 113e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 114e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 115e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 116e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Certificate[] caList; 117e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 118e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias); 119e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (caBytes != null) { 120e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Collection<X509Certificate> caChain = toCertificates(caBytes); 121e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 122e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList = new Certificate[caChain.size() + 1]; 123e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 124e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Iterator<X509Certificate> it = caChain.iterator(); 125e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int i = 1; 126e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root while (it.hasNext()) { 127e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList[i++] = it.next(); 128e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 129e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 130e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList = new Certificate[1]; 131e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 132e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 133e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root caList[0] = leaf; 134e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 135e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return caList; 136e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 137e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 138e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 139e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Certificate engineGetCertificate(String alias) { 140a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 141a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 142a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 143a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 144903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias); 145903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (encodedCert != null) { 146903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return getCertificateForPrivateKeyEntry(alias, encodedCert); 147e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 148e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 149903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias); 150903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (encodedCert != null) { 151903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return getCertificateForTrustedCertificateEntry(encodedCert); 152e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 153e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 154903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // This entry/alias does not contain a certificate. 155e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 156e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 157e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 158903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) { 159903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // For this certificate there shouldn't be a private key in this KeyStore entry. Thus, 160903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // there's no need to wrap this certificate as opposed to the certificate associated with 161903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // a private key entry. 162903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return toCertificate(encodedCert); 163903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 164903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 165903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) { 166903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // All crypto algorithms offered by Android Keystore for its private keys must also 167903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // be offered for the corresponding public keys stored in the Android Keystore. The 168903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // complication is that the underlying keystore service operates only on full key pairs, 169903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // rather than just public keys or private keys. As a result, Android Keystore-backed 170903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // crypto can only be offered for public keys for which keystore contains the 171903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // corresponding private key. This is not the case for certificate-only entries (e.g., 172903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // trusted certificates). 173903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // 174903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // getCertificate().getPublicKey() is the only way to obtain the public key 175903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // corresponding to the private key stored in the KeyStore. Thus, we need to make sure 176903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // that the returned public key points to the underlying key pair / private key 177903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // when available. 178903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 179903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin X509Certificate cert = toCertificate(encodedCert); 180903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (cert == null) { 181903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // Failed to parse the certificate. 182903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return null; 183903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 184903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 185903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; 186903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin if (mKeyStore.contains(privateKeyAlias)) { 187903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // As expected, keystore contains the private key corresponding to this public key. Wrap 188903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // the certificate so that its getPublicKey method returns an Android Keystore 189903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // PublicKey. This key will delegate crypto operations involving this public key to 190903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // Android Keystore when higher-priority providers do not offer these crypto 191903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // operations for this key. 192903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return wrapIntoKeyStoreCertificate(privateKeyAlias, cert); 193903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } else { 194903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // This KeyStore entry/alias is supposed to contain the private key corresponding to 195903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // the public key in this certificate, but it does not for some reason. It's probably a 196903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // bug. Let other providers handle crypto operations involving the public key returned 197903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin // by this certificate's getPublicKey. 198903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin return cert; 199903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 200903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin } 201903d0fb98817dca284a640dbc853c7fcbbdc8313Alex Klyubin 2024a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin /** 2034a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key 2044a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * returned by the certificate contains information about the alias of the private key in 2054a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * keystore. This is needed so that Android Keystore crypto operations using public keys can 2064a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * find out which key alias to use. These operations cannot work without an alias. 2074a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin */ 2084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate( 2094a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin String privateKeyAlias, X509Certificate certificate) { 2104a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (certificate != null) 2114a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin ? new KeyStoreX509Certificate(privateKeyAlias, certificate) : null; 2124a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 2134a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin 214e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private static X509Certificate toCertificate(byte[] bytes) { 215e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 216e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 2174a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (X509Certificate) certFactory.generateCertificate( 2184a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin new ByteArrayInputStream(bytes)); 219e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateException e) { 220e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.w(NAME, "Couldn't parse certificate in keystore", e); 221e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 222e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 223e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 224e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 225e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @SuppressWarnings("unchecked") 226e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private static Collection<X509Certificate> toCertificates(byte[] bytes) { 227e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 228e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 2294a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return (Collection<X509Certificate>) certFactory.generateCertificates( 2304a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin new ByteArrayInputStream(bytes)); 231e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateException e) { 232e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.w(NAME, "Couldn't parse certificates in keystore", e); 233e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new ArrayList<X509Certificate>(); 234e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 235e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 236e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 237e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private Date getModificationDate(String alias) { 238e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final long epochMillis = mKeyStore.getmtime(alias); 239e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (epochMillis == -1L) { 240e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 241e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 242e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 243e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new Date(epochMillis); 244e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 245e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 246e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 247e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Date engineGetCreationDate(String alias) { 248a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 249a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 250a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 251a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 252e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias); 253e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (d != null) { 254e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return d; 255e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 256e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 257baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin d = getModificationDate(Credentials.USER_SECRET_KEY + alias); 258baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (d != null) { 259baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return d; 260baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 261baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 262e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root d = getModificationDate(Credentials.USER_CERTIFICATE + alias); 263e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (d != null) { 264e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return d; 265e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 266e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 267e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return getModificationDate(Credentials.CA_CERTIFICATE + alias); 268e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 269e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 270e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 271e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 272e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throws KeyStoreException { 273e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((password != null) && (password.length > 0)) { 274e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("entries cannot be protected with passwords"); 275e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 276e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 277e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (key instanceof PrivateKey) { 2782eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); 279baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (key instanceof SecretKey) { 280baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin setSecretKeyEntry(alias, (SecretKey) key, null); 281e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 282baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Only PrivateKey and SecretKey are supported"); 283e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 284e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 285e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 2863ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key) 2873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throws KeyStoreException { 2883ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin String keyAlgorithm = key.getAlgorithm(); 2893ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProtection.Builder specBuilder; 2903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 2913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder = 2923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin new KeyProtection.Builder( 2933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); 294dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin // Authorized to be used with any digest (including no digest). 295dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin specBuilder.setDigests(KeyProperties.DIGEST_NONE); 2963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 2973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder = 2983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin new KeyProtection.Builder( 2993ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.PURPOSE_ENCRYPT 3003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_DECRYPT 3013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_SIGN 3023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin | KeyProperties.PURPOSE_VERIFY); 303dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin // Authorized to be used with any digest (including no digest). 304dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin specBuilder.setDigests(KeyProperties.DIGEST_NONE); 3052e3aaa7dc1c40c0145e8d52422f2fb65799a3a60Alex Klyubin // Authorized to be used with any encryption and signature padding scheme (including no 3062e3aaa7dc1c40c0145e8d52422f2fb65799a3a60Alex Klyubin // padding). 3073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setEncryptionPaddings( 308dcf3d35f23ba46f17251d4181eee4675691f3380Alex Klyubin KeyProperties.ENCRYPTION_PADDING_NONE); 3093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Disable randomized encryption requirement to support encryption padding NONE 3103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // above. 3113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setRandomizedEncryptionRequired(false); 3123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 3133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm); 3143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 3153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin specBuilder.setUserAuthenticationRequired(false); 3163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 3173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin return specBuilder.build(); 3183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 3193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 3202eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, 3213f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { 32296481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin int flags = 0; 3233f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyProtection spec; 3243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (param == null) { 3253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec = getLegacyKeyProtectionParameter(key); 3263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else if (param instanceof KeyStoreParameter) { 3273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec = getLegacyKeyProtectionParameter(key); 3283f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyStoreParameter legacySpec = (KeyStoreParameter) param; 3293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (legacySpec.isEncryptionRequired()) { 3303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin flags = KeyStore.FLAG_ENCRYPTED; 3313f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } 3323f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } else if (param instanceof KeyProtection) { 3333f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin spec = (KeyProtection) param; 3343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 3353f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin throw new KeyStoreException( 3363f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin "Unsupported protection parameter class:" + param.getClass().getName() 3373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". Supported: " + KeyProtection.class.getName() + ", " 3383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + KeyStoreParameter.class.getName()); 339e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 340e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 341e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Make sure the chain exists since this is a PrivateKey 342e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((chain == null) || (chain.length == 0)) { 343e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Must supply at least one Certificate with PrivateKey"); 344e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 345e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 346e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Do chain type checking. 347e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root X509Certificate[] x509chain = new X509Certificate[chain.length]; 348e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < chain.length; i++) { 349e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (!"X.509".equals(chain[i].getType())) { 350e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 351e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root + i); 352e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 353e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 354e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (!(chain[i] instanceof X509Certificate)) { 355e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #" 356e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root + i); 357e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 358e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 359e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root x509chain[i] = (X509Certificate) chain[i]; 360e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 361e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 362e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] userCertBytes; 363e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 364e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root userCertBytes = x509chain[0].getEncoded(); 365e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 3663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to encode certificate #0", e); 367e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 368e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 369e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 370e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * If we have a chain, store it in the CA certificate slot for this 371e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * alias as concatenated DER-encoded certificates. These can be 372e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * deserialized by {@link CertificateFactory#generateCertificates}. 373e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 374e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] chainBytes; 375e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (chain.length > 1) { 376e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 377e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...} 378e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * so we only need the certificates starting at index 1. 379e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 380e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[][] certsBytes = new byte[x509chain.length - 1][]; 381e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int totalCertLength = 0; 382e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < certsBytes.length; i++) { 383e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 384e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root certsBytes[i] = x509chain[i + 1].getEncoded(); 385e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root totalCertLength += certsBytes[i].length; 386e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 3873ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to encode certificate #" + i, e); 388e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 389e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 390e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 391e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 392e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Serialize this into one byte array so we can later call 393e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * CertificateFactory#generateCertificates to recover them. 394e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 395e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root chainBytes = new byte[totalCertLength]; 396e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root int outputOffset = 0; 397e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (int i = 0; i < certsBytes.length; i++) { 398e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final int certLength = certsBytes[i].length; 399e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength); 400e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root outputOffset += certLength; 401e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root certsBytes[i] = null; 402e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 403e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } else { 404e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root chainBytes = null; 405e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 406e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 4073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String pkeyAlias; 4084a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (key instanceof AndroidKeyStorePrivateKey) { 4093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkeyAlias = ((AndroidKeyStoreKey) key).getAlias(); 410802768dd86c4e8a933dbfbac2e9f1a1daa5f93faKenny Root } else { 4113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkeyAlias = null; 4123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin byte[] pkcs8EncodedPrivateKeyBytes; 4153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterArguments importArgs; 4163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final boolean shouldReplacePrivateKey; 4173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) { 4183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length()); 4193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!alias.equals(keySubalias)) { 4203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Can only replace keys with same alias: " + alias 4213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " != " + keySubalias); 4223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin shouldReplacePrivateKey = false; 4243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs = null; 4253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes = null; 4263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 4273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin shouldReplacePrivateKey = true; 4283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Make sure the PrivateKey format is the one we support. 4293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin final String keyFormat = key.getFormat(); 4303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) { 4313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException( 4323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin "Unsupported private key export format: " + keyFormat 4333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". Only private keys which export their key material in PKCS#8 format are" 4343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " supported."); 4353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Make sure we can actually encode the key. 4383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes = key.getEncoded(); 4393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (pkcs8EncodedPrivateKeyBytes == null) { 4403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Private key did not export any key material"); 4413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs = new KeymasterArguments(); 4443ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin try { 445ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, 4463ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm( 4473ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin key.getAlgorithm())); 4483ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin @KeyProperties.PurposeEnum int purposes = spec.getPurposes(); 449ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 4503ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.Purpose.allToKeymaster(purposes)); 4513ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (spec.isDigestsSpecified()) { 452ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST, 4533ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.Digest.allToKeymaster(spec.getDigests())); 4543ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4553ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 456ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, 4573ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes())); 4583ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int[] keymasterEncryptionPaddings = 4593ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.EncryptionPadding.allToKeymaster( 4603ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec.getEncryptionPaddings()); 4613ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 4623ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin && (spec.isRandomizedEncryptionRequired())) { 4633ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin for (int keymasterPadding : keymasterEncryptionPaddings) { 4643ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!KeymasterUtils 4653ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 4663ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin keymasterPadding)) { 4673ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException( 4683ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin "Randomized encryption (IND-CPA) required but is violated by" 4693ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + " encryption padding mode: " 4703ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + KeyProperties.EncryptionPadding.fromKeymaster( 4713ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin keymasterPadding) 4723ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin + ". See KeyProtection documentation."); 4733ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4743ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4753ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 476ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); 477ae6cb7aad56bb006769cd8a69b92af7236644fc1Alex Klyubin importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, 4783ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); 4793ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterUtils.addUserAuthArgs(importArgs, 4803ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec.isUserAuthenticationRequired(), 4813ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin spec.getUserAuthenticationValidityDurationSeconds()); 482d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 483d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityStart()); 484d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 485d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityForOriginationEnd()); 486d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 487d6c7799b9a8b00d160a1d2d32c7326132cbc7b7bAlex Klyubin spec.getKeyValidityForConsumptionEnd()); 488b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } catch (IllegalArgumentException | IllegalStateException e) { 489b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException(e); 4903ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4913ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 4923ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4933ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 4943ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin boolean success = false; 4953ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin try { 4963ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the private key, if necessary 4973ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (shouldReplacePrivateKey) { 4983ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Delete the stored private key and any related entries before importing the 4993ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // provided key 5003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias); 5013ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); 5023ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int errorCode = mKeyStore.importKey( 5033ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.USER_PRIVATE_KEY + alias, 5043ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin importArgs, 5053ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeymasterDefs.KM_KEY_FORMAT_PKCS8, 5063ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin pkcs8EncodedPrivateKeyBytes, 5073ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin flags, 5083ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin resultingKeyCharacteristics); 5093ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5103ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store private key", 5113ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5123ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5133ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 5143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Keep the stored private key around -- delete all other entry types 5153ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteCertificateTypesForAlias(mKeyStore, alias); 5163ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias); 5173ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5183ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5193ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the leaf certificate 5203ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes, 5213ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.UID_SELF, flags); 5223ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5233ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store certificate #0", 5243ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5253ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin 5273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin // Store the certificate chain 5283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes, 5293ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.UID_SELF, flags); 5303ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 5313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin throw new KeyStoreException("Failed to store certificate chain", 5323ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.getKeyStoreException(errorCode)); 5333ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5343ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin success = true; 5353ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } finally { 5363ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (!success) { 5373ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (shouldReplacePrivateKey) { 5383ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias); 5393ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } else { 5403ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteCertificateTypesForAlias(mKeyStore, alias); 5413ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias); 5423ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 5433ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin } 544e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 545e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 546e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 5473f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin private void setSecretKeyEntry(String entryAlias, SecretKey key, 5483f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin java.security.KeyStore.ProtectionParameter param) 549baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throws KeyStoreException { 5503f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin if ((param != null) && (!(param instanceof KeyProtection))) { 5513f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin throw new KeyStoreException( 5523f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin "Unsupported protection parameter class: " + param.getClass().getName() 5533f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin + ". Supported: " + KeyProtection.class.getName()); 5543f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin } 5553f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin KeyProtection params = (KeyProtection) param; 5563f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin 557dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin if (key instanceof AndroidKeyStoreSecretKey) { 558baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot 559baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // overwrite its own entry. 560dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubin String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); 561baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyAliasInKeystore == null) { 562baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("KeyStore-backed secret key does not have an alias"); 563baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 564baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { 565baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("KeyStore-backed secret key has invalid alias: " 566baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + keyAliasInKeystore); 567baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 568baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyEntryAlias = 569baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 570baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (!entryAlias.equals(keyEntryAlias)) { 571baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Can only replace KeyStore-backed keys with same" 572baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " alias: " + entryAlias + " != " + keyEntryAlias); 573baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 574baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // This is the entry where this key is already stored. No need to do anything. 575baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (params != null) { 576baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Modifying KeyStore-backed key using protection" 577baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " parameters not supported"); 578baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 579baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return; 580baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 581baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 582baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (params == null) { 583baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 584baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Protection parameters must be specified when importing a symmetric key"); 585baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 586baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 587baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin // Not a KeyStore-backed secret key -- import its key material into keystore. 588baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyExportFormat = key.getFormat(); 589baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyExportFormat == null) { 590baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 591baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Only secret keys that export their key material are supported"); 592baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (!"RAW".equals(keyExportFormat)) { 593baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 594baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Unsupported secret key material export format: " + keyExportFormat); 595baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 596baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin byte[] keyMaterial = key.getEncoded(); 597baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (keyMaterial == null) { 598baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Key did not export its key material despite supporting" 599baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + " RAW format export"); 600baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 601baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 602baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin KeymasterArguments args = new KeymasterArguments(); 603b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin try { 604b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int keymasterAlgorithm = 605b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm()); 606b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); 607b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin 608b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterDigests; 609c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { 610c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm 611c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one 612c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // digest, we don't let import parameters override the digest implied by the key. 613c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // If the parameters specify digests at all, they must specify only one digest, the 614c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // only implied by key algorithm. 615c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin int keymasterImpliedDigest = 616c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm()); 617c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (keymasterImpliedDigest == -1) { 618c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin throw new ProviderException( 619c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin "HMAC key algorithm digest unknown for key algorithm " 620c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + key.getAlgorithm()); 621c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin } 622c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin keymasterDigests = new int[] {keymasterImpliedDigest}; 623c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (params.isDigestsSpecified()) { 624c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // Digest(s) explicitly specified in params -- check that the list consists of 625c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // exactly one digest, the one implied by key algorithm. 626c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin int[] keymasterDigestsFromParams = 627c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin KeyProperties.Digest.allToKeymaster(params.getDigests()); 628c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if ((keymasterDigestsFromParams.length != 1) 629c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) { 630c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin throw new KeyStoreException( 631c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin "Unsupported digests specification: " 632c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + Arrays.asList(params.getDigests()) + ". Only " 633c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest) 634c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin + " supported for HMAC key algorithm " + key.getAlgorithm()); 635cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin } 636cbdd377db84d58dff31a380e85fdd94a27d863c6Alex Klyubin } 637c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin } else { 638c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin // Key algorithm does not imply a digest. 639c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin if (params.isDigestsSpecified()) { 640c58153b2d7418f44f2b0e397478be808e91decefAlex Klyubin keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests()); 641b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } else { 642b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin keymasterDigests = EmptyArray.INT; 643b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 644c46e9e7da4558f6bc99262361fd1ca35c3a44090Alex Klyubin } 645b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); 646baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 647b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin @KeyProperties.PurposeEnum int purposes = params.getPurposes(); 648b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterBlockModes = 649b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); 650b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 651b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin && (params.isRandomizedEncryptionRequired())) { 652b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin for (int keymasterBlockMode : keymasterBlockModes) { 653b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 654b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin keymasterBlockMode)) { 655b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException( 656b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin "Randomized encryption (IND-CPA) required but may be violated by" 657b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + " block mode: " 658b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) 659b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin + ". See KeyProtection documentation."); 660b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 6615927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin } 662f853f649981ed3cf2f1fbf1363a0932e9736daf6Alex Klyubin } 663b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, 664b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeyProperties.Purpose.allToKeymaster(purposes)); 665b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); 666b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (params.getSignaturePaddings().length > 0) { 667b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException("Signature paddings not supported for symmetric keys"); 668b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 669b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( 670b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getEncryptionPaddings()); 671b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); 672b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin KeymasterUtils.addUserAuthArgs(args, 673b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.isUserAuthenticationRequired(), 674b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getUserAuthenticationValidityDurationSeconds()); 675b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, 676b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityStart()); 677b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 678b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityForOriginationEnd()); 679b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 680b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin params.getKeyValidityForConsumptionEnd()); 681b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin 682b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0) 683b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin && (!params.isRandomizedEncryptionRequired())) { 684b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin // Permit caller-provided IV when encrypting with this key 685b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 686b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } 687b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin } catch (IllegalArgumentException | IllegalStateException e) { 688b6e628644a981b8077b3755b9def4550ff4a46a0Alex Klyubin throw new KeyStoreException(e); 689b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin } 690b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin 691baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias); 692baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias; 693baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin int errorCode = mKeyStore.importKey( 694baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyAliasInKeystore, 695baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin args, 696baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin KeymasterDefs.KM_KEY_FORMAT_RAW, 697baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin keyMaterial, 69896481c3ddc6c58cfcad2a5cb9325ee2b24b0c540Alex Klyubin 0, // flags 699baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin new KeyCharacteristics()); 7003ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (errorCode != KeyStore.NO_ERROR) { 701baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException("Failed to import secret key. Keystore error code: " 702baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + errorCode); 703baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 704baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 705baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 706e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 707e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) 708e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throws KeyStoreException { 709a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new KeyStoreException("Operation not supported because key encoding is unknown"); 710e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 711e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 712e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 713e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { 714e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (isKeyEntry(alias)) { 715e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Entry exists and is not a trusted certificate"); 716e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 717e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 718a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root // We can't set something to null. 719a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (cert == null) { 720a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("cert == null"); 721a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 722a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 723e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final byte[] encoded; 724e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root try { 725e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root encoded = cert.getEncoded(); 726e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } catch (CertificateEncodingException e) { 727e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException(e); 728e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 729e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 7302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, 7313ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin KeyStore.UID_SELF, KeyStore.FLAG_NONE)) { 732e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); 733e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 734e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 735e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 736e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 737e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineDeleteEntry(String alias) throws KeyStoreException { 7386c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin if (!engineContainsAlias(alias)) { 739a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root return; 740a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 7416c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin // At least one entry corresponding to this alias exists in keystore 742a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 743db026710ec0adcf7f72dfb24c65d38a882ee26d8Kenny Root if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) { 7446c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin throw new KeyStoreException("Failed to delete entry: " + alias); 745e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 746e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 747e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 748e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private Set<String> getUniqueAliases() { 7494350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin final String[] rawAliases = mKeyStore.list(""); 750e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (rawAliases == null) { 751e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return new HashSet<String>(); 752e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 753e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 754e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Set<String> aliases = new HashSet<String>(rawAliases.length); 755e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root for (String alias : rawAliases) { 756e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final int idx = alias.indexOf('_'); 757e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if ((idx == -1) || (alias.length() <= idx)) { 758e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root Log.e(NAME, "invalid alias: " + alias); 759e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root continue; 760e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 761e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 762e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root aliases.add(new String(alias.substring(idx + 1))); 763e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 764e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 765e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return aliases; 766e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 767e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 768e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 769e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public Enumeration<String> engineAliases() { 770e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return Collections.enumeration(getUniqueAliases()); 771e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 772e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 773e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 774e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineContainsAlias(String alias) { 775a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 776a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 777a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 778a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 779e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias) 780baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias) 781e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias) 782e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias); 783e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 784e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 785e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 786e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public int engineSize() { 787e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return getUniqueAliases().size(); 788e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 789e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 790e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 791e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineIsKeyEntry(String alias) { 792e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return isKeyEntry(alias); 793e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 794e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 795e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root private boolean isKeyEntry(String alias) { 796baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias); 797baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 798baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 799baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin private boolean isPrivateKeyEntry(String alias) { 800a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 801a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 802a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 803a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 804e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias); 805e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 806e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 807baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin private boolean isSecretKeyEntry(String alias) { 808baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin if (alias == null) { 809baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new NullPointerException("alias == null"); 810baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 811baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 812baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias); 813baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } 814baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin 815a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root private boolean isCertificateEntry(String alias) { 816a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root if (alias == null) { 817a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root throw new NullPointerException("alias == null"); 818a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 819a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 820a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias); 821a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root } 822a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root 823e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 824e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public boolean engineIsCertificateEntry(String alias) { 825a4640c082c8ccf66ebfb50ace5747409ab6aee55Kenny Root return !isKeyEntry(alias) && isCertificateEntry(alias); 826e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 827e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 828e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 829e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public String engineGetCertificateAlias(Certificate cert) { 830e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (cert == null) { 831e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 832e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 8334a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (!"X.509".equalsIgnoreCase(cert.getType())) { 8344a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin // Only X.509 certificates supported 8354a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8364a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 8374a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin byte[] targetCertBytes; 8384a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin try { 8394a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin targetCertBytes = cert.getEncoded(); 8404a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } catch (CertificateEncodingException e) { 8414a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8424a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 8434a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (targetCertBytes == null) { 8444a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return null; 8454a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 846e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 847e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root final Set<String> nonCaEntries = new HashSet<String>(); 848e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 849e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 850e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation 851e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * says to only compare the first certificate in the chain which is 852e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * equivalent to the USER_CERTIFICATE prefix for the Android keystore 853e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * convention. 854e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 8554350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE); 85678ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certAliases != null) { 85778ad849163a7b01073b46fbd7d818392720005d1Kenny Root for (String alias : certAliases) { 85878ad849163a7b01073b46fbd7d818392720005d1Kenny Root final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias); 85978ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certBytes == null) { 86078ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 86178ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 862e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 86378ad849163a7b01073b46fbd7d818392720005d1Kenny Root nonCaEntries.add(alias); 864e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 8654a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (Arrays.equals(certBytes, targetCertBytes)) { 86678ad849163a7b01073b46fbd7d818392720005d1Kenny Root return alias; 86778ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 868e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 869e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 870e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 871e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root /* 872e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * Look at all the TrustedCertificateEntry types. Skip all the 873e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root * PrivateKeyEntry we looked at above. 874e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root */ 8754350babc028822e8905190d88a9f5b8c6ffce8ecAlex Klyubin final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE); 87678ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certAliases != null) { 87778ad849163a7b01073b46fbd7d818392720005d1Kenny Root for (String alias : caAliases) { 87878ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (nonCaEntries.contains(alias)) { 87978ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 88078ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 881e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 88278ad849163a7b01073b46fbd7d818392720005d1Kenny Root final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias); 88378ad849163a7b01073b46fbd7d818392720005d1Kenny Root if (certBytes == null) { 88478ad849163a7b01073b46fbd7d818392720005d1Kenny Root continue; 88578ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 886e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 8874a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin if (Arrays.equals(certBytes, targetCertBytes)) { 88878ad849163a7b01073b46fbd7d818392720005d1Kenny Root return alias; 88978ad849163a7b01073b46fbd7d818392720005d1Kenny Root } 890e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 891e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 892e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 893e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root return null; 894e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 895e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 896e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 897e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineStore(OutputStream stream, char[] password) throws IOException, 898e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root NoSuchAlgorithmException, CertificateException { 899e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream"); 900e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 901e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 902e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root @Override 903e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root public void engineLoad(InputStream stream, char[] password) throws IOException, 904e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root NoSuchAlgorithmException, CertificateException { 905e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (stream != null) { 906e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new IllegalArgumentException("InputStream not supported"); 907e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 908e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 909e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root if (password != null) { 910e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root throw new IllegalArgumentException("password not supported"); 911e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 912e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 913e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root // Unfortunate name collision. 9143ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin mKeyStore = KeyStore.getInstance(); 915e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root } 916e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root 9172eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root @Override 9182eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) 9192eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root throws KeyStoreException { 9202eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root if (entry == null) { 9212eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root throw new KeyStoreException("entry == null"); 9222eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9232eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9246c03bf523d6b0e4edfade726ed0ee1c49c4d0f69Alex Klyubin Credentials.deleteAllTypesForAlias(mKeyStore, alias); 9252eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9263ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) { 9273ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin java.security.KeyStore.TrustedCertificateEntry trE = 9283ceb1a04b44539c2b2c3afec6df487fe128911f2Alex Klyubin (java.security.KeyStore.TrustedCertificateEntry) entry; 9292eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root engineSetCertificateEntry(alias, trE.getTrustedCertificate()); 9302eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root return; 9312eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9322eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9332eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root if (entry instanceof PrivateKeyEntry) { 9342eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root PrivateKeyEntry prE = (PrivateKeyEntry) entry; 9353f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param); 936baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else if (entry instanceof SecretKeyEntry) { 937baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin SecretKeyEntry secE = (SecretKeyEntry) entry; 9383f8d4d840894468f2be8a5b56ff266cef2d71c50Alex Klyubin setSecretKeyEntry(alias, secE.getSecretKey(), param); 939baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin } else { 940baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin throw new KeyStoreException( 941baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" 942baf2838fd2c7ddf517bd5bd9917551a4706af5b6Alex Klyubin + "; was " + entry); 9432eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9442eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root } 9452eeda7286f3c7cb79f7eb71ae6464cad213d12a3Kenny Root 9464a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin /** 9474a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from 9484a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain 9494a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * can find out which keystore private key entry to use. This is needed so that Android Keystore 9504a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * crypto operations using public keys can find out which key alias to use. These operations 9514a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin * require an alias. 9524a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin */ 9534a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin static class KeyStoreX509Certificate extends DelegatingX509Certificate { 9544a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin private final String mPrivateKeyAlias; 9554a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin KeyStoreX509Certificate(String privateKeyAlias, X509Certificate delegate) { 9564a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin super(delegate); 9574a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin mPrivateKeyAlias = privateKeyAlias; 9584a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 9594a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin 9604a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin @Override 9614a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin public PublicKey getPublicKey() { 9624a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin PublicKey original = super.getPublicKey(); 9634a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey( 9644a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin mPrivateKeyAlias, original.getAlgorithm(), original.getEncoded()); 9654a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 9664a0ff7ca984d29bd34b02e54441957cad65e8b53Alex Klyubin } 967e29df16cb57b69995df597e8a6d95d986c1c43fcKenny Root} 968