keystore
daemon manages keys and certificates, and how to connect to it using the provided keystore_cli
utility. Now we will look at the intermediate layers between the OS daemon and the public KeyChain
API introduced in ICS.Browsing the
android.security
package, we find two AIDL files: IKeyChainService.aidl
and IKeyChainAliasCallback.aidl
. This is a hint that the actual key store functionality, like most Android OS services, is implemented as a remote service that the public API's bind to. IKeyChainAliasCallback
is just the callback called when you select a key via KeyStore#choosePrivateKeyAlias()
, so it's of little interest. IKeyChainService
has the actual methods KeyChain
uses to get a handle to a private key or a certificate, plus some internal API's used by the Settings and certificate installer applications. Naturally, the whole interface is marked as hidden, so SDK applications cannot directly bind to the service.The
IKeyChainService
interface has one implementation, the KeyChainService
in the KeyChain.apk
system package. We find the source in packages/apps/KeyChain
, so let's explore the app's configuration. Looking at the manifest reveals that it consists of three components: the KeyChainService
, a KeyChainActivity
, and a broadcast receiver, you guessed it, KeyChainBroadcastReceiver
. The package is com.android.keychain
and its sharedUserId
is set to 'android.uid.system', which, as we saw in the previous article, is necessary to be able to send management commands to the native keystore
daemon. Let's first examine the service.As can be expected, the
KeyChainService
is a wrapper for the android.security.KeyStore
class that directly communicates with the native keystore daemon. It provides 4 sets of functionality: - key store management: methods for getting private keys and certificates
- trust store management: methods for installing and deleting CA certificates in the user trust store
- key and trust store initialization: a
reset()
method that deletes all key store entries, including the master key, thus returning the key store to a 'not initialized' state; it also removes all user-installed trusted certificates - methods for querying and adding entries to the key access grant database (more on this later)
KeyChain
application is running as the system
user, any process that binds to its remote interface would technically be able to perform all key and trust store operations. To prevent this, the KeyChainService
imposes additional access control on its users. It employs two mechanisms to achieve this: controlling access based on the caller's UID and a key access grant database. Deleting a CA certificate and resetting the key and trust stores are only allowed to the system
user (those operations are typically called via the Settings app's UI, which runs as system
), and installing a trusted CA certificate is only allowed to the system
user or the certificate installer application (com.android.certinstaller
package). Controlling access to the key store is a little bit more interesting: KeyChainService
maintains a grants database (in /data/data/com.android.keychain/databases/grants.db
) that maps UID's to the key aliases they are allowed to use. Let's have a look inside:# cd /data/data/com.android.keychain/databases
cd /data/data/com.android.keychain/databases
# ls
ls
grants.db
grants.db-journal
# sqlite3 grants.db
sqlite3 grants.db
sqlite> .schema
.schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE grants ( alias STRING NOT NULL, uid INTEGER NOT NULL, UNIQUE (al
ias,uid));
sqlite> select * from grants;
select * from grants;
test|10044
key1|10044
In this example, the application with UID
10044
(our test application) is granted access to the keys with the test
and key1
aliases.Each call to
getPrivateKey()
or getCertificate()
is subject to a check against the grants database, and results in a exception if a grant for the required alias is not found. As stated before, KeyChainService
has API's for adding and querying grants, and only the system
user is allowed to call them. But who is responsible for actually granting and revoking access? Remember the private key selection dialog from the first article? When you call KeyChain#choosePrivateKeyAlias()
, it will start the KeyChainActivity
introduced above, which will check if the key store is unlocked, and if so, show they key selection dialog. Clicking the 'Allow' button will return to the KeyChainActivity
, which will then call KeyChainService#setGrant()
with the selected alias, adding it to the grants database. Thus, even if the activity requesting access to a private key has the needed permissions, the user has to unlock the key store and explicitly authorize access to each individual key.Besides controlling private key storage, the
KeyChainService
also offers trust store management by using the newly added TrustedCertificateStore
class (part of libcore
). This class provides both the ability to add user-installed trusted CA certificates and remove (mark as not trusted) system (pre-installed) CA's. Since the implementation is fairly complex and rather interesting, it will be the topic of another post.The last component of the
KeyChain
app is the KeyChainBroadcastReceiver
. It listens for a android.intent.action.PACKAGE_REMOVED
broadcast and simply forwards control to the KeyChainService
. On receiving the PACKAGE_REMOVED
action, the service does some grant database maintenance: it goes through all entries and deletes those referencing packages that are no longer available (i.e., uninstalled ones). With this we now have the (almost) complete picture (click to enlarge):ICS introduces a new service that grants access to both the system key store (managed by the keystore daemon) and trust store (manged by the
TrustedCertificateStore
class) that backs the KeyChain
API exposed in the public SDK. That makes it possible to control access to keys based on both the calling process's UID and the key access grant database, thus allowing for fine-grained, user-driven control over what keys each application can access. We've discussed most of the components this framework consists of in this and the previous entry. What remains is to look into the new trust store implementation introduced in Android 4.0. That will be the focus of the next post of this series.
Post a Comment