3. 生成对称密钥(AES):KeyGenParameterSpec 参数、Cipher 模式选择、密钥生成代码实现

3.1 原理概述

Android Keystore 系统允许应用在安全硬件(TEE / StrongBox)内生成并存储 AES 对称密钥,密钥材料永不离开安全环境。生成过程通过 KeyGenParameterSpec 声明密钥属性,再由 KeyGenerator 在 Keystore 内派生密钥对象。密钥生成后仅以别名引用,原始字节不可直接导出,从而保证机密性。

3.2 KeyGenParameterSpec 核心参数

参数说明推荐值
keySizeAES 密钥长度(位)256(默认)、192、128
blockModes分组密码模式BlockMode.GCM(首选)
encryptionPaddings填充方式EncryptionPadding.NONE(GCM 自带)、PKCS7(CBC)
keyValidityStart / end密钥有效时间窗口可留空(永久)
setUserAuthenticationRequired使用前是否需要生物/锁屏验证false(教程阶段)
setRandomizedEncryptionRequired强制每次加密不同 IVtrue(默认能防重放)

3.3 Cipher 模式选择对比

模式安全性是否需要 IV适用场景
GCM高(认证加密)是(12 字节推荐)数据完整性要求高,首推
CBC中(需 MAC 搭配)是(16 字节)向后兼容旧接口
ECB弱(不推荐)否仅用于单块数据,避免使用
实践建议:Android 官方推荐使用 GCM 模式,它同时提供加密和完整性验证,且 Keystore 对 GCM 做了硬件加速支持。若使用 CBC 则需额外计算 HMAC 保证完整。

3.4 密钥生成流程

  1. 获取 KeyGenerator 实例,指定算法和提供者。
  2. 构造 KeyGenParameterSpec,配置 keySize、blockModes、encryptionPaddings 等。
  3. 用 spec 初始化 KeyGenerator。
  4. 调用 generateKey() 生成密钥,返回 SecretKey 对象(实为 Keystore 句柄)。
  5. 通过 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 密钥生成流程示意图

AES 对称密钥生成流程 KeyGenerator.getInstance("AES","AndroidKeyStore") KeyGenParameterSpec.Builder .setKeySize(256) .setBlockModes(GCM) .setEncryptionPaddings(NONE) keyGenerator.init(spec) generateKey() → SecretKey Keystore.containsAlias(alias) 说明:密钥材料始终位于 Keystore 安全环境内,应用无法读取原始字节,只能通过别名操作。

3.8 调试建议

  • 调用 keyStore.aliases() 枚举所有已保存的密钥,确认别名存在。
  • 在模拟器上测试时,部分设备可能回退到软件实现,可检查日志看是否有 StrongBox 支持信息。
  • 使用 adb shell dumpsys key_store 查看 Keystore 内容(需 root 或 debug 版本)。
性能优化:密钥生成是重操作,建议在应用启动后的后台线程执行,避免阻塞 UI。同一密钥只需生成一次,后续通过别名复用即可。