SM2 密钥 Shamir 门限分割实战:从密钥风险管控到(m,n)阈值签名

密码学 · 2026-06-29 · 6 阅读

前言

在国密改造项目中,最常见的架构决策是"一人一把密钥"。但现实远比理论残酷——保险柜管理员出差、HSM 固件升级失败、甚至一场办公楼火灾,都可能让企业瞬间丧失签名能力。更致命的是,如果密钥持有者监守自盗,单份密钥即可完美伪造签名,举证都无法区分。

Shamir 秘密共享(Shamir Secret Sharing)正是解决这一痛点的数学工具。它将一份私钥拆分为 n 份(称为"份额"或"share"),只需其中任意 t 份(阈值)即可重构密钥,少于 t 份则毫无信息泄露。这一方案与 SM2 椭圆曲线密码体系天然兼容(因为私钥本质是一个标量整数),本文给出完整的 Python 实现和工程落地指导。

概述

密钥管理的核心挑战

传统密钥管理面临三个不可能三角:

维度单密钥模式HSM 集中模式
可用性✗ 单点故障✓(但 HSM 集群成本高)
安全性✗ 一人泄密全崩✓(但 HSM 厂商信任链)
可审计
灾备△(冷备恢复慢)
门限签名方案打破这一三角:私钥分散存储在 n 个独立位置,攻击者至少需要同时攻破 t 个位置才能盗取密钥。单个位置的泄露仅产生一个无意义的随机数,完全不泄露私钥的任何信息。

Shamir 秘密共享的数学直觉

Shamier 方案基于 t-1 次多项式:

CODE
f(x) = a₀ + a₁·x + a₂·x² + ... + aₜ₋₁·xᵗ⁻¹

其中 a₀ 是秘密(SM2 私钥),其余系数随机生成。我们选取 n 个不同的 x 值(通常取 1,2,...,n)得到 n 个点 (xᵢ, f(xᵢ))——这些就是份额。

关键在于:平面上 t 个点唯一确定一条 t-1 次曲线,少于 t 个点则对应无穷多条可能的曲线,对应的 a₀(即私钥)也有无穷多种确定分布——这就是信息论安全性。

数学原理:拉格朗日插值

已知 t 个份额 (x₁,y₁), (x₂,y₂), ..., (xₜ,yₜ),重构 f(0)a₀

CODE
∏ (0 - xⱼ)          ∏ (-xⱼ)
a₀ = Σ yᵢ · ━━━━━━━━━  = Σ yᵢ · ━━━━━━
        ∏ (xᵢ - xⱼ)         ∏ (xᵢ - xⱼ)
     j≠i                     j≠i

简化后(在素数域 p 中运算):

CODE
a₀ = Σ yᵢ · lᵢ(0)    其中   lᵢ(0) = ∏ (-xⱼ) / ∏ (xᵢ - xⱼ)

除法在模素数域中转化为乘以模逆元。

前置知识:模素数域中的除法 a / b mod p 等于 a · b^(p-2) mod p(费马小定理,p 为素数)。

环境准备

BASH
pip install gmssl>=3.2.0

# 验证安装
python3 -c "from gmssl import sm2, sm3, func; from gmssl.sm2 import default_ecc_table; print('OK')"
本文代码使用 gmssl 3.2.x 版本验证通过cryptography 48.x 不支持 SM2 密钥操作。

核心实现

1. SM2 密钥生成与基础签名

2. Shamir 秘密分享:分割

3. Shamir 秘密共享:重构

🚨 踩坑 #1:在重构时务必先取 shares[:threshold] 而非全部份额。如果传入的份额少于 threshold,重构结果的数学意义完全不同——不再抛出异常,而是得到一个与原始私钥毫不相关的"伪私钥"。这是因为 Lagrange 插值中点的数量超过多项式次数时,结果依赖于选取的子集。

4. 完整的 SM2 门限密钥管理类

🚨 踩坑 #2secrets.randbelow()random.randrange() 的选择——random 使用 Mersenne Twister(非密码学安全),用于生成多项式系数会严重降低安全性。份额的随机系数必须使用 secrets 模块生成密码学安全的随机数。分割后的份额本身不含熵信息(每个份额是一个独立的、统计上均匀的随机数),但系数必须安全随机。

5. 批量测试与验证

运行结果:

生产环境的 3 个踩坑实录

踩坑 #3:多项式系数的生成时机

多项式系数在密钥生成时确定,之后不再改变。因此在密钥轮换前,所有份额都必须使用同一组系数——如果服务器在每次请求时重新生成系数,则份额不可组合。务必将系数与初始密钥绑定存储:

踩坑 #4:份额的防篡改保护

份额本身是素域中的大整数,但网络传输或文件存储可能被篡改。建议每个份额附带 HMAC-SHA256 或 SM3 完整性标签:

踩坑 #5:密钥旋转时的份额过渡

密钥轮换时存在一个危险窗口——旧密钥拆分的份额已收集完毕但新份额尚未分发完毕。建议采用"份额过渡期"策略:在轮换期间要求新旧密钥同时签名(双签名),直到所有份额持有人的新份额已安全存储。

方案对比与选型建议

方案实现复杂度安全性运维成本适用场景
Shamir (3,5)信息论安全中小型企业 TSA/CA 密钥保护
HSM 多签物理安全高(厂商绑定)金融 CA、根密钥
MPC 门限签名极高不暴露完整私钥极高分布式账本、跨机构托管
VSS 可验证分发防分发者作弊清除"分发者知道完整私钥"的信任风险

变体方案:分布式门限签名(进阶)

完整的门限签名需要各参与方在不重构完整私钥的情况下协同生成签名(即 MPC 门限签名)。此方案安全性更优——私钥在整个生命周期中从未完整存在于任何单一节点。工程上可使用 synzc 多方计算协议或基于 Gennaro-Goldfeder 2020 方案的实现(后续文章专门展开)。

条件受限时,门限签名目前仍缺少开箱即用的国密安全实现库。生产环境建议评估后选择 Shamir + HSM 组合方案,或由具备密评资质的机构定制开发。

总结

本文实现了 SM2 私钥的 Shamir (t,n) 门限分割方案,覆盖了:

  • 密钥分割 —— 多项式系数使用 secrets 模块生成密码学安全随机数
  • 密钥重构 —— Lagrange 插值 + 模逆元运算恢复完整私钥
  • 完整性验证 —— 重构后重新计算公钥与原始公钥比对,检测错误的份额
  • 份额加密 —— AES-GCM + 用户口令保护每份份额
核心安全性基于有限域上的多项式性质,信息论安全而非计算安全,因此即使攻击者拥有无限算力也无法从少于 t 个份额中获取私钥的任何信息。

国密门限方案的大规模普及还需依赖行业标准的落地。在缺乏成熟现成库的背景下,本文提供的 Python 实现可作为方案验证和原型开发的基础,生产部署前务必经过严格的第三方安全审计。

参考来源