第3章 密钥生成基础:KeyPairGeneratorSpec用法、椭圆曲线与RSA密钥对生成
Android Keystore 系统提供硬件级(TEE/StrongBox)密钥存储,所有密钥材料永不离开安全环境。本章聚焦两种最常用的非对称算法——RSA 和 椭圆曲线(EC),讲解如何使用 KeyPairGeneratorSpec 在 Keystore 内安全生成密钥对,并对比两者在安全等级、性能、兼容性上的差异。
3.1 原理:KeyPairGeneratorSpec 与 Keystore Provider
KeyPairGeneratorSpec 是 Android 4.3(API 18)引入的构造类,用于定义密钥对的属性(算法、用途、别名、有效期等),再通过 KeyPairGenerator 初始化,最终在 Keystore 内生成密钥对。其核心原理在于:
- Provider 绑定:必须使用
"AndroidKeyStore"Provider,否则密钥将落入软件层,不受硬件保护。 - Spec 参数:除算法外,还需指定
Context、别名、密钥用途(如PURPOSE_ENCRYPT | PURPOSE_DECRYPT)、有效期等。 - RSA 特点:常用 2048 或 4096 位,支持加密/签名,但加解密性能低于 EC。
- EC 特点:常用 P-256(prime256v1),同等安全等级下密钥更短、签名更快,但 Android 6.0 以下支持不全。
3.2 实现流程
- 获取 Keystore 实例:
KeyStore.getInstance("AndroidKeyStore"),并加载.load(null)。 - 构建
KeyPairGeneratorSpec.Builder,设置上下文、别名、算法、用途、有效期。 - 实例化
KeyPairGenerator:KeyPairGenerator.getInstance(keyAlgorithm, "AndroidKeyStore")。 - 调用
generator.initialize(spec)完成初始化。 - 调用
generator.generateKeyPair(),生成的密钥对自动存入 Keystore。 - 后续通过别名从 Keystore 读取公钥和私钥引用。
3.3 代码示例:生成 RSA 与 EC 密钥对
RSA 2048 位密钥对(API 23+)
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
Context context = ...;
String alias = "my_rsa_key";
KeyPairGenerator generator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setKeySize(2048)
.setCertificateSubject(new X500Principal("CN=MyApp"))
.setCertificateSerialNumber(BigInteger.valueOf(1))
.setKeyValidityStart(...)
.setKeyValidityEnd(...)
.build();
generator.initialize(spec);
generator.generateKeyPair();
EC P-256 密钥对(API 23+)
KeyPairGenerator generatorEC = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
KeyGenParameterSpec specEC = new KeyGenParameterSpec.Builder("my_ec_key",
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256)
.setAlgorithmParameterSpec(new ECGenParameterSpec("prime256v1"))
.setCertificateSubject(new X500Principal("CN=ECKey"))
.setCertificateSerialNumber(BigInteger.valueOf(2))
.build();
generatorEC.initialize(specEC);
generatorEC.generateKeyPair();
提示: API 23 以下需使用已废弃的
KeyPairGeneratorSpec(非 KeyGenParameterSpec)。如果项目最低版本低于 23,请使用兼容库或条件判断分支。
3.4 RSA 与 EC 特性对比
| 特性 | RSA | EC(P-256) |
|---|---|---|
| 密钥长度 | 2048 / 4096 位 | 256 位 |
| 安全等级 | 112 / 128 位等效 | 128 位等效 |
| 签名性能 | 较慢(尤其私钥签名) | 快 5~10 倍 |
| 加密支持 | 是(OAEP/PKCS1) | 否(EC仅签名/密钥交换) |
| Android 最低版本 | API 18(基础支持) | API 18(P-256 需 API 23+ 稳定) |
| 硬件安全模块 | 大多数 TEE 支持 | 部分低端设备不支持 EC 硬件加速 |
3.5 调试:验证密钥已正确存储
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
if (ks.containsAlias("my_ec_key")) {
PublicKey pub = ks.getCertificate("my_ec_key").getPublicKey();
PrivateKey priv = (PrivateKey) ks.getKey("my_ec_key", null);
Log.d("Keystore", "EC key pair retrieved, pub algo: " + pub.getAlgorithm());
} else {
Log.w("Keystore", "Key alias not found");
}
3.6 常见错误与解决
- “KeyStoreException: KeyStore not initialized” — 忘记调用
.load(null)。 - “InvalidAlgorithmParameterException: KeyGenParameterSpec is not supported” — 确认 API level ≥ 23,或改用旧版
KeyPairGeneratorSpec。 - “NoSuchProviderException: Provider not available” — 确保使用字符串
"AndroidKeyStore",而非空 Provider。 - “KeyStoreException: keypair generation failed” — 通常因设备 TEE 不支持指定算法或密钥大小,可尝试降级为 2048 RSA 或 P-256 EC。
兼容性警告: Android 6.0(API 23)之前,
KeyGenParameterSpec 不可用。若需支持更低版本,必须使用 KeyPairGeneratorSpec 且无法设置用途、填充等细粒度参数。建议 minSdkVersion 设为 23 以简化代码。
3.7 性能与稳定性优化
- 密钥大小选择:优先使用 2048 位 RSA 或 P-256 EC,避免 4096 位 RSA 在低端设备上生成时间超过 5 秒。
- 生成过程异步:
generateKeyPair()可能涉及硬件交互,应放在后台线程(如AsyncTask或Coroutine),避免阻塞 UI。 - 缓存公钥:公钥不会泄露安全,可从 Keystore 读取后缓存到内存,减少 I/O 开销。
- 密钥复用:同一业务角色建议复用同一个别名,不要频繁生成新密钥对,以免 Keystore 条目膨胀。
3.8 密钥生成流程图
关键要点:
- EC P-256 在签名场景下性能优于 RSA,且同等安全等级密钥更短。
- RSA 仍适用于需要加密/解密的场景(如传输对称密钥)。
- 务必在
generateKeyPair()前检查设备是否支持硬件 Keystore,可通过KeyStore.getInstance("AndroidKeyStore")抛异常与否判断。