2. SM2 密钥对生成:椭圆曲线参数、公私钥生成原理、OpenSSL 命令行生成 SM2 密钥

聊到 SM2,很多人第一反应就是「国密算法」。但说实话,真正上手做安全存储的时候,密钥对生成才是第一道坎。你想想看,密钥都搞不对,后面加密、签名全是白搭。

我个人习惯,在讲任何密码算法之前,先搞清楚它的数学根基。SM2 属于椭圆曲线密码体制,跟国际上的 ECDSA、ECDH 是同一个家族。但 SM2 有自己的曲线参数,不能跟别的混用。

2.1 椭圆曲线参数:SM2 的「基因」

SM2 使用的椭圆曲线方程是:y² = x³ + ax + b。这条曲线定义在素数域 Fp 上,其中 p 是一个 256 位的大素数。

具体参数长什么样?我直接给你列出来:

参数名 含义 值(十六进制)
p 素数域模数 FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
a 曲线系数 FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
b 曲线系数 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
Gx 基点 G 的 x 坐标 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
Gy 基点 G 的 y 坐标 BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0
n 基点 G 的阶 FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
我的经验:这些参数是国密标准 GM/T 0003-2012 规定的。我在项目中见过有人把 SM2 的曲线参数跟 secp256r1 搞混,结果密钥对生成出来,验签死活过不了。嗯,这种坑踩一次就够了。

说白了,这些参数就是 SM2 的「基因」。所有 SM2 密钥对都必须基于这套参数生成,否则就不是合规的 SM2。

2.2 公私钥生成原理:从点到密钥

椭圆曲线密码的核心,就是「点乘」运算。我给你拆解一下:

  1. 私钥:随机选取一个小于 n 的大整数 d。这就是你的私钥,必须保密。
  2. 公钥:计算 P = d * G。其中 G 是基点,* 是椭圆曲线上的点乘运算。结果 P 是一个椭圆曲线上的点,坐标就是 (Px, Py)。

为什么这样是安全的?因为从 P 反推 d,需要求解椭圆曲线离散对数问题。以目前的计算能力,256 位的 SM2 曲线,没人能算出来。

核心逻辑:

  • 私钥 = 随机数(256 位)
  • 公钥 = 私钥 × 基点(椭圆曲线点乘)
  • 安全基础 = 椭圆曲线离散对数难题

我刚开始做密码学时,总觉得「点乘」很玄乎。后来自己手写了一遍测试代码,才发现其实就是重复的「点加」和「倍点」运算。你想想看,计算机做这个比人快多了。

下面这张图,帮你理清 SM2 密钥对生成的整体流程:

SM2 密钥对生成流程 椭圆曲线参数 (p, a, b, G, n) 步骤1:生成随机数 d (0 < d < n) 步骤2:计算公钥 P = d * G 输出:密钥对 私钥 d(保密) | 公钥 P = (Px, Py)(公开) 注意 私钥必须使用 真随机数生成器 不能是伪随机 安全基础 椭圆曲线离散 对数难题 (ECDLP)

2.3 OpenSSL 命令行生成 SM2 密钥

理论讲完了,咱们来点实际的。OpenSSL 从 1.1.1 版本开始支持 SM2。我建议你至少用 3.0 以上版本,兼容性更好。

生成 SM2 私钥,只需要一条命令:

openssl ecparam -genkey -name SM2 -out sm2_private.pem

这条命令干了三件事:

  • 加载 SM2 的椭圆曲线参数(就是上面那张表)
  • 生成一个随机数作为私钥
  • 把私钥写入 sm2_private.pem 文件

你可以看看私钥长什么样:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJ7X1V3Y4K5Z6W7R8S9T0U1V2W3X4Y5Z6A7B8C9D0E1F2oAoGCCqBHM9V
AYIAAUEJBIIBkzCBkgIBAQQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END EC PRIVATE KEY-----
注意:私钥文件必须妥善保管。我曾经见过有人把私钥文件直接放在代码仓库里,结果被扫描工具抓出来,整个项目都得重做密钥。私钥一旦泄露,加密的数据就等于公开了。

有了私钥,公钥就好办了:

openssl ec -in sm2_private.pem -pubout -out sm2_public.pem

公钥文件内容:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE7X8R9S0U1V2W3X4Y5Z6A7B8C9D0E1F2
oAoGCCqBHM9VAYIAAUEJBIIBkzCBkgIBAQQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-----END PUBLIC KEY-----

你可以用这条命令验证密钥对是否匹配:

openssl ec -in sm2_private.pem -pubin -in sm2_public.pem -check

如果输出 EC key pair verified,说明密钥对没问题。

我的习惯:生成密钥对之后,我会立刻用 openssl ec -text 查看私钥和公钥的十六进制值,确认曲线参数是 SM2。这一步虽然简单,但能避免很多后续问题。

2.4 避坑指南

做 SM2 密钥生成,有几个坑我帮你提前踩了:

  • 曲线名称别写错:OpenSSL 里 SM2 的曲线名就是 SM2,不是 sm2p256v1 也不是 prime256v1。我见过有人写成 prime256v1,结果生成的是国际标准的密钥,不是国密。
  • 私钥格式要统一:PKCS#8 和 SEC1 两种格式,不同库可能不兼容。我建议统一用 PKCS#8,兼容性最好。
  • 随机数质量:私钥的随机性直接决定安全性。生产环境一定要用硬件随机数生成器,别用 /dev/urandom 凑合。

一句话总结:

SM2 密钥对生成 = 固定曲线参数 + 随机私钥 + 点乘公钥。用 OpenSSL 三行命令搞定,但参数和格式不能搞错。