第4章 KeyGenParameterSpec 详解:Builder 参数、用途标记、加密/签名模式设置
KeyGenParameterSpec 是 Android Keystore 中用于配置密钥生成参数的规范类。它通过 Builder 模式构建,允许开发者精确控制密钥的用途、算法细节、有效期等属性。本章将从原理到代码,逐一拆解 Builder 参数、用途标记(Purpose)、加密/签名模式设置,并提供完整的实战流程。
4.1 核心原理:Builder 参数总览
KeyGenParameterSpec.Builder 的构造方法接收两个必选参数:密钥别名(String) 和 密钥用途(int)。其余参数通过链式调用设置。下表列出最常用的 Builder 方法及其作用:
| Builder 方法 | 参数类型 | 说明 |
|---|---|---|
setKeySize(int) | int | 密钥长度(位),例如 RSA 2048、AES 256 |
setKeyValidityStart(Date) | Date | 密钥生效起始时间(null 表示立即生效) |
setKeyValidityEnd(Date) | Date | 密钥失效时间(null 表示永不过期) |
setBlockModes(String...) | String[] | 允许的分组加密模式,如 ECB、CBC、CTR、GCM |
setEncryptionPaddings(String...) | String[] | 允许的加密填充模式,如 PKCS1Padding、OAEPPadding、NoPadding |
setSignaturePaddings(String...) | String[] | 允许的签名填充模式,如 RSASSA-PKCS1-v1_5、PSS |
setDigests(String...) | String[] | 允许的消息摘要算法,如 SHA-256、SHA-384、SHA-512 |
setUserAuthenticationRequired(boolean) | boolean | 是否要求用户验证(指纹/锁屏)后才能使用密钥 |
setInvalidatedByBiometricEnrollment(boolean) | boolean | 新增生物识别后是否使密钥失效(默认 true) |
提示: 用途标记(Purpose)不是通过 Builder 方法设置,而是作为构造参数传入。它是位掩码,可以组合多个用途。
4.2 用途标记(Purpose)详解
用途标记定义密钥在 Keystore 授信硬件中允许执行的操作。定义在 KeyProperties 类中:
PURPOSE_ENCRYPT(1) – 加密数据PURPOSE_DECRYPT(2) – 解密数据PURPOSE_SIGN(4) – 签名PURPOSE_VERIFY(8) – 验签PURPOSE_WRAP_KEY(16) – 密钥封装(API 28+)PURPOSE_AGREE_KEY(32) – 密钥协商(API 30+,用于 ECDH)
常用组合:PURPOSE_ENCRYPT | PURPOSE_DECRYPT 用于对称/非对称加解密;PURPOSE_SIGN | PURPOSE_VERIFY 用于签名验证;PURPOSE_ENCRYPT | PURPOSE_DECRYPT | PURPOSE_SIGN | PURPOSE_VERIFY 用于同时需要加解密和签名能力的非对称密钥对。
警告: 如果用途标记未包含实际使用的操作,Keystore 会在运行时抛出
KeyStoreException:Key user not authorized for the operation。务必根据业务场景设置最小必要权限。
4.3 加密/签名模式设置
Android Keystore 要求预声明允许的加密模式、填充和摘要算法,目的是在 TEE/SE 环境中预先验证操作合法性。以下为常见配置:
| 算法 | 常用模式组合 |
|---|---|
| AES | setBlockModes("GCM") + setEncryptionPaddings("NoPadding")(GCM 自身不填充) |
| RSA 加密 | setEncryptionPaddings("OAEPPadding") + setDigests("SHA-256", "SHA-384") |
| RSA 签名 | setSignaturePaddings("RSASSA-PKCS1-v1_5") + setDigests("SHA-256") |
| ECDSA 签名 | setDigests("SHA-256", "SHA-384")(无需设置填充) |
注意: GCM 模式需要 NoPadding,且 IV 长度固定为 12 字节。RSA 的 OAEPPadding 推荐搭配 SHA-256 或更高强度的摘要。
4.4 实现流程(代码示例)
以下代码演示如何生成一个 RSA 2048 密钥对,该密钥可用于加密、解密、签名和验签,有效期 1 年,要求用户验证后才可使用。
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.util.Calendar;
String alias = "my_rsa_key";
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT |
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setKeySize(2048)
.setKeyValidityStart(start.getTime())
.setKeyValidityEnd(end.getTime())
.setBlockModes(KeyProperties.BLOCK_MODE_ECB, KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384)
.setUserAuthenticationRequired(true)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
generator.initialize(spec);
generator.generateKeyPair();
4.5 调试与验证
生成密钥后,可以通过 KeyStore 加载并检查 spec 属性:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry entry = keyStore.getEntry(alias, null);
if (entry instanceof KeyStore.PrivateKeyEntry) {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
// 通过 Certificate 可获取公钥,但无法直接获取 KeyGenParameterSpec
}
// 注意:KeyGenParameterSpec 不能从已生成的密钥中反查,需保留引用或重新构造
调试技巧: 在初始化 KeyPairGenerator 之前打印 spec 的各个参数,帮助检查配置是否符合预期:
Log.d("KeySpec", "Purpose: " + spec.getPurposes());Log.d("KeySpec", "Block Modes: " + Arrays.toString(spec.getBlockModes()));
4.6 常见错误与解决方案
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
生成时抛出 InvalidAlgorithmParameterException | 用途标记与算法不匹配(如 RSA 尝试 PURPOSE_AGREE_KEY) | 检查算法支持的用途,例如 ECDH 才需要 PURPOSE_AGREE_KEY |
加密/解密时 KeyStoreException: Key user not authorized | 未设置 setUserAuthenticationRequired(true) 但未进行生物识别验证 | 确认是否需要用户验证,若不需要则设为 false |
签名时 KeyStoreException: Signature padding not supported | 签名填充模式未在 spec 中声明 | 使用 setSignaturePaddings() 添加对应填充 |
| 密钥过期后操作失败 | 未设置或设定了过短的 setKeyValidityEnd | 检查当前时间是否在有效期内,或移除有效期限制(设为 null) |
4.7 性能与稳定性优化
- 减少密钥长度: 非对称密钥 RSA 2048 足够安全,RSA 4096 在 Keystore 中操作会显著变慢,且部分 TEE 不支持。
- 模式选择: AES-GCM 是推荐模式,内置认证标签,无需额外 MAC;避免使用 ECB 模式。
- 摘要缓存: 签名操作中频繁指定摘要会触发额外哈希计算,建议在 spec 中只设置需要的摘要。
- 生物识别频率: 如果
setUserAuthenticationRequired(true),每次使用密钥都需要用户验证,高频场景会严重影响体验,考虑使用会话超时模式(API 30+ 的setUserAuthenticationValidityDurationSeconds)。