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.
Post a Comment