3. KeyPairGeneratorSpec 与 KeyGenParameterSpec:构建参数对象、生成RSA密钥对
Android Keystore 系统提供两套 API 用于构建 RSA 密钥对参数:KeyPairGeneratorSpec(API 18 引入,API 28 废弃)和 KeyGenParameterSpec(API 23 引入,推荐用于 API 23+ 设备)。本章将详细对比两者的差异,并演示如何通过参数对象控制密钥属性、最终生成 RSA 密钥对。
3.1 参数对象的核心作用
在调用 KeyPairGenerator.initialize() 之前,必须通过参数对象描述密钥的以下特征:
- 算法参数:密钥大小(如 2048 位)、算法(RSA)
- 用途:加密/解密、签名/验证、密钥协商
- 签名摘要:SHA-256、SHA-384、SHA-512 等
- 填充方案:PKCS1、OAEP(含摘要与掩码函数)
- 访问控制:指纹验证、用户认证有效期、密钥始终可用
- 密钥别名与存储:Keystore 中的唯一标识
3.2 两代 API 对比
| 特性 | KeyPairGeneratorSpec(废弃) | KeyGenParameterSpec(推荐) |
|---|---|---|
| 最低 API 级别 | API 18(Android 4.3) | API 23(Android 6.0) |
| 是否支持用途(PURPOSE) | 否(仅支持加解密或签名) | 是(可组合 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) |
| 签名算法指定 | setSignatureAlgorithm(“SHA256withRSA”) |
setSignaturePaddings 与 setDigest 分开设置 |
| 加密填充指定 | setEncryptionPaddings 方法 |
setEncryptionPaddings 更细粒度(支持 OAEP 参数) |
| 是否支持用户认证 | 否 | 是(指纹/生物识别 + 密钥超时) |
| 是否支持密钥生成额外参数 | 能够设置 setKeySize、setCertificateSubject、setCertificateSerialNumber |
通过 Builder 直接构建,参数更丰富(如 setInvalidatedByBiometricEnrollment) |
| 公认状态 | API 28 废弃,不建议在新代码中使用 | 官方推荐,持续更新 |
⚠ 兼容性警告:如果应用需要支持低于 API 23 的设备,只能使用
KeyPairGeneratorSpec;此时需注意密钥用途单一(无法同时允许加密和签名),且不能使用 OAEP 填充。建议对低版本设备回退到纯软件 RSA 方案(不存于硬件 Keystore)。
3.3 使用 KeyGenParameterSpec 生成 RSA 密钥对(示例)
✏ 推荐做法:以下代码仅适用于 API 23+。密钥别名应使用公司包名前缀,避免与其他应用冲突。
// 1. 获取 KeyPairGenerator 实例,指定算法和提供者
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore");
// 2. 构建参数对象
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
"my_rsa_key_alias", // 密钥别名
KeyProperties.PURPOSE_ENCRYPT | // 用途:加密
KeyProperties.PURPOSE_DECRYPT | // 用途:解密
KeyProperties.PURPOSE_SIGN | // 用途:签名
KeyProperties.PURPOSE_VERIFY) // 用途:验证
.setDigests(KeyProperties.DIGEST_SHA256, // 支持的摘要算法
KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.setKeySize(2048) // 密钥长度 2048 位
.setCertificateSubject(new X500Principal("CN=MyApp"))
.setCertificateSerialNumber(BigInteger.ONE)
.setCertificateNotBefore(new Date())
.setCertificateNotAfter(new Date(System.currentTimeMillis() + 365 * 24L * 60 * 60 * 1000))
.build();
// 3. 初始化生成器
kpg.initialize(spec);
// 4. 生成密钥对(私钥自动存入 Keystore)
KeyPair keyPair = kpg.generateKeyPair();
生成的 keyPair 仅包含公钥对象;私钥始终存储在 Keystore 中,无法直接提取其明文。如需使用私钥,通过 PrivateKey 对象引用(实际调用 Keystore 硬件模块)。
3.4 生成流程与关键节点
下图展示从参数构建到密钥生成的核心步骤,以及两个 API 的版本分界点。
3.5 常见错误与解决方案
- NoSuchAlgorithmException: 忘记指定提供者
"AndroidKeyStore"或使用了错误的算法名(应为KeyProperties.KEY_ALGORITHM_RSA)。 - InvalidAlgorithmParameterException: 在 API 23- 设备上使用了
KeyGenParameterSpec。请检查运行时 API 级别并降级到KeyPairGeneratorSpec。 - KeyStoreException: KeyStore not initialized: 生成密钥前需先加载 Keystore 实例(通常只需
KeyStore.getInstance("AndroidKeyStore")并load(null))。 - UnsupportedOperationException: 当使用
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)时,硬件 Keystore 可能不支持无填充。建议始终使用 PKCS1 或 OAEP。 - 密钥别名冲突: 如果同别名密钥已存在,
generateKeyPair()会覆盖旧密钥。设计别名时建议包含版本号或时间戳。
🔥 性能要点:RSA 密钥生成属于计算密集型操作,硬件 Keystore 在生成 2048 位密钥时通常耗时 200ms~1s。应在后台线程执行生成操作,避免阻塞 UI。对于频繁创建临时密钥的场景,建议复用同一别名或预生成密钥池。