Keystore 入门:它到底是什么

很多 Android 开发者对 KeyStore 的第一印象就是“一个存密码的地方”。嗯,这么理解倒也不算错,但太粗糙了。Android Keystore 系统其实是一整套的安全密钥管理基础设施。我刚开始接触它时,也以为就是调用几个 API 存个字符串,直到线上出现了一个诡异的 ANR……先卖个关子,后面会细说。

1. 它是做什么的?

说白了,KeyStore 就是 Android 系统提供的一个安全容器。它帮你干这几件事:

  • 生成密钥:在安全环境中生成 RSA、AES、EC 等密钥对。
  • 存储密钥:密钥材料不会暴露给 App 进程,只给你一个“句柄”。
  • 执行加密操作:签名、验证、加密、解密都在 KeyStore 内部完成。
  • 访问控制:可以绑定用户身份(锁屏密码/指纹),密钥只在用户解锁后可用。

重要:密钥一旦进入 KeyStore,App 的堆内存里就再也看不到密钥原文了。这是它和 SharedPreferences 之流的本质区别。

2. 核心概念速览

要理解异步化,得先搞清楚 KeyStore 里的几个关键名词。我拿一个实际项目中的例子来说:

概念说明我踩过的坑
Key Entry KeyStore 中存储的一个密钥条目,由别名(alias)唯一标识。 别名不要用中文,系统底层是 C++ 处理字符串,部分厂商会出 bug。
Key Pair / Secret Key 非对称密钥对(公钥 + 私钥)或对称密钥。 私钥永远不会给你,只能通过 KeyStore 签名。
Key Generator KeyPairGenerator / KeyGenerator 用来在安全环境里生成密钥。 生成耗时可能上百毫秒,卡主线程就会 ANR。
KeyStore 一个抽象类,通过 getInstance("AndroidKeyStore") 获取实例。 它是单例吗?不是,但建议保持一个实例重复用。
Access Control 通过 KeyGenParameterSpec 设置用户认证、使用时间等限制。 setUserAuthenticationRequired(true) 配合强解锁策略,会让部分操作阻塞 UI。

3. 它和传统加密存储有什么不同?

做个对比你就清楚了。我以前用 Bouncy Castle 自己管理密钥,后来全部切成了 KeyStore:

对比项传统方式(文件/SP/数据库)Android Keystore
密钥暴露 App 进程内存中可随意读取 进程内不可读,仅通过句柄操作
硬件隔离 无(全靠软件防护) 支持 TEE / StrongBox,密钥不进 AP 侧内存
持久化位置 应用私有目录或数据库 存储在系统分区,由 keystore 守护进程管理
操作耗时 毫秒级(纯 CPU 计算) 可能几十到几百毫秒(跨进程 IPC + 硬件通信)
授权绑定 需自行实现指纹/密码校验 原生支持,但需要留心超时

一句话总结:KeyStore 用“安全但慢”换“高安全性”。我们这个课程的核心,就是解决那个“慢”字带来的 ANR 问题。

4. 系统架构是怎样工作的?

你也许会问:每次操作密钥都要通过 KeyStore,这中间到底经过了哪些环节?我画张图帮你理清脉络:

Android Keystore 系统架构 Android Keystore 核心架构 App 进程 调用 KeyStore API(JCA 接口) System Server KeyStore Service (Binder IPC) keystore 守护进程 密钥存储管理、访问控制 Keymaster HAL 硬件抽象层,调用 TEE / StrongBox TEE / StrongBox / Secure Element 真正执行加解密运算的硬件环境 注意 每次密钥操作都要走完 这整个链路,IPC + HAL + 硬件交互,容易卡主线程。 我当年就是在这条路径上 遇到了 ANR,才研究异步化。

5. 为什么它会引发 ANR?

你看上面的图就明白了。从 App 到硬件,中间有 3 次跨进程 + 1 次硬件调用。如果你的主线程直接调用 KeyStore 的 sign() 或 generateKeyPair(),系统可能阻塞几十甚至几百毫秒。我曾经在低端机上测到 generateKeyPair 耗时 800ms——主线程直接卡死。

曾经有个项目,在用户登录时生成 RSA 密钥对,结果频繁 ANR。排查后发现罪魁祸首就是 KeyGenerator.generateKeyPair() 在锁屏后要等待用户认证超时。

6. 基本使用演示

先看看最简单的代码结构,后续章节会逐步拆解异步方案:

// 获取 AndroidKeyStore 实例
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

// 生成密钥(注意:这行可能在主线程爆炸)
KeyPairGenerator generator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
generator.initialize(
        new KeyGenParameterSpec.Builder("my_key_alias",
                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
                .setDigests(KeyProperties.DIGEST_SHA256)
                .setUserAuthenticationRequired(true) // 绑定用户认证
                .build());
KeyPair keyPair = generator.generateKeyPair();

// 签名操作
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(keyStore.getKey("my_key_alias", null));
signature.update(data);
byte[] signed = signature.sign();

注意 Sign 和 generateKeyPair 都是同步阻塞的。我建议你从现在开始就养成习惯:所有 KeyStore 操作都放到后台线程或者使用 Kotlin 协程。

好了,这就是 KeyStore 的入门认知。记住核心:它是一个在安全环境中操作密钥的远程服务,而远程服务天然就不适合在主线程同步调用。后面的章节,我会一步步教你如何用线程池、协程、以及 RxJava 来彻底根治 ANR。