4. SM2 密钥对生成:OpenSSL 命令行实战

密钥对生成,是所有国密改造的第一步。说白了,没有密钥,后面签名、加密、证书申请全都无从谈起。SM2 的密钥生成跟 RSA 不太一样,它基于椭圆曲线,所以参数更少,但细节反而更容易踩坑。

我个人习惯,在项目里永远先用 OpenSSL 命令行把流程跑通,再写代码。为什么?因为命令行是最直接的调试工具,出错了你能立刻看到错误码,不用猜。好,我们直接开干。

4.1 环境确认:你的 OpenSSL 支持 SM2 吗?

先别急着敲命令。我遇到过好几次,同事拿着系统自带的 OpenSSL 1.0.2 折腾半天,结果根本不支持 SM2。嗯,这里要注意:

  • OpenSSL 1.1.1+:原生支持 SM2,直接可用
  • OpenSSL 1.1.0 及以下:需要打国密补丁,或者用 GmSSL 替代
  • GmSSL:国密专用分支,命令略有差异

怎么验证?跑一行命令:

openssl ecparam -list_curves | grep SM2

如果输出里有 SM2 字样,恭喜你,环境没问题。如果没输出,要么版本太低,要么编译时没开 EC 支持。我曾经在一个 CentOS 6 的老机器上折腾了俩小时,最后发现是系统自带的 OpenSSL 0.9.8……直接换 GmSSL 解决。

4.2 生成 SM2 私钥(PEM 格式)

PEM 是最常见的格式,Base64 编码,可读性好。生成命令很简单:

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

拆解一下:

  • ecparam:椭圆曲线参数操作命令
  • -genkey:生成密钥对
  • -name SM2:指定曲线为 SM2 国密曲线
  • -out sm2_private.pem:输出文件

生成的私钥长这样:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJ7X1V5...(省略)...oAoGCCqBHM9VAYI1
-----END EC PRIVATE KEY-----

注意看,它用的是 EC PRIVATE KEY 标识,不是 RSA 那种 RSA PRIVATE KEY。这个细节在证书申请时特别重要——有些工具会按关键字判断密钥类型,搞错了直接报错。

我的习惯:生成私钥后,立刻用 openssl ec -in sm2_private.pem -text -noout 查看一下。确认曲线名称是 SM2,私钥长度是 256 位。这一步花不了 10 秒,但能避免后续很多莫名其妙的错误。

4.3 私钥格式转换:PEM ↔ DER

DER 是二进制格式,Java 和一些硬件安全模块(HSM)喜欢用它。转换命令:

# PEM 转 DER
openssl ec -in sm2_private.pem -outform DER -out sm2_private.der

# DER 转 PEM
openssl ec -in sm2_private.der -inform DER -out sm2_private.pem

你想想看,DER 文件用文本编辑器打开全是乱码,但体积比 PEM 小大概 30%。在嵌入式设备或者带宽受限的场景下,DER 更实用。我在一个物联网项目中,设备 Flash 只有 512KB,私钥必须用 DER 存储,省一点是一点。

特性 PEM DER
编码方式 Base64 + 头尾标识 二进制 ASN.1
可读性 好,可直接查看 差,需工具解析
文件大小 较大(约 300 字节) 较小(约 220 字节)
常见场景 OpenSSL、Nginx、Apache Java KeyStore、HSM、嵌入式

4.4 从私钥导出公钥

公钥是从私钥推导出来的,SM2 的椭圆曲线算法保证了这一点。导出命令:

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

公钥内容:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE...(省略)...
-----END PUBLIC KEY-----

注意,这里用的是 PUBLIC KEY 标识,不是 EC PUBLIC KEY。为什么?因为 OpenSSL 默认把公钥包装成 SubjectPublicKeyInfo 格式,包含了算法标识。如果你需要纯 EC 公钥(不带算法头),加个 -conv_form compressed 参数:

openssl ec -in sm2_private.pem -pubout -conv_form compressed -out sm2_public_comp.pem

压缩格式的公钥只有 33 字节(非压缩是 65 字节),在区块链和证书透明度(CT)场景里很常见。我记得有一次做证书链优化,公钥体积减半后,TLS 握手时间直接降了 15%。

避坑指南:千万不要把私钥文件直接发给别人!公钥导出后,建议用 openssl pkey -in sm2_public.pem -pubin -text -noout 验证一下。我曾经见过一个案例,同事误把私钥当公钥发出去,虽然 SM2 私钥本身有保护,但这种低级错误在等保测评里会被扣分。

4.5 核心流程:一张图看懂

下面这张图总结了 SM2 密钥对生成的完整流程,以及各格式之间的转换关系。我建议你把它存下来,以后做国密改造时对照着用。

SM2 密钥对生成与格式转换流程 ecparam -genkey -name SM2 私钥 PEM 文件 ec -pubout 公钥 PEM 文件 ec -outform DER 私钥 DER ec -pubout -outform DER 公钥 DER 验证:ec -text -noout 💡 说明: PEM 适合人眼阅读和文本传输;DER 体积小,适合嵌入式/Java 环境。 公钥导出后务必验证曲线名称和密钥长度,避免后续证书申请失败。

4.6 批量生成与安全建议

在实际项目中,你可能需要一次性生成几百个密钥对。写个脚本循环调用就行,但我建议你注意三点:

  1. 随机数质量:服务器端用 /dev/urandom,嵌入式设备如果缺乏硬件随机数,考虑外挂 TRNG 芯片。SM2 的私钥安全性完全依赖随机数,这个不能省。
  2. 私钥权限:生成后立刻 chmod 600,防止其他进程读取。我在一个金融项目中,就因为 CI/CD 脚本没设权限,私钥被日志采集器扫走了……还好是测试环境。
  3. 备份与加密:私钥建议用 openssl ec -aes-256-cbc 加密存储,密码单独保管。等保三级要求私钥必须加密存储,别等测评时再补。

核心要点回顾:

  • SM2 密钥生成用 ecparam -genkey -name SM2,不是 genrsa
  • PEM 和 DER 互转用 ec -outform/inform
  • 公钥导出用 ec -pubout,压缩格式加 -conv_form compressed
  • 生成后务必验证:曲线名称、密钥长度、文件权限

嗯,SM2 密钥对生成这部分就这些。命令行操作看起来简单,但每个参数背后都有设计考量。你只要把今天这几个命令练熟,后续的证书请求和签名操作就会顺畅很多。