23. 密钥存储与认证:Android Keystore、硬件-backed密钥、TEE集成
各位同学,今天我们来聊一个非常核心的话题——密钥存储与认证。说白了,就是你的手机里那些密码、指纹、支付密钥,到底存在哪里?安不安全?
我刚开始做Android安全时,有个误区:觉得密钥存在文件里,再加密一下,就安全了。直到我参与了一个金融类App的渗透测试……嗯,那款App把私钥存在SharedPreferences里,虽然做了AES加密,但密钥硬编码在代码中。反编译后,三分钟就拿到了所有用户的“安全”密钥。从那以后,我深刻理解了一个道理:密钥存储,不能只靠软件。
23.1 Android Keystore:系统级的密钥保险箱
Android Keystore是Google提供的一个系统服务。它不直接存储密钥内容,而是把密钥交给一个叫做“Keystore Service”的守护进程管理。应用只能通过Binder接口操作密钥,比如签名、加密,但永远拿不到密钥的明文。
我个人习惯把Keystore理解成一个“黑盒保险箱”:你往里放东西,它帮你锁好;你需要用的时候,它帮你操作,但绝不把钥匙给你看。
- 密钥一旦生成,无法从进程中导出
- 支持密钥使用权限控制(比如只允许在用户解锁后使用)
- 支持硬件-backed密钥(后面细讲)
- 支持密钥失效时间设置
来看一个生成密钥的例子:
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();
注意看setUserAuthenticationRequired(true)。这个参数的意思是:使用这把密钥前,用户必须解锁设备(输入PIN/图案/指纹)。我曾经在项目中遇到过一个问题:某App设置了此参数,但没处理好锁屏状态下的回调,导致用户在支付时频繁闪退。避坑指南:一定要监听KeyStoreException中的KEY_USER_NOT_AUTHENTICATED错误码。
23.2 硬件-backed密钥:把密钥锁进芯片里
软件Keystore虽然安全,但密钥还是存在文件系统中(虽然加密了)。如果攻击者拿到了root权限,理论上可以暴力破解。这时候,硬件-backed密钥就派上用场了。
硬件-backed密钥,说白了就是密钥的生成、存储、运算全部在独立的硬件安全模块中完成。Android支持两种硬件方案:TEE(Trusted Execution Environment)和StrongBox。
| 特性 | 软件Keystore | TEE-backed | StrongBox |
|---|---|---|---|
| 密钥存储位置 | 文件系统(加密) | TEE内部 | 独立安全芯片 |
| 抗物理攻击 | 弱 | 中 | 强 |
| 性能 | 快 | 中等 | 较慢 |
| 典型场景 | 低安全需求 | 支付、指纹 | 金融、政府 |
你想想看,如果密钥存在TEE里,就算Android系统被攻破了,攻击者也拿不到密钥。因为TEE和REE(普通执行环境)是物理隔离的。我曾在某款手机上测试过:即使刷了第三方ROM,TEE里的密钥依然纹丝不动。
KeyStore.getInstance().isHardwareBacked()。但注意,这个方法在Android 13之后有些变化,建议结合KeyGenParameterSpec.Builder.setIsStrongBoxBacked(true)来尝试创建StrongBox密钥,如果抛出异常,说明不支持。
23.3 TEE集成:密钥的“安全飞地”
TEE到底是什么?我打个比方:你的手机里有两个世界。一个是Android系统所在的“普通世界”(REE),另一个是TEE所在的“安全世界”。两个世界共享同一个CPU,但内存、外设都是隔离的。普通世界无法直接访问安全世界的内存。
Android Keystore与TEE的集成,是通过一个叫做“Keymaster”的HAL层实现的。Keymaster TA(Trusted Application)运行在TEE中,负责所有密钥操作。
为什么TEE这么安全?因为即使攻击者控制了整个Android系统,也无法直接读取TEE内存。攻击者只能通过Keymaster HAL提供的接口来操作密钥,而这些接口是受严格限制的——比如,你可以请求签名,但拿不到私钥本身。
23.4 实战:如何正确使用硬件-backed密钥
说了这么多理论,我们来点实际的。假设你要为一个支付App生成签名密钥,应该怎么做?
- 检查设备能力:先判断是否支持StrongBox,如果不支持,退而求其次使用TEE。
- 设置用户认证:必须要求用户解锁设备才能使用密钥。
- 设置密钥用途:明确指定PURPOSE_SIGN和PURPOSE_VERIFY,不要给多余权限。
- 处理异常:捕获KeyStoreException,特别是用户未认证的情况。
我曾经在项目中遇到过一个问题:某款手机宣称支持StrongBox,但实际创建密钥时抛出了HardwareNotAvailableException。后来发现是厂商的StrongBox实现有bug,需要重启设备才能恢复。所以,一定要有降级策略——如果StrongBox不可用,可以尝试TEE,如果TEE也不可用,再考虑软件Keystore(但需要用户明确同意风险)。
KeyGenParameterSpec.Builder.setKeyValidityStart()和setKeyValidityEnd()可以设置密钥的有效期。对于临时会话密钥,设置较短的有效期可以降低泄露风险。
23.5 总结
Android Keystore + 硬件-backed密钥 + TEE集成,构成了Android系统最核心的密钥保护体系。记住三个关键点:
- 软件Keystore:基础保护,防应用层攻击
- 硬件-backed:防系统级攻击,密钥不出芯片
- TEE集成:提供隔离的执行环境,防物理攻击
最后,送大家一句话:密钥安全,没有银弹。硬件-backed密钥不是万能的,但它把攻击门槛提高到了物理层面。对于大多数应用来说,这已经足够了。
嗯,今天就讲到这里。下次你们在开发中遇到密钥相关的问题,可以回想一下这节课的内容。
公众号:蓝海资料掘金营,微信deep3321