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 |
说白了,这些参数就是 SM2 的「基因」。所有 SM2 密钥对都必须基于这套参数生成,否则就不是合规的 SM2。
2.2 公私钥生成原理:从点到密钥
椭圆曲线密码的核心,就是「点乘」运算。我给你拆解一下:
- 私钥:随机选取一个小于
n的大整数d。这就是你的私钥,必须保密。 - 公钥:计算
P = d * G。其中G是基点,*是椭圆曲线上的点乘运算。结果P是一个椭圆曲线上的点,坐标就是(Px, Py)。
为什么这样是安全的?因为从 P 反推 d,需要求解椭圆曲线离散对数问题。以目前的计算能力,256 位的 SM2 曲线,没人能算出来。
核心逻辑:
- 私钥 = 随机数(256 位)
- 公钥 = 私钥 × 基点(椭圆曲线点乘)
- 安全基础 = 椭圆曲线离散对数难题
我刚开始做密码学时,总觉得「点乘」很玄乎。后来自己手写了一遍测试代码,才发现其实就是重复的「点加」和「倍点」运算。你想想看,计算机做这个比人快多了。
下面这张图,帮你理清 SM2 密钥对生成的整体流程:
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 三行命令搞定,但参数和格式不能搞错。