ICS Credential Storage Implementation
In the previous entry, we looked at how the new ICS KeyChain API is used and tried installing a user private key/certificate pair and a CA certificate. Now we'll will try to find out where they are actually stored and how they are protected.
Looking at
Next step is, of course, peeking into
Here each file name consists of the UID of the user that created it (1000 is
Key blobs are owned by the
This basically translates to:
The
As mentioned in the previous article, most of the described credential storage functionality has been available in Android since at least Donut (1.5), but the key store was only accessible to system applications such as Settings, and the WiFi and VPN clients. What ICS adds are a few layers on top of this that make it possible to offer user applications access to the system key store and assert fine-grained control over what keys each app is allowed to use. In the next part of the series we will look at the implementation of the new credential storage functionality added in ICS.
Looking at
frameworks/base/keystore/java/android/security
, we notice several interesting classes that are not mentioned in the SDK documentation. The most promising is the KeyStore
class, so let's have a look. Sure enough, it is marked as hidden (using the dreaded @hide
comment). It does have methods for interacting with the key store (get()
, put()
, delete()
, reset()
, etc.), but where is the actual key store? As it turns out, all methods send command to a local socket aptly named 'keystore'. With a little creative grepping, we find out that there is native daemon with the same name listening on that socket. The source is in frameworks/base/cmds/keystore/keystore.cpp
, so let's have a look. The file has some helpful comments, and we learn that keys are encrypted, checksummed and saved as files (one key per file). But where are the actual files? Looking at /init.rc
we find the keystore daemon startup command looks like this:service keystore /system/bin/keystore /data/misc/keystore class main user keystore group keystore socket keystore stream 666
Next step is, of course, peeking into
/data/misc/keystore
# ls -la /data/misc/keystore -rw------- keystore keystore 84 2011-11-30 15:26 .masterkey -rw------- keystore keystore 980 2011-11-30 15:56 1000_CACERT_testca -rw------- keystore keystore 820 2011-11-30 15:55 1000_USRCERT_test -rw------- keystore keystore 932 2011-11-30 15:55 1000_USRPKEY_test
Here each file name consists of the UID of the user that created it (1000 is
system
), the entry type (CA certificate, user certificate or private key), and the key name (alias) connected with underscores. And, of course, there is a .masterkey
. Going back to the keystore
daemon source, we find out that:- each key is encrypted with a 128-bit AES master key in CBC mode
- each key blob contains an info header, the initial vector (IV) used for encryption, an MD5 hash value of the encrypted data and the encrypted data itself
- the master key (in
.masterkey
) is itself encrypted with an AES key. The encryption key is derived from the password using the PBKDF2 key-derivation function with 8192 iterations (it may take a while...). The salt is randomly generated and is stored in the.masterkey
file's info header.
Key blobs are owned by the
keystore
user, so on a regular (not rooted) device, you need to go through the daemon to access the keys. As it turns out, there is a helpful command line utility that talks to the daemon and lets us manipulate the key store: keystore_cli
. It has commands for initializing the key store, listing, getting and deleting keys, etc. Experimenting with it shows that the keystore
daemon is additionally checking the calling process's UID to grant or deny access to each command:# keystore_cli unlock keystore_cli unlock 6 Permission denied # keystore_cli get CACERT_testca keystore_cli get CACERT_testca 1 No error -----BEGIN CERTIFICATE----- MIICiTCCAfKgAwI... # su system su system $ keystore_cli insert foo bar keystore_cli insert foo bar 1 No error $ keystore_cli saw "" keystore_cli saw "" 1 No error foo USRPKEY_test USRCERT_test CACERT_testca $ keystore_cli get foo keystore_cli get foo 1 No error bar $ exit # su app_44 su app_44 $ keystore_cli saw "" keystore_cli saw "" 1 No error $ keystore_cli insert baz boo keystore_cli insert baz boo 1 No error $ keystore_cli get baz keystore_cli get baz 1 No error boo
This basically translates to:
root
cannot lock/unlock the key store, but can access system keys- the
system
user can do pretty much anything (initialize or reset the key store, etc.) - regular users can insert, delete and access keys, but can only see their own keys
The
android.security.KeyStore
class we found while browsing the framework's source is almost a one-to-one port of the keystore_cli
command's functionality to Java. By using it Java apps can get direct access to the keystore
daemon, but as we said, that class is not part of the public API. There are a couple of reasons for this:- even if they had access to it, normal apps wouldn't have the needed permissions to initialize or unlock the key store
- it's interface exposes the current implementation: keys are returned as raw blobs which wouldn't be possible if the key store and related cryptographic operations were implemented in hardware (such as in a TPM).
As mentioned in the previous article, most of the described credential storage functionality has been available in Android since at least Donut (1.5), but the key store was only accessible to system applications such as Settings, and the WiFi and VPN clients. What ICS adds are a few layers on top of this that make it possible to offer user applications access to the system key store and assert fine-grained control over what keys each app is allowed to use. In the next part of the series we will look at the implementation of the new credential storage functionality added in ICS.
Comments
you are writing:
> regular users can ... delete ... own keys
what is the command syntax for removing a key?
for example your "foo" entry?
i tried to remove a set (cacert, cert + key) with no luck
error is all the time "key not found" :(
system@android:/ $ keystore_cli saw ""
1 No error
USRPKEY_galaxy-s2-XXX
USRCERT_galaxy-s2-XXX
CACERT_galaxy-s2-XXX
USRPKEY_YYY
USRCERT_YYY
CACERT_YYY
USRKEY_galaxy-s2-XXX <
7 Key not found
system@android:/ $ keystore_cli delete USRPKEY_ga*
7 Key not found
system@android:/ $ keystore_cli delete USRPKEY_galaxy-s2-XXX
7 Key not found
system@android:/ $
$ keystore_cli d foo
keystore_cli d foo
1 No error
I'm currently developing an SCEP client for Android. I'm trying to get access to the Android KeyStore by terminal, but it's no keystore_cli file on my device. Shall I reinstall or make a factory reset on my device?
If it is not an error, How can I manage to get access to the KeyStore without keystore_cli?
Thanks in advance.
Again, thank you for your answer.
thanks for the wonderful and informative blog posts.
Our current implementation is loading the certificate from the downloads area. Then we encrypt and save the private key and certificate in the private area. Once authentication is done we delete the certificate from downloads area because no one else should access it. We load the key and certificate in the PKCS backed keystore from the private area location for subsequent log ons.
We want to move to Key chain based implementation. Our product standard says to have a 2nd layer encryption i.e. on top of the OS, app must also encrypt the certificate and the key. If we use the key chain then the app itself is not encrypting it. I know the credential storage has 1st layer encryption reading this blog post.
But can another application implementing the key chain apis get hold of the private key from my certificate. ? Can we somehow provide the 2nd layer app level protection for the key chain. ?
cheers,
Saurav
You might want to look at the later posts on the subject.
when install certificate(.p12) on VPN side clear credentials is enabled.
But when same certificate is installed on Wifi side clear credentials is not enabled in Android side.
May i know exact reason for this. Reference to Issue 153326
Will not enable clear credentials
Remove Certificate from Trusted user will remove certificate from setting -> WiFi-> Ca certificate dropdown
return mBinder.zero() == KEY_NOT_FOUND;
Today (moments ago) Delete comment