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 两个大素数,长度约为密钥长度的一半 随机生成,需通过素性检测

生成流程大致如下:

  1. 选择密钥长度:比如 2048 位。
  2. 生成两个大素数 p 和 q:长度各约 1024 位。这一步最耗时,也最考验随机数质量。
  3. 计算模数 n = p × q。
  4. 计算欧拉函数 φ(n) = (p-1)(q-1)。
  5. 选择公钥指数 e:通常取 65537,要求 1 < e < φ(n) 且 gcd(e, φ(n)) = 1。
  6. 计算私钥指数 d:d ≡ e⁻¹ mod φ(n),即 e 关于 φ(n) 的模逆元。
  7. 输出公钥 (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 简单得多:

  1. 选择曲线:比如 NIST P-256 或 Curve25519。
  2. 生成随机私钥 d:一个 256 位的随机整数。
  3. 计算公钥 Q = d × G:这是椭圆曲线上的标量乘法。
  4. 输出私钥 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 完全一致:

  1. 使用 SM2 标准曲线参数。
  2. 生成随机私钥 d:256 位随机整数。
  3. 计算公钥 Q = d × G。
  4. 输出私钥 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 知识体系总览

下面这张图,帮你把三种密钥对生成的核心逻辑串起来:

非对称密钥对生成知识体系 非对称密钥对生成核心逻辑 RSA ECC(通用) SM2(国密) 核心参数 密钥长度 (2048/4096) 公钥指数 e=65537 核心参数 曲线方程 + 基点 G 私钥 d (随机数) 核心参数 SM2 标准曲线参数 用户标识 ID 密钥对生成流程 RSA: 生成素数 p,q → 计算 n, φ(n) → 选择 e → 计算 d ECC/SM2: 生成随机私钥 d → 计算公钥 Q = d × G 输出:公钥 + 私钥(安全存储)

三种算法殊途同归——最终都是输出一对密钥:公钥公开,私钥保密。区别在于数学原理、参数选择和性能特征。选哪个,取决于你的业务场景、合规要求和兼容性需求。

我的建议:新系统优先考虑 ECC(Curve25519),需要国密合规就用 SM2。RSA 虽然成熟,但密钥太长、生成太慢,除非有历史兼容性要求,否则尽量别碰。