4. SM2 密钥对生成:私钥、公钥与格式转换
好,咱们进入实战环节。SM2 密钥对生成,是整个国密证书体系的基石。你想想看,后面所有的签名、加密、证书签发,都依赖这一对钥匙。如果这一步搞错了,后面全是白搭。
我个人习惯,在开始任何密码学操作之前,先理清楚我们要做什么。说白了就三步:生成私钥、提取公钥、格式转换。咱们一个一个来。
核心要点:SM2 密钥对基于椭圆曲线。私钥是一个随机大整数,公钥是椭圆曲线上的一个点。记住这个本质,后面理解格式转换就简单了。
4.1 生成 SM2 私钥
生成私钥,本质上就是生成一个合格的随机数。这个随机数必须满足 SM2 算法规定的椭圆曲线参数要求。我见过不少新手直接用 openssl rand 生成一个 256 位的随机数就当私钥用,这是不对的。SM2 的私钥有特定的取值范围,必须用专门的工具生成。
最常用的工具是 OpenSSL(需要 1.1.1 以上版本,并开启国密支持)。命令如下:
# 生成 SM2 私钥(PEM 格式,默认)
openssl ecparam -genkey -name SM2 -out sm2_private_key.pem
# 查看生成的私钥内容
openssl ec -in sm2_private_key.pem -text -noout
嗯,这里要注意。执行完第一条命令,你会得到一个 PEM 文件。用文本编辑器打开,它看起来像这样:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJ7Q7vG...(省略)...oAoGCCqBHM9VAYI1
-----END EC PRIVATE KEY-----
这个文件里包含了什么?我用一个表格给你拆解清楚:
| 字段 | 含义 | 长度 |
|---|---|---|
| 版本号 | 固定为 1(表示 ecPrivateKey) | 1 字节 |
| 私钥值 | 256 位的随机大整数(核心机密) | 32 字节 |
| 曲线参数 OID | 标识使用的是 SM2 曲线(1.2.156.10197.1.301) | 变长 |
| 公钥(可选) | 部分工具会附带公钥,方便使用 | 65 字节(未压缩) |
我的经验:生成私钥后,第一件事就是检查文件权限。我曾经在测试环境里用 644 权限生成私钥,结果被同事的脚本扫到了,虽然只是测试环境,但也吓出一身冷汗。生产环境务必设置为 600 或更严格。
4.2 提取 SM2 公钥
私钥有了,公钥怎么来?从数学上讲,公钥 = 私钥 × 基点 G(椭圆曲线上的标量乘法)。这个计算是单向的,从私钥可以算出公钥,反过来不行。这就是椭圆曲线密码学的安全基础。
用 OpenSSL 提取公钥很简单:
# 从私钥中提取公钥(PEM 格式)
openssl ec -in sm2_private_key.pem -pubout -out sm2_public_key.pem
# 查看公钥内容
openssl ec -pubin -in sm2_public_key.pem -text -noout
提取出来的公钥长这样:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE...(省略)...
-----END PUBLIC KEY-----
公钥的结构和私钥不同,它主要包含:
- 算法标识:告诉解析器这是 SM2 公钥
- 曲线 OID:同私钥,标识 SM2 曲线
- 公钥点:一个 65 字节的数据(04 开头 + 32 字节 x 坐标 + 32 字节 y 坐标)
避坑指南:公钥点有压缩和未压缩两种格式。未压缩格式以 04 开头,共 65 字节;压缩格式以 02 或 03 开头,共 33 字节。SM2 标准推荐使用未压缩格式。我曾经遇到一个对接方,他们用了压缩格式,我这边解析库没处理好,折腾了半天才发现是格式不匹配。
4.3 密钥格式转换:PEM 与 DER
PEM 和 DER 是两种最常见的密钥编码格式。它们的区别,我用一个对比表给你说清楚:
| 特性 | PEM | DER |
|---|---|---|
| 编码方式 | Base64 文本 | 二进制 |
| 可读性 | 可读,有头尾标记 | 不可读,纯二进制 |
| 文件大小 | 比 DER 大约 30% | 更紧凑 |
| 传输方式 | 适合文本协议(HTTP、邮件) | 适合二进制协议(如智能卡) |
| 常见场景 | OpenSSL、Nginx、Java KeyStore | Windows、硬件安全模块 |
转换命令也很直接:
# PEM 转 DER(私钥)
openssl ec -in sm2_private_key.pem -outform DER -out sm2_private_key.der
# DER 转 PEM(私钥)
openssl ec -in sm2_private_key.der -inform DER -out sm2_private_key.pem
# PEM 转 DER(公钥)
openssl ec -pubin -in sm2_public_key.pem -outform DER -out sm2_public_key.der
# DER 转 PEM(公钥)
openssl ec -pubin -in sm2_public_key.der -inform DER -out sm2_public_key.pem
我的习惯:日常开发用 PEM,因为方便查看和调试。部署到生产环境时,如果对性能有极致要求,我会转成 DER 格式,减少 I/O 开销。不过说实话,对于大多数 Web 应用,这点性能差异可以忽略不计。
4.4 知识体系总览
为了让你对整个流程有个全局认识,我画了一张图。这张图把私钥生成、公钥提取、格式转换的完整链路串起来了:
4.5 实战中的常见问题
最后,我分享几个我在项目中踩过的坑:
- 曲线不匹配:生成时用了
prime256v1而不是SM2。虽然都是 256 位椭圆曲线,但参数不同,生成的密钥对不兼容。检查方法:openssl ec -in key.pem -text -noout | grep ASN1_OID,看 OID 是不是 SM2 的。 - 公钥提取失败:私钥文件损坏或格式不对。我遇到过从 Windows 传到 Linux 时换行符被改变,导致解析失败。用
file命令检查文件类型,确保是 PEM 或 DER 格式。 - DER 转 PEM 后多出空行:某些工具对 PEM 格式要求严格,多余的空行会导致解析失败。用
openssl ec -in key.der -inform DER -out key.pem重新转换,不要手动编辑。
总结一下:SM2 密钥对生成,核心就是三步——生成私钥、提取公钥、格式转换。每一步都有对应的 OpenSSL 命令,但更重要的是理解背后的原理和常见陷阱。记住,私钥是命根子,公钥是身份证,格式是交流语言。搞清楚了这些,后面的证书签发就水到渠成了。