3、非对称密钥对生成:RSA、ECC 与 SM2
非对称密钥对,说白了就是一把公钥、一把私钥。公钥可以公开,私钥必须藏好。我刚开始接触密码学时,总觉得这玩意儿很玄乎。后来亲手生成过几万对密钥,才真正理解——密钥对生成的质量,直接决定了整个系统的安全底线。
这一章,咱们就聊聊三种最主流的非对称密钥对生成:RSA、ECC 和国密 SM2。我会把我在项目中踩过的坑、总结的经验,一并倒出来。
3.1 RSA 密钥对生成
RSA 是目前应用最广泛的非对称算法。它的安全性基于大整数分解难题——给你两个大素数 p 和 q 的乘积 n,想反推出 p 和 q,计算上不可行。
3.1.1 核心参数与生成流程
生成 RSA 密钥对,关键参数就这几个:
| 参数 | 说明 | 推荐值 |
|---|---|---|
| 密钥长度 | 模数 n 的比特位数 | 2048 位(最低安全要求),4096 位(推荐) |
| 公钥指数 e | 通常固定为 65537 (0x10001) | 65537 |
| 素数 p, q | 两个大素数,长度约为密钥长度的一半 | 随机生成,需通过素性检测 |
生成流程大致如下:
- 选择密钥长度:比如 2048 位。
- 生成两个大素数 p 和 q:长度各约 1024 位。这一步最耗时,也最考验随机数质量。
- 计算模数 n = p × q。
- 计算欧拉函数 φ(n) = (p-1)(q-1)。
- 选择公钥指数 e:通常取 65537,要求 1 < e < φ(n) 且 gcd(e, φ(n)) = 1。
- 计算私钥指数 d:d ≡ e⁻¹ mod φ(n),即 e 关于 φ(n) 的模逆元。
- 输出公钥 (n, e) 和私钥 (n, d)。
关键点:p 和 q 不能太接近,否则容易被费马分解法攻破。我见过一个项目,直接用随机数生成器连续生成两个相邻的素数……结果被安全审计直接打回。p 和 q 的差值至少要有 2^(密钥长度/2 - 100) 量级。
3.1.2 代码示例(OpenSSL)
# 生成 2048 位 RSA 私钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private_key.pem
# 从私钥提取公钥
openssl pkey -in private_key.pem -pubout -out public_key.pem
# 查看密钥详情
openssl pkey -in private_key.pem -text -noout
我个人习惯用 OpenSSL 的命令行工具做快速验证。但在生产环境,我建议用语言自带的密码库,比如 Python 的 cryptography 或 Java 的 KeyPairGenerator,可控性更强。
3.1.3 避坑指南
我曾经接手过一个遗留系统,里面 RSA 密钥对是用 Java 的 SecureRandom 配合一个固定种子生成的。结果所有实例的私钥一模一样……嗯,那场面,你想想看。记住:随机数生成器必须使用真随机源或高质量的伪随机源,绝不能固定种子。
- 密钥长度别选 1024 位:NIST 已明确要求 2023 年后禁用 1024 位 RSA。现在起步就是 2048 位。
- 私钥存储必须加密:生成后立即用 AES-256-GCM 加密存储,或者使用 HSM(硬件安全模块)。
- 定期轮换:RSA 私钥建议每 1-2 年轮换一次。我在金融项目中,要求每 6 个月轮换一次。
3.2 ECC 椭圆曲线密钥对生成
ECC 的优势在于——同等安全强度下,密钥长度更短。256 位的 ECC 密钥,安全性与 3072 位的 RSA 相当。这对移动端、IoT 设备来说,简直是福音。
3.2.1 核心参数与生成流程
ECC 密钥对生成,依赖的是椭圆曲线上的离散对数问题。核心参数包括:
| 参数 | 说明 | 常见曲线 |
|---|---|---|
| 曲线方程 | y² = x³ + ax + b | secp256r1 (P-256), secp384r1 (P-384) |
| 基点 G | 曲线上的一个生成元 | 由标准定义 |
| 阶 n | 基点 G 的阶(素数) | 由标准定义 |
| 私钥 d | 随机整数,范围 [1, n-1] | 随机生成 |
| 公钥 Q | Q = d × G(椭圆曲线点乘) | 计算结果 |
生成流程比 RSA 简单得多:
- 选择曲线:比如 NIST P-256 或 Curve25519。
- 生成随机私钥 d:一个 256 位的随机整数。
- 计算公钥 Q = d × G:这是椭圆曲线上的标量乘法。
- 输出私钥 d 和公钥 Q。
我个人建议:新项目优先考虑 Curve25519(X25519)或 Curve448。它们的设计更抗侧信道攻击,实现上也更不容易出错。NIST 曲线虽然广泛使用,但历史上对曲线参数的随机性有过争议。
3.2.2 代码示例(OpenSSL)
# 生成 ECC 私钥(使用 prime256v1 曲线,即 secp256r1)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out ecc_private.pem
# 提取公钥
openssl pkey -in ecc_private.pem -pubout -out ecc_public.pem
# 查看曲线参数
openssl ecparam -list_curves
3.2.3 避坑指南
我曾经在调试一个跨平台通信项目时,发现 iOS 端和 Android 端生成的 ECC 公钥格式不一样。折腾了两天,才发现是曲线参数编码方式不同——一个用了未压缩格式,一个用了压缩格式。记住:公钥的编码格式必须统一,建议使用未压缩格式(前缀 0x04)。
- 不要自己实现椭圆曲线运算:数学太复杂,一个点加运算写错,整个系统就废了。用成熟的密码库。
- 私钥随机性必须强:ECC 私钥就是一个随机数。如果随机数生成器有缺陷,私钥可以被预测。2012 年就有人发现某些 Android 设备生成的 ECC 私钥存在严重偏差。
- 注意曲线选择:不要用 secp256k1(比特币用的那条)做通用加密,它没有经过 NIST 和 CNSA 的充分评估。
3.3 SM2 国密密钥对生成
SM2 是中国国家密码管理局发布的椭圆曲线公钥密码算法标准(GM/T 0003-2012)。它本质上也是一种 ECC,但曲线参数和签名算法都是中国自主设计的。
3.3.1 核心参数与生成流程
SM2 使用的椭圆曲线方程为:y² = x³ + ax + b,其中:
- p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
- a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
- b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
- G 点坐标和阶 n 由标准定义
生成流程与标准 ECC 完全一致:
- 使用 SM2 标准曲线参数。
- 生成随机私钥 d:256 位随机整数。
- 计算公钥 Q = d × G。
- 输出私钥 d 和公钥 Q。
注意:SM2 的公钥通常以 04 || x || y 的未压缩格式存储,共 65 字节(1 字节前缀 + 32 字节 x 坐标 + 32 字节 y 坐标)。私钥是 32 字节的随机数。
3.3.2 代码示例(OpenSSL 1.1.1+ 或 GmSSL)
# 使用 GmSSL(国密 OpenSSL 分支)生成 SM2 密钥对
gmssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out sm2_private.pem
# 提取公钥
gmssl pkey -in sm2_private.pem -pubout -out sm2_public.pem
# 查看密钥
gmssl pkey -in sm2_private.pem -text -noout
如果使用标准 OpenSSL 1.1.1 及以上版本,SM2 曲线已经内置:
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -out sm2_private.pem
3.3.3 避坑指南
我曾经帮一家银行做国密改造,发现他们直接用 OpenSSL 的 EC 接口生成 SM2 密钥对,但没设置 SM2_ID。结果签名验证一直失败。SM2 签名算法需要一个用户标识(ID),默认值是 "1234567812345678"。如果通信双方用的 ID 不一致,签名就验不过。
- SM2 不是简单的 ECC 换曲线:它的签名算法(SM2-SM3)和密钥交换协议(SM2-KE)都有独特设计。生成密钥对只是第一步。
- 合规性要求:在中国金融、政务等场景,必须使用国密算法。但注意,SM2 密钥对必须配合 SM3 哈希和 SM4 对称加密一起使用,形成完整的国密套件。
- 互操作性:不同厂商的 SM2 实现可能存在细微差异。我建议在项目初期就做交叉测试,确保 A 厂商生成的密钥能被 B 厂商的库正确解析。
3.4 三种密钥对生成对比
| 特性 | RSA | ECC(通用) | SM2 |
|---|---|---|---|
| 安全基础 | 大整数分解 | 椭圆曲线离散对数 | 椭圆曲线离散对数 |
| 密钥长度(安全等价) | 2048 位 | 256 位 | 256 位 |
| 生成速度 | 慢(素数生成耗时) | 快 | 快 |
| 签名/加密性能 | 签名慢,验签快 | 签名快,验签快 | 签名快,验签快 |
| 标准化组织 | PKCS#1, NIST | NIST, ANSI, SECG | 中国国家密码管理局 |
| 适用场景 | 传统系统、兼容性要求高 | 移动端、IoT、新系统 | 中国合规场景 |
3.5 知识体系总览
下面这张图,帮你把三种密钥对生成的核心逻辑串起来:
三种算法殊途同归——最终都是输出一对密钥:公钥公开,私钥保密。区别在于数学原理、参数选择和性能特征。选哪个,取决于你的业务场景、合规要求和兼容性需求。
我的建议:新系统优先考虑 ECC(Curve25519),需要国密合规就用 SM2。RSA 虽然成熟,但密钥太长、生成太慢,除非有历史兼容性要求,否则尽量别碰。