[翻译]ICS 证书存储实现(ICS Credential Storage Implementation)

前言:

这是一篇关于Android安全的翻译,这是原文地址。接上一篇翻译。这篇文章主要讲了 key store 的存储位置,加密方式,读写权限等。随着Android源码的更新,这篇文章已经有些过时了,但它表达的思想还是非常可取的。

我会一段一段地复制原文,然后在下面给出翻译,如有图片,我会重新保存再上传,以免出现“图片无法显示的情况”。


原文:

ICS Credential Storage Implementation

November 30, 2011

译文:

ICS 证书存储实现

2011年11月30日

原文:

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.

译文:

前面的部分,我们学习了新的 ICS KeyChain API 是如何被使用,并且尝试安装了一个用户私有的密钥/证书对和一个 CA 证书,现在我们要找到它们实际存储的地方,还有它们是如何被保护的。

原文:

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:

译文:

查看 frameworks/base/keystore/java/android/security,我们注意到几个有意思的, SDK 文档没有提到的类。最有价值的是 KeyStore 类,所以我们看一看。果然,它被标记成 hidden (使用了可怕的 @hide 注解)(译注:@hide 注解是 Android 源码中,隐藏一个 JAVA 类或者方法时所用的注解,使用了这个注解,它就不会被编译到 Android SDK 中,所以应用开发是没法调用的。但使用 makefile 在源码环境下编译是可以访问的。)。它有和 key store (凭据存储) 交互的方法(get(), put(), delete(), reset() 等)。但真正的 key store 在哪里?事实证明,所有的方法发送命令到一个本地 socket(套接字),恰好命名为 'keystore' (译注:我看新的源码,没有使用 socket, 而是使用了 Binder, 总之是要跨进程通信)。通过一点有创造性的文本查询,我们发现有一个本地的守护进程有相同的名字,监听在那个 socket 上。代码在 frameworks/base/cmds/keystore/keystore.cpp(译注:新的代码已经没有这个文件了,因为是 Binder, 对应的类是 IKeystoreService, 在 frameworks\base\core\java\android\security 中有一个 IKeystoreService.aidl)。让我们看一下,文件有一些有帮助的注释,我们知道 key 是被加密,校验和保存为一个文件(一个 key per 文件)。但真正的文件在哪儿呢?查看 /init.rc 我们找到 keystore 守护进程的启动命令看起来是这样的(译注:我没有看 ICS 的源码):

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

译文:

下一步,查看 /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:

译文:

这里每一个名字的构成部分有:创建它的用户的 UID (1000 是 system), 条目类型(CA 证书,用户证书或私钥),和 key 的名称(别名),用下划线连接。当然,有一个 .masterkey。回到 keystore 守护进程源码,我们找到了:

原文:

  • 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 被用一个128位的 AES 主密钥CBC 模式加密。
  • 每一个 key 大对象包含一个头信息,一个用来加密的初始化向量initial vector (IV) ,一个被加密数据的 MD5 哈希值以及被加密数据本身。
  • 主密钥 (在 .masterkey)自己被一个 AES key 加密,加密的密钥是从密码中用 PBKDF2 密钥导出方法经过8192次迭代衍生出来的(它可能需要一些时间)。salt 是随机生成的,保存在 .masterkey 文件的头信息中。

原文:

What this means in practice is that the Android key store is pretty secure for a software solution: even if you had access to a rooted device and managed to extract the key blobs, you would still need the keystore password to derive the master key. Trying out different password to decrypt the master key would require at least 8192 iterations to derive a key, which is prohibitively expensive. In addition, the derivation function is seeded with a 128-bit random number, so pre-calculated password tables cannot be used.

译文:

这意味着在实践中 Android 凭据存储在软件方案上是非常安全的,即使你访问一个 root 过的设备,并且成功提取凭据文件,你仍然需要 keystore 的密码来导出主密钥。尝试用不同的密码来解密主密钥需要至少 8192 次迭代来衍生一个凭据,这是过度昂贵的。此外,衍生方法用了一个128位数字的随机种子,所以预设的密码表是不能用的。

原文:

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 用户拥有凭据文件,因此在一个正常(没有 root)的设备上,你需要通过守护进程访问私钥。事实表明,有一个很有用的命令行工具,它和守护进程对话并让我们操作 key store:keystore_cli。它有初始化(initializing)、列出(listing)、获取(getting)、删除(deleting )等命令。实验显示:keystore 守护进程额外地检查了调用者的进程UID,来对每一个命名授权或者拒绝。

# 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

译文:

这可以简单理解成:

  • root 不能加锁/解锁 key store,但可以访问系统 key
  • system 用户可以做不少其它事情(初始化或重置 key store)
  • 普通用户能插入、删除和访问 key,但只能看到他们自己的

原文:

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:

译文:

我们浏览 framework 源码发现的 android.security.KeyStore 类,几乎是 keystore_cli 命令功能到 Java 的一对一的端口。通过使用它,Java 应用能直接访问 keystore 守护进程,但面我们前说过,这个类不是公开 API 的一部分,有下面的一些原因:

原文:

  • 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).

译文:

  • 即使它们能够访问,普通 app 也是没有所需权限初始化或解锁 key store 的。
  • 它的接口暴露了当前的实现,key 是以原始文件返回的,这是不可能的,如果 key store 和关联的密码操作是在硬件实现的(例如在一个 受信任的平台模块(Trusted Platform Module)

原文:

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.

译文:

正如前一篇文章提到的,大多数所描述的证书存储功能已经在 Android Donut(1.5) 以后可用了,但 key store 只对系统应用例如设置、WIFI、VPN客户端等可用。ICS在这上部增加了一些层,使得用户应用访问系统 key store 成为可能,并在允许一个应用使用什么 key 上维护了细粒度控制。在这系列的下一部分,我们将要学习 ICS 添加的新证书存储的实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容