news 2026/6/11 17:38:14

别再手动算参数了!用Python一步步复现SM2协同签名全过程(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再手动算参数了!用Python一步步复现SM2协同签名全过程(附完整代码)

用Python实战SM2协同签名:从数学公式到可运行代码的完整指南

在密码学领域,SM2作为我国自主研发的椭圆曲线公钥密码算法标准,其协同签名机制因能实现多方安全计算而备受关注。但当你翻阅理论文档时,是否曾被那些抽象的数学符号和冗长的参数计算步骤劝退?本文将带你用Python一步步拆解SM2协同签名的每个环节,把晦涩的公式转化为可执行的代码。

1. 环境准备与基础工具

实现SM2协同签名需要几个核心组件:椭圆曲线运算库、大整数处理工具和哈希函数。我们选择gmssl库作为基础,它原生支持SM2算法曲线参数。

首先安装必要依赖:

pip install gmssl pycryptodome

初始化SM2椭圆曲线参数(基于GMT 0003.5-2012标准):

from gmssl import sm2, func import secrets # 标准SM2曲线参数 SM2_p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF SM2_a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC SM2_b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93 SM2_n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123 SM2_Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7 SM2_Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0

注意:实际开发中应使用标准库提供的曲线参数,此处仅为演示原理。gmssl内部已内置这些参数。

2. 密钥生成与参数交换

协同签名的核心在于客户端和服务端各自持有部分私钥,通过安全交互完成签名。我们先实现密钥对生成:

def generate_key_pair(): """生成SM2密钥对""" private_key = secrets.randbelow(SM2_n) public_key = sm2.base_point_mult(private_key) return private_key, public_key # 客户端密钥 d1, P1 = generate_key_pair() # 服务端密钥 d2, P2 = generate_key_pair()

公共公钥P的计算需要双方交换公钥:

def compute_shared_public_key(P1, P2): """计算协同签名公共公钥 P = (d1*d2-1)*G """ # 等效于 P = (P1 * d2) - G 或 (P2 * d1) - G temp_point = sm2.point_mult(d1, P2) # P1 = d1*G, 所以 P1*d2 = d1*d2*G shared_pub = sm2.point_sub(temp_point, (SM2_Gx, SM2_Gy)) return shared_pub P = compute_shared_public_key(P1, P2)

3. 签名过程分步实现

3.1 客户端第一阶段计算

客户端生成随机数K1并计算相关参数:

def client_phase1(d1, P2): K1 = secrets.randbelow(SM2_n) R1 = sm2.base_point_mult(K1) # R1 = K1*G R1_ = sm2.point_mult(K1, P2) # R1_ = K1*P2 return K1, R1, R1_ K1, R1, R1_ = client_phase1(d1, P2)

服务端收到后需验证R1_ == sm2.point_mult(d2, R1),因为:

R1_ = K1*P2 = K1*d2*G d2*R1 = d2*K1*G = K1*d2*G

3.2 服务端响应计算

服务端生成K2并计算响应参数:

def server_phase(d2, P1, R1, R1_): # 验证客户端参数 if not sm2.point_equal(R1_, sm2.point_mult(d2, R1)): raise ValueError("Client parameter verification failed") K2 = secrets.randbelow(SM2_n) R2_ = sm2.base_point_mult(K2) # R2_ = K2*G R2 = sm2.point_mult(K2, P1) # R2 = K2*P1 return K2, R2_, R2 K2, R2_, R2 = server_phase(d2, P1, R1, R1_)

3.3 客户端签名计算

客户端验证服务端参数并计算部分签名:

def client_sign(m, d1, K1, K2, R2_, R2): # 验证服务端参数 if not sm2.point_equal(R2, sm2.point_mult(d1, R2_)): raise ValueError("Server parameter verification failed") # 计算签名随机数K K = (K1 + K2 * d1) % SM2_n # 计算r = (H(M) + x1) mod n x1, _ = sm2.base_point_mult(K) e = int.from_bytes(func.sm3_hash(m.encode()), 'big') r = (e + x1) % SM2_n # 计算s_ = (K1 + r) / d1 mod n s_ = (K1 + r) * pow(d1, -1, SM2_n) % SM2_n return r, s_ message = "plaintext" r, s_ = client_sign(message, d1, K1, K2, R2_, R2)

4. 服务端完成签名

服务端收到s_后计算最终签名分量:

def server_complete_sign(d2, K2, s_, r): # 计算 t = (s_ + K2) / d2 mod n t = (s_ + K2) * pow(d2, -1, SM2_n) % SM2_n # 客户端最终计算 s = (t - r) mod n s = (t - r) % SM2_n return t, s t, s = server_complete_sign(d2, K2, s_, r)

现在我们已经得到完整签名(r, s)。验证签名的正确性:

def verify_signature(P, message, signature): sm2_crypt = sm2.CryptSM2(private_key="", public_key=P) return sm2_crypt.verify(signature, message) signature = (r, s) print("Signature valid:", verify_signature(P, message, signature))

5. 关键问题与调试技巧

在实际实现过程中,可能会遇到以下典型问题:

模逆运算失败
SM2签名涉及多个模逆计算(如pow(d1, -1, SM2_n))。当d1与n不互质时,会抛出异常。解决方案:

def safe_inv(a, n): try: return pow(a, -1, n) except ValueError: # 极低概率事件,可重新生成密钥 raise ValueError("No modular inverse exists, regenerate keys")

参数验证失败
协同签名过程中有两次关键验证:

  1. 服务端验证R1_ == d2*R1
  2. 客户端验证R2 == d1*R2_

验证失败通常意味着:

  • 随机数生成不符合规范
  • 参数在传输过程中被篡改
  • 密钥对生成逻辑有误

签名验证不通过
如果最终签名验证失败,可按以下步骤排查:

检查项验证方法可能原因
公共公钥P比较P1d2-G与P2d1-G密钥交换错误
随机数K检查K=K1+K2*d1 mod n参数传递错误
哈希值e对比标准SM3实现消息编码不一致

6. 性能优化实践

当需要高频执行协同签名时,可考虑以下优化手段:

预计算加速
椭圆曲线点乘是最耗时的操作,对固定参数可预先计算:

# 客户端预计算d1的逆元 d1_inv = pow(d1, -1, SM2_n) # 服务端预计算d2的逆元 d2_inv = pow(d2, -1, SM2_n)

并行计算
客户端和服务端的部分计算可并行执行:

from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor() as executor: # 客户端并行计算R1和R1_ future_R1 = executor.submit(sm2.base_point_mult, K1) future_R1_ = executor.submit(sm2.point_mult, K1, P2) R1, R1_ = future_R1.result(), future_R1_.result()

缓存机制
对于长期合作的客户端-服务端对,可缓存部分中间结果减少重复计算。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 17:35:14

汽车仪表盘LCD驱动芯片PCA85162:低复用驱动与RAM映射实战解析

1. 项目概述:汽车仪表盘背后的“像素指挥官”在汽车座舱里,无论是经典的指针式仪表盘旁那块显示里程、水温的小屏幕,还是中控台上那些显示空调状态、时间信息的字符区域,背后都离不开一个看似不起眼却至关重要的角色——LCD驱动芯…

作者头像 李华
网站建设 2026/6/11 17:33:55

LuckyLilliaBot:三合一协议机器人框架的终极指南

LuckyLilliaBot:三合一协议机器人框架的终极指南 【免费下载链接】LuckyLilliaBot 支持 OneBot 11、Satori 和 Milky 协议 项目地址: https://gitcode.com/gh_mirrors/li/LuckyLilliaBot LuckyLilliaBot是一个支持OneBot 11、Satori和Milky三大协议的现代化Q…

作者头像 李华
网站建设 2026/6/11 17:31:50

Mechvibes终极指南:5分钟创建你的专属机械键盘音效包 [特殊字符]

Mechvibes终极指南:5分钟创建你的专属机械键盘音效包 🎹 【免费下载链接】mechvibes Mechvibes 项目地址: https://gitcode.com/gh_mirrors/me/mechvibes 想要为你的键盘打字体验增添个性化音效吗?Mechvibes是一款免费开源的键盘音效软…

作者头像 李华
网站建设 2026/6/11 17:31:01

突破性IP定位革命:如何在5分钟内构建微秒级离线查询系统

突破性IP定位革命:如何在5分钟内构建微秒级离线查询系统 【免费下载链接】ip2region Ip2region is an offline IP-to-Region localization library and IP data management framework with both IPv4 and IPv6 supports, 10-microsecond level query efficiency, xd…

作者头像 李华
网站建设 2026/6/11 17:28:26

3步解锁九大网盘全速下载:技术合规的直链获取终极方案

3步解锁九大网盘全速下载:技术合规的直链获取终极方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼…

作者头像 李华