[翻译]使用 ICS KeyChain API(Using the ICS KeyChain API)

前言:

这是一篇关于Android安全的翻译,这是原文地址。这篇文章主要讲了通过 KeyChain 安装和使用私有证书的方法。证书相关处理在 Android 应用开发中并不常见,但是在企业应用中是不可或缺的。

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


原文:

Using the ICS KeyChain API

November 29, 2011

译文:

使用 ICS (冰激凌三明治安卓4.0系统代号) KeyChain 应用编程接口

2011年11月29日

原文:

Update: Sample app code is now available on github.

译文:

更新:示例应用代码现在在 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 4.0 (冰激凌三明治, ICS) 介绍了一个全新的,统一平板和手机的 UI 。很多'以人为中心'的交互和共享特性,以及其它方便的改进,例如更好的相机应用,过度炒作的人脸解锁。由于每个人都在讨论这些,我们要看看那些用户很少看见,但却是重要的安全相关的改进。

原文:

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 TrustStores 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.

译文:

Android 经常被说成缺失了重要的安全特性,并且被企业界严肃地接受了,长久以来这是 RIM (译注:加拿大RIM公司)黑莓的领域。两个缺失的特性是:控制系统的受信任 CA 证书的能力,提供一个中心化的证书存储。由于很多公司使用私有的 PKI(Public Key Infrastructure 公钥基础设施),在系统层面安装信任证书对于使用受 PKI 保护的企业服务是必不可少的。直到现在,使用这些所需 CA 证书的唯一途径是嵌入到每个应用中,并且创建自定义的 TrustStores,用 SSL 连接。一个系统层面可用的证书存储已经存在一段时间了,但它只对内置的 VPN 和 WiFi (EAP) 客户端可用。人们可以通过“设置”应用安装私有的凭据/证书对 (key/certificate) 。但是没有通过应用实现安装的公开 API 。通过 KeyChain 类,ICS 给信任证书管理和安全证书存储提供了 SDK API。我们在下面的部分要看看它如何使用。

原文:

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:

译文:

KeyChain 类看似简单,它提供了4个公开的静态方法,但这些方法对大多数证书相关工作来说足够了,首先让我们看看如何安装一个私有的凭据/证书对,并用它签名和验证一些数据。KeyChain API 让你安装一个包装在 PKCS#12 文件中的私有的 key/certificate 对。而不是提供一个 API 来直接安装私钥和证书,KeyChain 提供了一个工厂方法 createInstallIntent(),它返回一个系统 intent 来解析和安装 keys/certificates , (它实际上和以前版本中设置应用提供的 intent 是相同的)。要安装一个 PKCS#12 文件,你必须把它读进一个二进制数组,把它存储在 intent extrasEXTRA_PKCS12 字段中,然后启动相关的 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.

译文:

为了解压并解析凭据和证书,这会提示你输入 PKCS#12 的密码,如果密码正确,会提示输入‘证书名称’,像下面的截屏展示的一样。如果 PKCS#12 有一个友好的名称属性,它默认会被显示,如果没有,你会得到一个很长的十六进制 hash 串。你在这儿输入的字符串是key/certificate的别名,在后面你要用它们通过 KeyChain API 访问 key/certificate。如果你还没有设置过,会提示你设置一个锁屏 PIN (Personal Identification Number,个人识别密码),或者密码来保护证书存储,

pkcs12-install.png

原文:

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:

译文:

要用系统证书存储中的一个私有的密钥库,你需要调用 KeyChain.choosePrivateKeyAlias() 并且提供一个回调实现,它接收已选的别名:

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.

译文:

第一个参数是当前的 context, 第二个是回调,第三第四是可接受的密钥类型(RSA, DSA,或者 null 表示所有)和可接受的证书发行人,为了让证书匹配私钥。(修订:发现 keyTypesissuers 当前都没有用,所以只传了 null)接下来的两个参数是请求证书的服务器主机和端口号,最后一个是预选的别名。除了密钥类型,这里其它的都不去指定(null 或者 -1),这样可以选择所有可用的证书。需要注意的是 alias() 回调不会在主线程调用,因此你不应该尝试直接操作 UI (它在一个 binder 线程调用)。使用密钥需要用户授权,因此 Android 会显示一个私钥选择对话框,这个对话框还用来同意访问已选的私钥。

cert-select.png

原文:

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

译文:

为了得到一个私钥的引用,你需要调用 KeyChain.getPrivateKey() 方法,传递前一步接收到的私钥别名。看起来没有文档说明,但当你尝试在主线程调用这个方法,你会得到一个异常,说这可能会 ‘导致死锁’。这里我们在一个后台线程调用它,用 AsyncTask(当处理潜在的耗时 I/O 操作,这几乎总是正确的)。

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.

译文:

首先我们利用密钥别名得到私钥和证书链(certificate chain),然后创建并且验证一个签名(signature),来检查密钥是否真正可用。由于我们使用一个自定义的证书,所以 chain 由一个单独的实体组成。但对于一个 CA 签署的证书,你需要在返回的数组中查找到真正的证书实体。

原文:

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.

译文:

安装一个 CA 证书和安装一个 PKCS#12 文件差别不大,把证书加载到一个字节数组,作为一个 extra 传递到安装的 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:

译文:

Android 会解析证书,如果它的基本约束扩展(Basic Constraints extension)被设置成 CA:TRUE,它会被当成是一个 CA 证书,并导入到用户信任存储。你需要授权导入证书,但有趣的是,导入对话框既不显示证书 DN (Distinct Name 独特的名称),也不显示它的哈希值。用户没有办法知道他们在导入什么,直到导入完成。很少有人会真正地费心检查,因此这会是一个潜在的安全威胁:恶意的应用可能会欺骗用户安装流氓证书,导入对话框看起来是这样的:

ca-cert-install.png

原文:

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

译文:

证书导入以后,它会在 Trusted credentials 页面的 User tab 中显示(设置->安全->信任的证书)。点击证书条目,显示一个详情对话框,这里你能(终于!)检查主题、发行者、有效期限、序列号和 SHA-1/SHA-256 指纹。你也可以通过点击 'Remove' 按钮删除证书(下滑显示)。

cert-details.png

原文:

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.

译文:

虽然你能删除个人的 CA 证书,但没有办法删除个人的密钥和用户证书。你可以使用安全-设置-证书存储区域的 '清除证书' 选项删除所有的证书。另一个要注意的事是一旦你有私钥在证书存储中,你就不能删除屏幕锁,由于它被用来保护 keystore 的访问。在之前的 Android 版本,有一个单独的'证书存储密码'('credential storage password'),但好像在 ICS 他们决定简化,用锁屏密码保护证书存储。

原文:

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.

译文:

新引入的 KeyChain API 让你在一个中心化的安全证书存储中安装和访问私钥,同样添加系统层面的受信任证书。它没有提供 keystore 之下的下层访问,利用 Android intent 的分发机制代替调用做实际工作的系统 Activity。CA 证书安装对话框缺少了一个关键特性(显示证书详情),但总得来说,提供系统 keystore 服务的访问是朝着正确方向的一个进步。

原文:

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.

译文:

这覆盖了我们的 Android keystore 介绍的第一部分。在下一部分我们要调查 KeyChain 后面的一切,并且尝试给出一些关于底层的实现。

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

推荐阅读更多精彩内容