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,这中间到底经过了哪些环节?我画张图帮你理清脉络:
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。