3. 生成对称密钥(AES):KeyGenParameterSpec 参数、Cipher 模式选择、密钥生成代码实现
3.1 原理概述
Android Keystore 系统允许应用在安全硬件(TEE / StrongBox)内生成并存储 AES 对称密钥,密钥材料永不离开安全环境。生成过程通过 KeyGenParameterSpec 声明密钥属性,再由 KeyGenerator 在 Keystore 内派生密钥对象。密钥生成后仅以别名引用,原始字节不可直接导出,从而保证机密性。
3.2 KeyGenParameterSpec 核心参数
| 参数 | 说明 | 推荐值 |
|---|---|---|
| keySize | AES 密钥长度(位) | 256(默认)、192、128 |
| blockModes | 分组密码模式 | BlockMode.GCM(首选) |
| encryptionPaddings | 填充方式 | EncryptionPadding.NONE(GCM 自带)、PKCS7(CBC) |
| keyValidityStart / end | 密钥有效时间窗口 | 可留空(永久) |
| setUserAuthenticationRequired | 使用前是否需要生物/锁屏验证 | false(教程阶段) |
| setRandomizedEncryptionRequired | 强制每次加密不同 IV | true(默认能防重放) |
3.3 Cipher 模式选择对比
| 模式 | 安全性 | 是否需要 IV | 适用场景 |
|---|---|---|---|
| GCM | 高(认证加密) | 是(12 字节推荐) | 数据完整性要求高,首推 |
| CBC | 中(需 MAC 搭配) | 是(16 字节) | 向后兼容旧接口 |
| ECB | 弱(不推荐) | 否 | 仅用于单块数据,避免使用 |
实践建议:Android 官方推荐使用 GCM 模式,它同时提供加密和完整性验证,且 Keystore 对 GCM 做了硬件加速支持。若使用 CBC 则需额外计算 HMAC 保证完整。
3.4 密钥生成流程
- 获取
KeyGenerator实例,指定算法和提供者。 - 构造
KeyGenParameterSpec,配置 keySize、blockModes、encryptionPaddings 等。 - 用 spec 初始化
KeyGenerator。 - 调用
generateKey()生成密钥,返回SecretKey对象(实为 Keystore 句柄)。 - 通过
KeyStore.load(null)并调用getEntry(alias)可后续获取。
3.5 代码实现
以下代码在 Android 10(API 29)及以上安全使用 Keystore 生成 AES-256/GCM 密钥:
// 生成 AES 对称密钥
public static void generateAesKey(Context context, String alias) throws Exception {
// 1. 获取 KeyGenerator 实例
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
// 2. 构建 KeyGenParameterSpec
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setKeySize(256)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true) // 强制随机 IV
.setUserAuthenticationRequired(false) // 本教程暂不绑定用户验证
.build();
// 3. 初始化生成器
keyGenerator.init(spec);
// 4. 生成密钥(实际在安全硬件内完成)
SecretKey secretKey = keyGenerator.generateKey();
// 5. 验证密钥已写入 Keystore
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (keyStore.containsAlias(alias)) {
Log.i("KeystoreDemo", "AES key saved with alias: " + alias);
}
}
注意:生成密钥后不要尝试调用
secretKey.getEncoded(),否则在真机 Keystore 实现中会抛出 KeyStoreException 或返回 null。如需导出密钥用于测试,应使用软件实现(不推荐生产)。
3.6 错误处理与兼容性
- NoSuchAlgorithmException:设备不支持 AES 或 AndroidKeyStore 提供者。
- InvalidAlgorithmParameterException:参数组合不合法,例如 GCM 模式下指定了 PKCS7Padding。
- KeyStoreException:Keystore 被锁或未初始化。
- 兼容性:Android 6.0(API 23)起可用 KeyGenParameterSpec,但 StrongBox 需要 API 28+。Always-on 指纹绑定需单独处理。
3.7 密钥生成流程示意图
3.8 调试建议
- 调用
keyStore.aliases()枚举所有已保存的密钥,确认别名存在。 - 在模拟器上测试时,部分设备可能回退到软件实现,可检查日志看是否有
StrongBox支持信息。 - 使用
adb shell dumpsys key_store查看 Keystore 内容(需 root 或 debug 版本)。
性能优化:密钥生成是重操作,建议在应用启动后的后台线程执行,避免阻塞 UI。同一密钥只需生成一次,后续通过别名复用即可。