3. SM2密钥对生成与工程管理

SM2 的密钥对生成看起来只有两步:取一个随机数 d,再算一个点 P = [d]G。但在工程实践中,真正决定系统安全性的往往不是这两步数学运算,而是随机数质量、编码格式、存储方式和生命周期管理。本节围绕这四个环节展开,重点说明“怎么做”和“怎么判断做对了”。

私钥随机数:范围、来源与常见错误

SM2 推荐曲线参数中,基点 G 的阶 n 是一个 256 位素数。私钥 d 必须满足 1 ≤ d ≤ n-2,即它是一个位于 [1, n-2] 区间内的整数。这个范围不是可选项:d = 0 会使公钥退化为无穷远点,d = n 或 d = n-1 在部分实现中会引发边界问题,因此标准明确排除了这些取值。

随机数来源必须使用密码学安全的伪随机数生成器(CSPRNG),例如操作系统提供的 /dev/urandom、getrandom()、BCryptGenRandom 或硬件随机数发生器。工程中最常见的错误有三类:一是用普通语言库的 rand() 或时间戳作为种子,这类随机数可预测;二是随机数生成后未做范围检查,直接当作私钥使用;三是把随机数生成和私钥派生混在一起,导致同一随机数被复用。一个稳妥的做法是:生成 32 字节随机数,将其解释为大端整数,若不在 [1, n-2] 内则重新生成,直到满足条件。

私钥一旦泄露,攻击者可以伪造任意签名、解密任意密文。私钥不能出现在日志、异常信息、调试输出或版本控制系统中。生产环境应优先使用硬件密码模块(HSM)或可信执行环境(TEE)生成并保存私钥。

公钥计算:从私钥到曲线点

公钥是曲线上的一个点 P = (x, y),由 P = [d]G 计算得到。这里的 [d]G 表示基点 G 的 d 倍点,使用椭圆曲线点乘运算。工程实现中通常调用密码库的标量乘法接口,例如在 OpenSSL 中通过 EC_POINT_mul 完成,在 GmSSL 或 BouncyCastle 中也有对应封装。需要强调的是,点乘运算本身必须使用常数时间实现,避免通过时间侧信道泄露私钥信息。

计算完成后,公钥点需要序列化。SM2 公钥有两种常见编码:未压缩格式和压缩格式。未压缩格式为 04 || x || y,共 65 字节;压缩格式为 02/03 || x,共 33 字节,其中 02 表示 y 为偶数,03 表示 y 为奇数。压缩格式能减少传输和存储开销,但需要接收方支持解压。工程中若与外部系统交互,应优先确认对方支持的编码格式,避免因格式不一致导致验签失败。

密钥编码格式:私钥、公钥与容器

私钥的编码同样需要规范。裸私钥是 32 字节大端整数,但直接存储裸私钥不利于识别和导入。常见做法是使用 ASN.1 DER 编码的 ECPrivateKey 结构,或使用 PKCS#8 格式封装。公钥则常使用 SubjectPublicKeyInfo 结构。若需要跨系统交换,还可以使用 PEM 文本格式,即以 -----BEGIN EC PRIVATE KEY----- 或 -----BEGIN PUBLIC KEY----- 包裹 Base64 内容。

下表给出几种常见格式的对比,便于在工程中按场景选择:

对象格式典型长度适用场景
私钥裸 32 字节32 B内部二进制存储、HSM 导入
私钥PKCS#8 DER约 100–150 B跨库导入、密钥容器
公钥未压缩点65 B通用交换、验签
公钥压缩点33 B带宽受限、区块链地址
公钥SubjectPublicKeyInfo约 90 B证书、TLS 配置

密钥存储与生命周期管理

私钥存储的核心原则是“最小暴露面”。软件实现中,私钥应加密保存,例如使用口令派生密钥(PBKDF2、scrypt 或 Argon2)加密后再落盘;更高安全等级应使用 HSM、智能卡或 TEE,使私钥永不离开安全边界。公钥可以公开存储,但仍需保证完整性,防止被替换。

生命周期管理包括生成、使用、轮换、归档和销毁五个阶段。生成阶段要记录密钥用途、算法、曲线和生成时间;使用阶段要限制调用权限和调用频率;轮换阶段应设定有效期,例如签名密钥一年一换,加密密钥按数据敏感级别决定;归档阶段要保证旧密钥可验证历史签名;销毁阶段要确保私钥被安全擦除,而不是简单删除文件。工程中建议为每个密钥分配唯一标识 keyId,并在签名或密文中携带该标识,便于后续定位和轮换。

SM2 密钥对生成与生命周期管理 ① 生成阶段 CSPRNG 生成 32 字节随机数 检查 1 ≤ d ≤ n-2 P = [d]G 计算公钥点 ② 编码阶段 私钥:裸 32B / PKCS#8 公钥:04||x||y 或 02/03||x PEM / DER 容器封装 ③ 存储阶段 私钥加密落盘 / HSM 保护 公钥公开存储 + 完整性校验 分配唯一 keyId 标识 ④ 生命周期管理 生成 记录用途/曲线/时间 使用 限权限/限频率 轮换 按有效期替换 归档 保留历史验签能力 销毁 安全擦除私钥 私钥全程不离开安全边界;公钥可公开但需防替换;keyId 贯穿签名与密文,便于轮换与追溯

在实际工程中,建议把上述流程固化为密钥管理策略:生成时强制使用 CSPRNG 并做范围校验;编码时明确格式并写入文档;存储时区分软件加密与硬件保护;生命周期上为每个密钥建立台账,记录 keyId、用途、算法、生成时间、有效期和状态。这样,当需要轮换或排查问题时,才能快速定位到具体密钥,而不是在一堆无标识的文件中猜测。

一个可落地的检查清单:私钥是否由 CSPRNG 生成?是否校验了 [1, n-2] 范围?公钥编码是否与对端一致?私钥是否加密或由 HSM 保护?是否分配了唯一 keyId?是否设定了有效期和轮换计划?销毁时是否执行了安全擦除?