Using the ICS KeyChain API
Update: Sample app code is now available on github.
The recently released Android 4.0 (Ice Cream Sandwich, ICS) introduces a new, unified UI for both tablets and handsets, lots of 'people-centric' communication and sharing features and other convenient improvements such as a better camera app and the much-hyped face unlock. Since everyone is talking about those, we will have a look at some of the less-user visible, but nonetheless important security-related improvements.
Android is often said to be missing crucial security features to be seriously accepted in the corporate world, which has long been the domain of RIM's BlackBerry. Two of those missing features were the ability to control the system's trusted CA certificates and offer a centralized secure credential storage. Since many companies use private PKI's, the ability to install trusted certificates system-wide is essential for using corporate services secured by those PKI's. Until now, the only way to use those was to embed the needed CA certificates in each application and create custom
The
This will prompt you for the PKCS#12 password in order to extract and parse the key and certificate. If the password is correct, you will be prompted for a 'certificate name' as shown in the screenshot below. If the PKCS#12 has a friendly name attribute it will be shown as the default, if not you will just get a long hexadecimal hash string. The string you enter here is the key/certificate alias you will use to access those later via the
To use a private key stored in the system credential storage, you need to call
The first parameter is the current context, the second -- the callback to invoke, and the third and forth specify the acceptable keys (RSA, DSA or null for any) and acceptable certificate issuers for the certificate matching the private key (Edit: it turns out both
In order to get a reference to a private key, you need to call the
We first get the private key and certificate chain using the key alias and then create and verify a signature to check if the key is actually usable. Since we are using a self-signed certificate the 'chain' consists of a single entry, but for a certificate signed by a CA you will need to find the actual end entity certificate in the returned array.
Installing a CA certificate is not very different from installing a PKCS#12 file: you load the certificate in a byte array and pass it as an extra to the install intent.
Android will parse the certificate, and if it's
After the certificate is imported, it will show up in the 'Trusted credentials' screen's 'User' tab (Settings->Security->Trusted credentials). Tapping the certificate entry displays a details dialog, where you can (finally!) check the subject, issuer, validity period, serial number and SHA-1/SHA-256 fingerprints. You can also remove the certificate by pressing the 'Remove' button (scroll down to display it).
While you can delete individual CA certificates, there is no way to delete individual keys and user certificates. You can delete all by using the 'Clear credentials' option in the Credential storage section of the security settings. Another thing to note is that, as long as you have keys in the credential storage, you cannot remove the screen lock, since it is used to protect access to the keystore. In previous Android versions, there was a separate 'credential storage password', but it seems in ICS they decided to simplify things by using the screen lock password to protect credential storage as well.
The newly introduced
That wraps the first part of our Android keystore introduction. In the next part we will look into all that is hidden behind the
The recently released Android 4.0 (Ice Cream Sandwich, ICS) introduces a new, unified UI for both tablets and handsets, lots of 'people-centric' communication and sharing features and other convenient improvements such as a better camera app and the much-hyped face unlock. Since everyone is talking about those, we will have a look at some of the less-user visible, but nonetheless important security-related improvements.
Android is often said to be missing crucial security features to be seriously accepted in the corporate world, which has long been the domain of RIM's BlackBerry. Two of those missing features were the ability to control the system's trusted CA certificates and offer a centralized secure credential storage. Since many companies use private PKI's, the ability to install trusted certificates system-wide is essential for using corporate services secured by those PKI's. Until now, the only way to use those was to embed the needed CA certificates in each application and create custom
TrustStore
s to be able to connect using SSL. A system-wide credential storage has actually been available for a while, but it was only usable by the built-in VPN and WiFi (EAP) clients. One could install a private key/certificate pair using the Settings app, but there was no public API to access the installed keys from applications. ICS offers SDK API's for both trusted certificate management and the secure credential storage via the KeyChain
class. We will have a look at how it is used in the following sections.The
KeyChain
class is deceptively simple: it offers only 4 public static methods, but those are sufficient to do most certificate-related tasks. Let's first see how one would install a private key/certificate pair and use those to sign and verify some data. The KeyChain
API lets you install a private key/certificate pair bundled in a PKCS#12 file. Instead of offering an API to directly install the key and certificate, KeyChain
provides a factory method, createInstallIntent()
that returns a system intent to parse and install keys/certificates (that is actually the same intent offered by the Settings app in previous versions). To install a PKCS#12 file, you have to read it to a binary array, store it under the EXTRA_PKCS12
key in the intent's extras, and start the associated activity:Intent intent = KeyChain.createInstallIntent();
byte[] p12 = readFile("keystore-test.pfx");
intent.putExtra(KeyChain.EXTRA_PKCS12, p12);
startActivity(intent);
This will prompt you for the PKCS#12 password in order to extract and parse the key and certificate. If the password is correct, you will be prompted for a 'certificate name' as shown in the screenshot below. If the PKCS#12 has a friendly name attribute it will be shown as the default, if not you will just get a long hexadecimal hash string. The string you enter here is the key/certificate alias you will use to access those later via the
KeyChain
API. You will be prompted to set a lock screen PIN or password to protect the credential storage if you haven't already set one.To use a private key stored in the system credential storage, you need to call
KeyChain.choosePrivateKeyAlias()
and provide a callback implementation that receives the selected alias:public class KeystoreTest extends Activity implements OnClickListener,
KeyChainAliasCallback {
@Override
public void onClick(View v) {
KeyChain.choosePrivateKeyAlias(this, this,
new String[] { "RSA" }, null, null, -1, null);
}
@Override
public void alias(final String alias) {
Log.d(TAG, "Thread: " + Thread.currentThread().getName());
Log.d(TAG, "selected alias: " + alias);
}
}
The first parameter is the current context, the second -- the callback to invoke, and the third and forth specify the acceptable keys (RSA, DSA or null for any) and acceptable certificate issuers for the certificate matching the private key (Edit: it turns out both
keyTypes
and issuers
are currently unused, so just pass null
). The next two parameters are the host and port number of the server requesting a certificate, and the last one is the alias to preselect. We leave all but the key type as unspecified (null
or -1
) here to be able to select from all available certificates. One thing to note here is that the alias()
callback will not be called on the main thread, so you shouldn't try to directly manipulate the UI (it is called on a binder thread). Using the key requires user authorization, so Android will display a key selection dialog which also serves to allow access to the selected key.In order to get a reference to a private key, you need to call the
KeyChain.getPrivateKey()
method passing the key alias name received in the previous step. This doesn't seem to be documented but if you try to call this method on the main thread you will get an exception saying that this may 'lead to a deadlock'. Here we call it on a background thread using AsyncTask
(which is almost always the right thing to do when dealing with potentially time-consuming I/O operations).new AsyncTask<Void, Void, Boolean>() {
private Exception error;
@Override
protected Boolean doInBackground(Void... arg) {
try {
PrivateKey pk = KeyChain.getPrivateKey(ctx,
alias);
X509Certificate[] chain = KeyChain.getCertificateChain(ctx,
alias);
byte[] data = "foobar".getBytes("ASCII");
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(pk);
sig.update(data);
byte[] signed = sig.sign();
PublicKey pubk = chain[0].getPublicKey();
sig.initVerify(pubk);
sig.update(data);
boolean valid = sig.verify(signed);
Log.d(TAG, "signature is valid: " + valid);
return valid;
} catch (Exception e) {
e.printStackTrace();
error = e;
return null;
}
}
@Override
protected void onPostExecute(Boolean valid) {
if (error != null) {
Toast.makeText(ctx, "Error: " + error.getMessage(),
Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(ctx, "Signature is valid: " + valid,
Toast.LENGTH_SHORT).show();
}
}.execute();
We first get the private key and certificate chain using the key alias and then create and verify a signature to check if the key is actually usable. Since we are using a self-signed certificate the 'chain' consists of a single entry, but for a certificate signed by a CA you will need to find the actual end entity certificate in the returned array.
Installing a CA certificate is not very different from installing a PKCS#12 file: you load the certificate in a byte array and pass it as an extra to the install intent.
Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert);
startActivity(intent);
Android will parse the certificate, and if it's
Basic Constraints
extension is set to CA:TRUE
it will consider it a CA certificate and import it into the user trust store. You will need to authenticate to import the certificate, but the funny thing is that the import dialog does not show neither the certificate DN, nor its hash value. The user has no way of knowing what they are importing, until it's done. Very few people will bother to actually check, so this could be a potential security threat: malicious applications might trick people into installing rogue certificates. Here's how the import dialog looks:After the certificate is imported, it will show up in the 'Trusted credentials' screen's 'User' tab (Settings->Security->Trusted credentials). Tapping the certificate entry displays a details dialog, where you can (finally!) check the subject, issuer, validity period, serial number and SHA-1/SHA-256 fingerprints. You can also remove the certificate by pressing the 'Remove' button (scroll down to display it).
While you can delete individual CA certificates, there is no way to delete individual keys and user certificates. You can delete all by using the 'Clear credentials' option in the Credential storage section of the security settings. Another thing to note is that, as long as you have keys in the credential storage, you cannot remove the screen lock, since it is used to protect access to the keystore. In previous Android versions, there was a separate 'credential storage password', but it seems in ICS they decided to simplify things by using the screen lock password to protect credential storage as well.
The newly introduced
KeyChain
API lets you install and access private keys in a centralized and secure credential storage, as well as add system-wide trusted certificates. It doesn't provide low-level access to the underlying keystore, utilizing the Android intent dispatching mechanism instead to call a system activity that does the actual work. The CA certificate install dialog is missing a crucial feature (displaying details about the certificate), but all in all, providing the access to the system keystore service is a step in the right direction.That wraps the first part of our Android keystore introduction. In the next part we will look into all that is hidden behind the
KeyChain
facade, and try to give some details about the underlying implementation.
Comments
The explanation you have given is very easy-to-understand. Thanks for this post.
Is there any post you can direct me to on how to use the private key-certificate store on Android versions before the ICS?
http://www.g-loaded.eu/2005/11/10/be-your-own-ca/
To create a PKCS#12 file from the key and the cert, you can use the following command:
$ openssl pkcs12 -export -in keystore-test.pem -inkey keystore-test.key -out kestore-test.pfx
Very useful tutorial. However, I have one question. How to use existing CA Certs ? For example, if an application wants to use Verisign ? How should it be done ?
I have imported the PKCS#12 containing private key and certificate.
Then i run your keystore-test app, select "Use private key".
After being prompted to select the certificate i choose the one just installed.
After:
protected Boolean[] doInBackground(Void... arg0) {
PrivateKey pk = KeyChain.getPrivateKey(ctx, alias);
I've added:
Log.d(TAG, "EncodedPrivateKey: " + pk.toString());
And it gives me the full content or the private key.
Does it mean that any application, once allowed by user (in the cert. selection dialog), can read any private key installed from .pfx file?
Is the following scenario possible by standard Android means - "administrator" installing the cert.+private key from the .pfx and the rights to use it are limited to the single app?
I have generated a pkcs12 file and I could install on 3.2 and was able to connect using EAP-TLS to my wifi but the same file when installed on 4.1 nexus device is not working.
Do you know if I am missing any other steps
thanks
Pal
http://code.google.com/p/android/issues/detail?id=34212
I want to push my private key and certificate chain to Android KeyStore. I see method Keystore.setKeyEntry, but in Android.security, it do not implement. In this case, i don't wat to use pfx file!
thanks
Chen
I want to trust HTTPS that does not have a CA in it's self-signed certificate. Can you please direct me what to do and how to achieve this ?
Thanks in advance,
Kalin.
Problem is I still cannot log in to the server on my android 4.0.4 phone. Using the default browser I get a warning about the cert: "Site name does not match the certificate name"
In logcat my app states hostname not verified. Any ideas?
Your certificate is already trusted, so you just need to make sure you have the correct hostname in the CN. If you are accessing the site by IP address, you need to add it to the certificate extensions.
thanks for sharing the example. I am very much able to sign and verify my data.
Now i want to generate key pair and make a pkcs10 request. Is there any support for it in android API or should i use openSSL for this?
Finally my aim is to generate pkcs7 signatures.
Anticipating a quick reply,
thanks
Rama
As reported by developers in many other forums - there is a bug in Android 4.1 in which calling KeyChain.getPrivateKey() fails because of memory management issues.
I have seen your comments in some forums in which you have reported that this issue was fixed in AOSP.
My questions is whether there is any way of working around this issue on 4.1 devices with a ROM that does not include the fix you have mentioned?
Thanks,
Nadav
i want to access installed certificates which store in trusted credential on my device.......through programming how could i see those install certificates....
I have a question on Keychain. Can i import a keypair or symmetric key or a password with protection phrase into keychain instead of keystore and retrieve them back? If we can please explain me how
Thanks.
I got the answer here: http://nelenkov.blogspot.in/2012/04/using-password-based-encryption-on.html
thank you very much. Your blog is helping me alot.
I need to create a bks keystore in android phone's internal memory and it should be accessible by all other apps running on that mobile. (I am able to create it on sdcard or i can create it inside my app)
Scenario: I will create a customized bks, other developer or other APP will use my bks to do some crypto operations and some other APP or developer will use my keystore to store OTPs etc..
Is it practically possbile, if so please help me here...
Thanks you.
Is it possible to create a private key using android security provider and then import it into a keychain? Does it have to be pkcs12?
Let's say I pass a certificate as an extra to the Intent created by KeyChain.createInstallIntent() and the user gives it a name and installs it. How can I get back at that certificate at any later point in time to, for example, get the public key and encrypt stuff with it?
Thanks!
A password protected client p12 certificate can be stored using a) KeyChain createInstallIntent API (available in ICS+) or b) KeyStore load API (available since API 1). In first case, you need user to provide alias and password through UI. In second case, this is done silently and an alias is automatically generated.
What is the difference between the two? Is it that when using Keychain API, it is stored in global keystore where it can be accessed by other applications. And, when using Keystore API, this certificate can only be accessed locally by the application that called load method? So, if I need to use the certificate only in my application, I should use the KeyStore API.
Thanks!
Which one you should use depends on what you are trying to do.
For API Level 16 Intent of action "ACTION_STORAGE_CHANGED" is broadcasted when the credentials are erased.
What is the best way to tackle for pre API Level 16 apps ? Do we need to try to fetch the certificates and then handle it through the KeyChainException ?
cheers,
Saurav
This thread is related to a problem Im trying to solve. Basically I need to store a secret key locally what is the most secure way to do this. I know that the only 100% solution is to not store the information locally but aside from that what is the best method? At first I thought the KeyChain class would help but there should be no UI operation involved in accessing the secret key. Thanks.
I am trying to implement mutual authentication on Android platform. By default, only server certificate verification is needed. But for those servers have configure client certificate auth, the client side needs to support check the requirement (or exceptions when requesting to server) and read client certificate in order to setup a SSLContext. My problem is how to make the client side to detect such exceptions? I learnt that IOS platform has callbacks like, SessionDidReceiveAuthenticationChallengeBlock and NSURLAuthenticationMethodClientCertificate. I wonder how to implement this for Android.
Sorry that my question is not exactly match the topic in the article besides that client side certificate stuff.