7、密钥管理与 Android KeyStore:KeyStore 介绍、生成对称密钥、生成非对称密钥、密钥有效期设置、密钥访问控制

说到生物识别,很多人第一反应是「指纹一按,门就开了」。但作为开发者,我们得想得更深——指纹验证通过后,真正用来加密数据的密钥,到底存在哪?怎么保证它不被偷走?

嗯,这就是 Android KeyStore 要解决的问题。它不是一个普通的文件存储,而是一个硬件隔离的保险箱。我个人习惯把 KeyStore 比作「钥匙管家」——它帮你保管密钥,但从不把密钥本身交出去。你只能通过它来使用密钥,比如加密、解密、签名。

KeyStore 是什么?

Android KeyStore 是系统提供的一个密钥管理容器。它有几个关键特性:

  • 硬件隔离:密钥存储在 Trusted Execution Environment(TEE)或 Secure Element(SE)中,App 进程无法直接读取
  • 导出禁止:一旦密钥生成,它的明文永远不会离开安全硬件
  • 使用绑定:可以限制密钥只能在用户解锁设备后使用,或者只能在生物识别验证通过后使用

核心要点:KeyStore 不存储数据,只存储密钥。你的加密数据可以放 SharedPreferences、Room 或者文件里,但密钥必须交给 KeyStore 管理。

生成对称密钥

对称密钥,说白了就是加密和解密用同一把钥匙。在 Android 上,我们通常用 AES 算法。我建议使用 AES-GCM 模式,因为它自带认证标签,能防止数据被篡改。

下面是我项目中常用的生成方式:

// 生成 AES 对称密钥
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "my_aes_key",  // 密钥别名,后续通过它引用
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setKeySize(256)
        .build();

KeyGenerator keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(spec);
keyGenerator.generateKey();

个人经验:我曾经在项目里用了 AES-CBC 模式,结果发现每次加密结果都一样,因为忘了加 IV。后来换成 GCM 模式,它自动处理了随机 IV,省心很多。

生成非对称密钥

非对称密钥,就是一对钥匙——公钥公开,私钥保密。在 KeyStore 里,私钥同样无法导出。我通常用 RSA 算法来做密钥对生成。

为什么需要非对称密钥?举个例子:你的 App 需要和服务器安全通信,服务器可以用你的公钥加密数据,只有你的私钥能解密。私钥在 KeyStore 里,谁也拿不走。

// 生成 RSA 非对称密钥对
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "my_rsa_key_pair",
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setDigests(KeyProperties.DIGEST_SHA256)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
        .setKeySize(2048)
        .build();

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyPairGenerator.initialize(spec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

注意:RSA 密钥长度不要低于 2048 位。1024 位的 RSA 在 2024 年的安全标准下已经不够用了。我见过有老项目还在用 1024,后来被安全审计要求全部升级。

密钥有效期设置

密钥不能永生。你想想看,如果一把钥匙永远不换,被破解的风险会越来越高。KeyStore 支持设置密钥的有效期,过期后密钥就无法使用了。

设置方式很简单,在 Builder 里加上时间限制:

KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "my_timed_key",
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setKeyValidityStart(Calendar.getInstance().getTime())
        .setKeyValidityEnd(getDateAfterDays(365))  // 一年后过期
        .build();

我个人习惯把有效期设成 90 天。为什么?因为如果密钥泄露,90 天的窗口期比一年小得多。而且定期轮换密钥,本身就是一种好的安全实践。

避坑指南:我曾经把有效期设成了 0 天,结果密钥生成后立刻过期,加密数据后无法解密。调试了半天才发现是日期计算出了问题。所以记得用 Calendar 或 Instant 来算时间,别手写毫秒数。

密钥访问控制

这是 KeyStore 最强大的地方。你可以控制密钥在什么条件下才能被使用。常见的控制方式有:

  • 用户认证绑定:密钥只能在用户通过生物识别或锁屏密码后使用
  • 设备锁屏绑定:设备锁屏后,密钥自动失效
  • 时间窗口绑定:只能在指定时间段内使用

最常用的是用户认证绑定。比如你的支付 App,只有在指纹验证通过后,才能用私钥签名交易数据。

KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "my_biometric_key",
        KeyProperties.PURPOSE_SIGN)
        .setUserAuthenticationRequired(true)  // 需要用户认证
        .setUserAuthenticationValidityDurationSeconds(30)  // 认证后30秒内有效
        .build();

小技巧:setUserAuthenticationValidityDurationSeconds 设成 0 表示每次使用都需要重新认证。设成正数表示认证后一段时间内可以重复使用。我一般设成 5 秒,既不用频繁按指纹,又不会让密钥长期暴露。

知识体系结构图

下面这张图帮你理清 KeyStore 的核心逻辑:

Android KeyStore 对称密钥 (AES) 非对称密钥 (RSA) 有效期设置 访问控制 硬件隔离 · 导出禁止 · 使用绑定 密钥明文永不离开安全硬件(TEE/SE)

总结一下

KeyStore 的核心价值就一句话:把密钥锁进保险箱,你只能隔着玻璃用,不能伸手拿。对称密钥适合大量数据加密,非对称密钥适合密钥交换和签名。有效期和访问控制是安全的两道锁,别偷懒不设。

嗯,我记得刚开始接触 KeyStore 时,总觉得它麻烦——明明一个字符串就能当密钥,干嘛要绕这么大一圈?后来项目上线,看到安全审计报告里「密钥存储合规」那一项打了勾,才明白这步路没白走。