4. Keystore密钥生成:generateKey接口调用链、参数解析、密钥材料生成、安全存储全流程
密钥生成,是Keystore最核心的能力之一。说白了,就是让系统帮你造一把钥匙,而且这把钥匙从出生那一刻起,就待在安全区里,你碰不到它的明文。我刚开始接触这个模块时,总觉得这步很简单——不就是调个接口吗?后来踩了坑才发现,里面的门道多着呢。
4.1 generateKey接口调用链
我们先从最上层的API看起。Android提供了两种方式生成密钥:KeyGenerator 和 KeyPairGenerator。前者用于对称密钥,后者用于非对称密钥对。
// 对称密钥生成示例
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
new KeyGenParameterSpec.Builder("my_aes_key",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
SecretKey key = keyGenerator.generateKey();
这一行 generateKey() 背后,到底发生了什么?我画了一张调用链图,帮你理清脉络。
你看,从应用层到TEE(可信执行环境),中间隔了四层。每一层都有它的职责。我当年排查一个密钥生成失败的问题时,就是顺着这条链一路追下去的——最后发现是HAL层返回了一个奇怪的错误码。
4.2 参数解析:KeyGenParameterSpec
生成密钥前,你得告诉Keystore你想要什么样的钥匙。这个信息封装在 KeyGenParameterSpec 里。我习惯把它比作「钥匙的出生证明」——上面写好了这把钥匙能干什么、不能干什么。
| 参数 | 说明 | 我的建议 |
|---|---|---|
| setPurpose | 密钥用途:加密、解密、签名、验证 | 按需设置,不要贪多 |
| setBlockModes | 分组密码模式:GCM、CBC、ECB | 优先用GCM,自带认证 |
| setEncryptionPaddings | 填充模式:PKCS7、None | GCM模式下必须用None |
| setKeySize | 密钥长度:128、192、256 | AES推荐256位 |
| setUserAuthenticationRequired | 是否需用户生物认证后才可用 | 敏感数据建议开启 |
| setInvalidatedByBiometricEnrollment | 新增指纹时是否失效 | 默认true,安全优先 |
核心要点:密钥一旦生成,它的用途就被「焊死」了。你不能事后修改。所以初始化时要想清楚——这把钥匙到底用来做什么。
4.3 密钥材料生成
参数传进去了,接下来就是真正「造钥匙」的阶段。这里分两种情况:
4.3.1 对称密钥(AES)
对称密钥的生成相对简单。Keymaster HAL会调用TEE内部的随机数生成器,产生一串高质量的随机字节。这个过程完全在安全区内完成,应用层根本看不到原始密钥材料。
// 伪代码:Keymaster HAL 内部逻辑
key_material_t generate_aes_key(key_size_t size) {
// 1. 从硬件随机数生成器获取熵
entropy_t entropy = hw_rng_get_entropy();
// 2. 使用DRBG生成密钥字节
uint8_t* key_bytes = drbg_generate(entropy, size / 8);
// 3. 将密钥包装成keymaster格式
return wrap_key_blob(key_bytes, size);
}
嗯,这里要注意:随机数的质量直接决定密钥的安全性。我记得有一次客户反馈说生成的密钥总是重复,排查了半天,发现是某款低端机的硬件RNG坏了,回退到了软件随机数——那质量,你懂的。
4.3.2 非对称密钥对(RSA/EC)
非对称密钥的生成更复杂一些。它需要生成一对密钥:公钥和私钥。私钥永远不出安全区,公钥可以导出给外部使用。
以RSA为例,生成过程大致是:
- 在TEE内生成两个大素数 p 和 q
- 计算 n = p * q
- 选择公钥指数 e(通常为65537)
- 计算私钥指数 d
- 将公钥(n, e)导出,私钥(p, q, d)安全存储
小技巧:如果你只需要签名验证,用EC密钥比RSA快得多,而且密钥长度更短。我在一个IoT项目里把RSA 2048换成EC P-256后,签名性能提升了将近3倍。
4.4 安全存储全流程
密钥材料生成后,不能直接扔到文件系统里。那样太危险了。Keystore有一套完整的安全存储机制。
4.4.1 密钥Blob的封装
生成的密钥会被封装成一个「密钥Blob」。这个Blob包含了:
- 密钥材料本身(加密后的)
- 密钥元数据(用途、算法、创建时间等)
- 访问控制列表(谁可以用、什么时候可以用)
- 完整性校验值(防止篡改)
4.4.2 加密存储
密钥Blob会用设备主密钥(Device Master Key)进行加密。这个主密钥由TEE或安全芯片保护,应用层永远拿不到。说白了,就是「钥匙锁在保险柜里,保险柜的密码由保安队长保管」。
// 密钥Blob结构示意
key_blob_t {
uint8_t encrypted_key_material[KEY_SIZE]; // 加密后的密钥
key_metadata_t metadata; // 密钥元数据
access_control_t access_control; // 访问控制
uint8_t mac[32]; // HMAC完整性校验
};
4.4.3 持久化存储
加密后的密钥Blob最终会写入 /data/misc/keystore/ 目录下。每个密钥对应一个文件,文件名是密钥别名的哈希值。
避坑指南:我曾经遇到过一个问题——应用卸载后重新安装,之前生成的密钥找不到了。原因是密钥的访问控制绑定了应用的UID,而重装后UID变了。解决方案是在生成密钥时设置 setIsStrongBoxBacked(false) 或者使用 setAttestationChallenge 来绕过这个限制。但要注意,这会降低安全性。
4.5 完整流程图
最后,我把整个密钥生成的全流程串起来,画了一张完整的流程图。你可以对照着看,每一步都做了什么。
整个流程走下来,你会发现一个有意思的事情:密钥材料在TEE内生成、在TEE内加密、以加密形式存储。应用层从头到尾都没碰过明文密钥。这就是Keystore的安全基石。
好了,关于密钥生成的流程就讲到这里。下一节我们会深入聊聊密钥使用时的那些坑——尤其是 KeyStore.Entry 的获取和类型转换,那才是真正容易翻车的地方。
公众号:蓝海资料掘金营,微信deep3321