2. Android KeyStore 系统架构:硬件支持、安全等级、密钥生命周期

Android KeyStore 并非单一的软件库,而是一套分层的密钥管理体系。它从底层的硬件安全模块(如 TEE、StrongBox)向上提供统一的 java.security.KeyStore 或 android.security.keystore API。理解其架构的三个维度——硬件支持、安全等级、密钥生命周期——是正确使用 KeyStore 加密蓝牙持久化数据的前提。

2.1 硬件支持层

Android 设备可能包含以下硬件安全模块,KeyStore 会根据设备能力自动选择最高可用模块:

  • TEE (Trusted Execution Environment):基于 ARM TrustZone 的独立安全世界,密钥运算在 TEE 内部完成,普通系统(REE)只能通过驱动接口调用。大多数中高端设备(从 Android 7.0 起)默认支持。
  • StrongBox:独立的安全芯片(如 Titan M),拥有自己的 CPU、存储和真随机数生成器。Android 9 引入,需在 KeyGenParameterSpec.Builder 中显式指定 setIsStrongBoxBacked(true)。
  • 软件安全 (Soft Keymaster):纯软件实现,密钥加密后存储于文件系统。无硬件支持的设备或未启用 TEE 时降级使用。
最佳实践:蓝牙持久化数据通常对性能要求不苛刻,应优先使用 StrongBox 或 TEE。使用前通过 KeyStore.getInstance("AndroidKeyStore") 和 Provider.getInfo() 检查设备支持情况。

2.2 安全等级对比

安全等级 实现方式 密钥隔离 抗物理攻击 典型设备
软件 /data/misc/keystore 加密文件 弱(受 SELinux 保护) 无 低端手机、模拟器
TEE ARM TrustZone + 安全 OS 强(非安全世界不可直接访问) 中等(需侧信道攻击) 大部分 Android 7+ 设备
StrongBox 独立安全芯片(eSE) 极高(物理+逻辑隔离) 高(防篡改、防探针) Pixel 3+、部分三星旗舰
关键结论:蓝牙设备配对密钥或持久化连接令牌等敏感数据,应使用 KeyProperties.PURPOSE_ENCRYPT | PURPOSE_DECRYPT 且在 TEE/StrongBox 中生成。无法满足时至少保证密钥不出用户空间。

2.3 密钥生命周期

KeyStore 中的密钥遵循一套完整的生命周期管理,所有操作都通过 KeyStore 和 KeyGenerator/KeyPairGenerator API 触发。

常见错误:未正确设置 KeyGenParameterSpec.Builder 中的 setKeyValidityStart/setKeyValidityEnd 导致密钥过期后无法解密;或忘记设置 setUserAuthenticationRequired(true) 导致密钥被恶意应用重用。
// 生成对称密钥 (AES 256) 并在 KeyStore 中管理
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "bluetooth_enc_key_01",
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setKeySize(256)
        .setIsStrongBoxBacked(true)   // 启用 StrongBox
        .setKeyValidityStart(new Date())
        .setKeyValidityEnd(new Date(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000))
        .build();

KeyGenerator keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(spec);
keyGenerator.generateKey();
Android KeyStore 密钥生命周期 1. 生成 KeyGenerator / KeyPairGenerator 2. 存储 TEE / StrongBox 加密后存文件 3. 使用 Cipher.doFinal() 运算在硬件内 4. 销毁 KeyStore.deleteEntry() 硬件支持影响存储与使用阶段 StrongBox 密钥不可导出;TEE 密钥受 Keymaster HAL 保护 非法 root 或系统分区修改可能导致密钥不可访问

2.4 密钥生命周期 API 要点

  • 生成:通过 KeyGenerator(对称)或 KeyPairGenerator(非对称)在指定 Provider 下创建,密钥内容不出硬件。
  • 存储:KeyStore 内部将密钥句柄(引用)持久化到 /data/misc/keystore/user_0/ 或硬件安全存储。应用不可直接读取原始密钥材料。
  • 使用:通过 Cipher、Signature、Mac 等引擎初始化时引用 KeyStore 别名,加解密运算在安全世界执行。
  • 销毁:调用 keyStore.deleteEntry(alias) 立即删除密钥材料(硬件中也会同步擦除)。不可恢复。
  • 有效期:通过 setKeyValidityStart / setKeyValidityEnd 控制,过期后任何操作抛出 KeyStoreException。

2.5 调试与验证

// 检查当前设备支持的 KeyStore 特性
String providerName = "AndroidKeyStore";
Provider provider = Security.getProvider(providerName);
if (provider == null) {
    Log.w("KeyStoreDebug", "AndroidKeyStore not available");
} else {
    Log.i("KeyStoreDebug", "Provider: " + provider.getInfo());
    // 输出类似: AndroidKeyStore v1.0 (TEE based, StrongBox supported)
}

// 列出所有已有别名
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
    String alias = aliases.nextElement();
    Log.i("KeyStoreDebug", "Alias: " + alias);
    if (ks.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
        Log.i("KeyStoreDebug", "  -> secret key (inaccessible raw)");
    }
}
调试小技巧:在 Gradle 构建中引入 com.android.support:appcompat-v7:28.0.0 仅用于读取 KeyStore 别名列表;切勿在生产直接导出密钥材料——硬件隔离的密钥根本无法导出,这是安全设计。

2.6 常见错误与稳定性优化

错误场景 原因 解决方法
生成密钥时抛 ProviderException: StrongBox not available 设备无 StrongBox 硬件 改用 setIsStrongBoxBacked(false) 或捕获异常降级到 TEE
解密时 KeyStoreException: Key user not authenticated 设置了 setUserAuthenticationRequired(true) 但未验证用户凭证 在使用前调用 BiometricPrompt.authenticate() 或移除该限制
密钥过期后无法解密已有数据 setKeyValidityEnd 设置过短 设计时应将有效期设为长效(如 365 天),并在业务层实现密钥轮换
蓝牙重连时 KeyStore 操作频繁导致 ANR 每次蓝牙连接都重新创建 KeyStore 实例 在 Application 初始化时一次性创建 KeyStore 并缓存引用

2.7 性能建议

  • 批量操作:若要同时使用多个密钥,优先在 Cipher.init() 阶段一次性指定所有参数,避免多次加载 KeyStore。
  • 缓存密钥句柄:密钥别名绑定后,KeyStore.getKey(alias, null) 返回的是句柄而非原始字节,可以复用,但注意 KeyStore 对象本身线程安全,无需每次加锁。
  • 避免频繁生成/销毁:KeyStore 中密钥生成和销毁涉及硬件状态刷新,影响性能。蓝牙持久化场景建议生成一次密钥后长期复用,仅在被保护数据泄露风险时轮换。