密钥生成:SM2密钥对生成原理
SM2密钥对生成,说白了就是一套数学魔术。椭圆曲线上的点乘运算,配合一个可靠的随机数,就能变出公钥和私钥。我刚开始接触国密时,觉得这玩意儿跟ECC没啥区别,后来踩了坑才发现——随机数质量才是真正的命门。
SM2密钥对生成原理
SM2基于素域椭圆曲线,参数固定(国密标准里写死了p、a、b、G、n)。生成过程很简单:
- 选一个随机数d ∈ [1, n-2] 作为私钥
- 计算公钥P = d * G(点乘,G是基点)
- 输出(d, P)
嗯,就这么三步。但每一步都藏刀。我见过有人直接用弱随机数生成私钥,结果多次生成发现私钥规律可循——那就等于把门锁送人了。
随机数质量要求
随机数质量直接影响密钥强度。国家标准GB/T 32918.1-2016里明确说了:私钥生成必须用 密码学安全的伪随机数生成器(CSPRNG)。注意,不是math.random()那种东西。
| 随机数等级 | 典型来源 | 适合场景 |
|---|---|---|
| 真随机数(TRNG) | 硬件熵源、量子随机数 | 高安全密钥生成 |
| 密码学安全PRNG | /dev/urandom、Windows CNG | 生产环境密钥生成 |
| 弱PRNG | rand()、C标准库随机数 | 绝不能用 |
我项目中遇到过两次因为用 /dev/random 阻塞导致生成超时的惨案。后来全部改成 /dev/urandom + 熵源健康检查,再也没有卡死过。
熵源设计
熵源是随机数的"米缸"。设计好的熵源,要考虑三点:
- 多样性:混合硬件噪声、系统抖动、用户输入、网络延时等多路源
- 不可预测性:避免使用固定时间戳、进程ID等可观测信号
- 健康监测:实时检测熵池水位,低于阈值告警
实际工程中,我建议按以下结构设计熵源(下面这张图是我画的一个典型方案):
常见生成工具:gmssl 与 openssl
实际干活时,我主要用两个工具:
gmssl(国密官方实现)
gmssl是国密标准组的参考实现,支持SM2全套操作。生成密钥对命令极简:
# 生成SM2私钥(PEM格式)
gmssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2p256v1 -out sm2_private.pem
# 提取公钥
gmssl pkey -in sm2_private.pem -pubout -out sm2_public.pem
我习惯加个 -pkeyopt ec_param_enc:named_curve 参数,保证点编码符合标准,否则有些解析库会报错。
openssl(需编译支持国密)
注意:原生openssl 1.1.1以上版本才支持EC参数自定义。需要手动指定OID或曲线名:
# 生成SM2私钥(需加载国密曲线)
openssl ecparam -genkey -name SM2 -out sm2_private.pem
# 查看公钥
openssl ec -in sm2_private.pem -pubout -out sm2_public.pem
如果openssl编译时没开ec_nistp_64_gcc_128优化,生成速度会慢一个数量级。我曾在ARM平台上吃过这个亏,生成一对密钥花了3秒多——后来换成gmssl,毫秒级完成。
工具对比与选型建议
| 特性 | gmssl | openssl |
|---|---|---|
| 国密协议支持度 | 原生完整 | 需编译额外引擎 |
| 默认随机源 | 系统CSPRNG | 系统CSPRNG |
| 输出格式 | PEM/DER均支持 | PEM为主 |
| ARM平台性能 | 优化较好 | 取决于编译选项 |
| 生态成熟度 | 较新,文档少 | 广泛,资料丰富 |
我的经验之选: 如果项目只做国密,直接用gmssl,省心。如果系统本身已有openssl对接大量老业务,那就编译openssl 1.1.1+ 加上国密引擎,保持统一。但不管用哪个,生成后一定要校验一下私钥长度和公钥点是否为无穷远点。