SM2密钥生成:从原理到落地

密钥对是怎么算出来的?SM2曲线与密钥生成原理

SM2密钥对的核心,说白了就是椭圆曲线上的点乘运算。你想想看,一条固定的椭圆曲线(SM2国标曲线),上面有个已知的基点 G。私钥是一个随机大整数 d,取值范围是 [1, n-2],n 是基点的阶。公钥就是 d 乘以 G,结果是个曲线上的点 P。

公式很简单:P = d × G

我在项目中遇到过一个问题:线上生成密钥对的时候,偶尔耗时特别长。后来排查发现,是因为随机数生成器在某些虚拟化环境下性能不稳定。嗯,这个坑后面会细讲。

一点小经验:SM2 的私钥长度是 256 位,公钥是一个坐标点 (x, y),都是 256 位。总共 64 字节。但实际编码存储时,还会带上算法标识等元数据,所以体积会大一些。

安全随机数:别在这儿栽跟头

密钥安全全看随机数。如果随机数可预测,私钥就相当于暴露了。我曾经见过一个项目,直接用 Python 的 random.randint 生成 SM2 私钥,结果上线不到一周,密钥就被逆向出来了。因为 random 模块用的是梅森旋转算法,属于伪随机,能被预测。

SM2 要求使用密码学安全的随机数生成器(CSPRNG)。常见的三种选择:

  • 操作系统源:Linux 下的 /dev/urandom,Windows 下的 CryptGenRandom
  • Python 标准库:os.urandom 或 secrets 模块
  • 第三方库:OpenSSL 的随机数引擎,gmssl 内部已封装
避坑指南:我曾经在容器环境里发现 /dev/random 阻塞,导致密钥生成超时。建议统一用 /dev/urandom,或者直接调用 gmssl 的 random_hex,它内部已经处理了平台差异。

密钥存储格式:PEM vs DER

生成密钥对之后,得存起来。SM2 的密钥存储格式沿用了 X.509 和 PKCS#8 标准。最常见的两种格式是 PEM 和 DER。

特性PEMDER
编码方式Base64 + 头尾标记二进制 ASN.1 DER
可读性好(纯文本)差(二进制)
大小比 DER 大 30% 左右紧凑
传输方式方便粘贴、拷贝适合二进制协议
常见场景配置文件、文本交换硬件安全模块、嵌入式

PEM 看起来像这样:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg...
-----END PRIVATE KEY-----

SM2 私钥在 PKCS#8 标准中,会嵌套一个 EC 参数 OID(国密是 1.2.156.10197.1.301)。公钥则使用 SubjectPublicKeyInfo 结构,同样含 OID。

实战:用 Python 生成 SM2 密钥对并导出

我平时用的最多的是 gmssl 库,国产 Python 包,原生支持 SM2。下面是一个完整的生成与导出示例:

from gmssl import sm2

# 生成密钥对
private_key, public_key = sm2.GenKeyPair()

print("私钥 (hex):", private_key)
print("公钥 (hex):", public_key)

# 导出为 PEM 格式
private_key_pem = sm2.PrivateKey(private_key).export_pem()
public_key_pem = sm2.PublicKey(public_key).export_pem()

# 导出为 DER 格式
private_key_der = sm2.PrivateKey(private_key).export_der()
public_key_der = sm2.PublicKey(public_key).export_der()

with open("sm2_private.pem", "wb") as f:
    f.write(private_key_pem)
with open("sm2_public.pem", "wb") as f:
    f.write(public_key_pem)

这里 GenKeyPair() 内部调用了 os.urandom 作为随机源,生成 32 字节私钥后,再做一次点乘得到公钥。导出的 PEM 文件自带头尾和换行,可以直接发给对方。

注意:如果你要跨平台兼容,建议统一使用 PEM。DER 虽然小,但解析时需要知道长度,不适合直接文本粘贴。
SM2 密钥生成与存储流程 SM2 密钥生成与存储流程 安全随机数 私钥 d ∈ [1, n-2] SM2基点 G 公钥 P = d×G PEM / DER 编码 密钥文件存储 私钥必须严格保密;公钥可以公开分发 推荐使用 PKCS#8 格式存储 SM2 私钥

这张图概括了从随机数到密钥文件的完整链路。你觉得核心就三步:随机数 → 私钥 → 公钥,最后用 PEM/DER 固化到硬盘。但每一步都有隐藏的门道,后面几章我们会深入拆解签名验签的具体实现。