[密码学实战]详解gmssl库与第三方工具兼容性问题及解决方案

news2025/4/21 17:00:06

[密码学实战]详解gmssl库与第三方工具兼容性问题及解决方案

在这里插入图片描述

引言

国密算法(SM2/SM3/SM4)在金融、政务等领域广泛应用,但开发者在集成gmssl库实现SM2签名时,常遇到与第三方工具(如OpenSSL、国密网关)验证不兼容的问题。本文深入剖析签名验证失败的五大核心原因,并提供可复现的代码解决方案,助你快速定位问题。

一、问题场景复现

使用gmssl生成SM2签名后,通过第三方工具(如OpenSSL命令行、其他语言SDK)验证时,返回“签名无效”或“格式错误”。例如:

# gmssl生成签名代码
from gmssl import sm2, func

sm2_crypt = sm2.CryptSM2(private_key=private_key, public_key=public_key)
sign = sm2_crypt.sign(message.encode(), func.random_hex(32))

# 第三方工具验证失败
openssl pkeyutl -verify -in message.bin -sigfile sign.bin -pubin -inkey pubkey.pem
# 输出: Signature Verification Failure

二、五大核心原因与解决方案

1. 签名格式不兼容(ASN.1 vs 原始R/S拼接)

  • 问题分析
    gmssl默认生成的签名是ASN.1 DER编码格式(如3045022100...),而多数第三方工具要求64字节的R/S拼接值(如r=32字节, s=32字节)。

  • 解决方案
    方法一:关闭ASN.1编码,直接输出R+S拼接

    sm2_crypt = sm2.CryptSM2(private_key=private_key, public_key=public_key, asn1=False)
    sign = sm2_crypt.sign(message.encode(), random_k)  # 输出为64字节十六进制
    

    方法二:手动解析ASN.1签名(需asn1crypto库)

    from asn1crypto import core
    
    der_sign = bytes.fromhex(sign)
    parsed = core.parse(der_sign)
    r = parsed.native['r']
    s = parsed.native['s']
    raw_sign = f"{r:064x}{s:064x}"  # 拼接为64字节
    

2. 公钥/私钥格式错误

  • 问题分析

    • SM2公钥应为非压缩格式(前缀04 + X + Y,共65字节,130字符十六进制)。
    • 私钥应为32字节(64字符十六进制)。
  • 解决方案
    检查密钥格式

    # 正确公钥示例
    public_key = "04" + "x" * 128  # 130字符
    
    # 正确私钥示例
    private_key = "f" * 64  # 64字符
    

    使用gmssl生成标准密钥对

    sm2_crypt = sm2.CryptSM2()
    private_key = sm2_crypt.generate_private_key()  # 自动生成64字符私钥
    public_key = sm2_crypt.generate_public_key()   # 自动生成130字符公钥
    

3. 消息哈希处理不一致

  • 问题分析
    gmsslsign()方法默认对消息自动计算SM3哈希,而第三方工具可能要求传入原始消息手动哈希后的值

  • 解决方案
    手动计算SM3哈希后签名

    from gmssl import sm3
    
    msg = "原始消息".encode()
    hash_msg = sm3.sm3_hash(func.bytes_to_list(msg))  # 返回64字符哈希值
    hash_bytes = bytes.fromhex(hash_msg)
    
    sign = sm2_crypt.sign(hash_bytes, random_k)  # 传入哈希值而非原始消息
    

4. 随机数k生成不安全

  • 问题分析
    SM2签名依赖随机数k,若使用弱随机源(如random库),可能导致私钥泄露。

  • 解决方案
    使用密码学安全随机数生成器

    import secrets
    
    random_k = secrets.token_hex(32)  # 生成32字节安全随机数
    

5. 第三方工具验证命令错误

  • 正确验证流程示例(OpenSSL)
    # 1. 保存消息和签名(原始R+S拼接格式)
    echo -n "hello" > message.bin
    echo -n "a1b2..." | xxd -r -p > sign.bin  # 替换为实际签名值
    
    # 2. 转换为PEM格式公钥(假设公钥为04...)
    echo "-----BEGIN PUBLIC KEY-----" > pubkey.pem
    echo "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE..." >> pubkey.pem  # 替换为Base64编码公钥
    echo "-----END PUBLIC KEY-----" >> pubkey.pem
    
    # 3. 执行验证
    openssl pkeyutl -verify -in message.bin -sigfile sign.bin -pubin -inkey pubkey.pem
    

三、完整修复代码示例

from gmssl import sm2, func
import secrets
import tkinter as tk
from tkinter import messagebox

class SM2SignApp:
    def __init__(self):
        # 初始化GUI组件(省略布局代码)
        self.sm2_input = tk.Text()
        self.sm2_public_key = tk.Text()
        self.sm2_private_key = tk.Text()
        self.sm2_output = tk.Text()

    def sm2_sign(self):
        try:
            # 获取输入
            input_text = self.sm2_input.get("1.0", tk.END).strip().encode('utf-8')
            public_key = self.sm2_public_key.get("1.0", tk.END).strip()
            private_key = self.sm2_private_key.get("1.0", tk.END).strip()

            if not public_key or not private_key:
                messagebox.showerror("错误", "请先生成密钥对")
                return

            # 使用非ASN.1格式签名
            sm2_crypt = sm2.CryptSM2(
                private_key=private_key, 
                public_key=public_key, 
                asn1=False  # 关键参数!!!
            )
            random_k = secrets.token_hex(32)  # 安全随机数
            sign = sm2_crypt.sign(input_text, random_k)

            # 输出签名
            self.sm2_output.delete("1.0", tk.END)
            self.sm2_output.insert(tk.END, sign)

        except Exception as e:
            messagebox.showerror("错误", str(e))

四、总结与避坑指南

  1. 签名格式优先选择R/S拼接,禁用ASN.1编码(asn1=False)。
  2. 严格校验密钥格式,公钥必须含04前缀,私钥为64字符。
  3. 统一哈希处理逻辑,确认第三方工具是否需要原始消息或哈希值。
  4. 使用安全随机数,避免random库,改用secrets或操作系统级随机源。
  5. 验证工具参数匹配,包括编码格式、哈希算法、密钥类型等。

附录:常见问题速查表

现象可能原因快速检测方法
签名长度不为64字符ASN.1编码未关闭检查asn1=False参数
公钥验证失败缺少04前缀或长度错误查看公钥是否为130字符
相同消息每次签名不同随机数k正常生效此为SM2特性,非错误
OpenSSL返回格式错误签名未转换为二进制使用xxd -r -p转换签名

如果本教程帮助您解决了问题,请点赞❤️收藏⭐支持!欢迎在评论区留言交流技术细节!欲了解更深密码学知识,请订阅《密码学实战》专栏 → 密码学实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2339559.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【k8s系列1】一主两从结构的环境准备

环境准备 虚拟机软件准备及安装,这里就不详细展开了,可以看文章:【一、虚拟机vmware安装】 linux环境准备及下载,下载镜像centOS7.9,以前也有写过这个步骤的文章,可以看:【二、安装centOS】 开始进入正题…

【Rust 精进之路之第2篇-初体验】安装、配置与 Hello Cargo:踏出 Rust 开发第一步

系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 **作者:**码觉客 发布日期: 2025-04-20 引言:磨刀不误砍柴工,装备先行! 在上一篇文章中,我们一起探索了 Rust 诞生的缘由&…

腾讯旗下InstantCharacter框架正式开源 可高度个性化任何角色

目前基于学习的主题定制方法主要依赖于 U-Net 架构,但其泛化能力有限,图像质量也大打折扣。同时,基于优化的方法需要针对特定主题进行微调,这不可避免地会降低文本的可控性。为了应对这些挑战,我们提出了 “即时角色”…

详讲Linux下进程等待

3.进程等待 引言:什么是进程等待 想象有两个小伙伴,一个是 “大强”(父进程 ),一个是 “小强”(子进程 )。大强给小强安排了任务,比如去收集一些石头。 …

JBoss + WildFly 本地开发环境完全指南

JBoss WildFly 本地开发环境完全指南 本篇笔记主要实现在本地通过 docker 创建 JBoss 和 WildFly 服务器这一功能,基于红帽的禁制 EAP 版本的重新分发,所以我这里没办法放 JBoss EAP 的 zip 文件。WildFly 是免费开源的版本,可以在红帽官网找…

【网络原理】TCP协议如何实现可靠传输(确认应答和超时重传机制)

目录 一. TCP协议 二. 确定应答 三. 超时重传 一. TCP协议 1)端口号 源端口号:发送方端口号目的端口号:接收方端口号 16位(2字节)端口号,可以表示的范围(0~65535) 源端口和目的…

【国家能源集团生态协作平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…

idea中导入从GitHub上克隆下来的springboot项目解决找不到主类的问题

第一步:删除目录下的.idea和target,然后用idea打开 第二步:如果有需要,idea更换jdk版本 原文链接:https://blog.csdn.net/m0_74036731/article/details/146779040 解决方法(idea中解决)&#…

【AI论文】CLIMB:基于聚类的迭代数据混合自举语言模型预训练

摘要:预训练数据集通常是从网络内容中收集的,缺乏固有的领域划分。 例如,像 Common Crawl 这样广泛使用的数据集并不包含明确的领域标签,而手动整理标记数据集(如 The Pile)则是一项劳动密集型工作。 因此&…

Linux操作系统--环境变量

目录 基本概念: 常见环境变量: 查看环境变量的方法: 测试PATH 测试HOME 和环境变量相关的命令 环境变量的组织方式:​编辑 通过代码如何获取环境变量 通过系统调用获取或设置环境变量 环境变量通常具有全局属性 基本概念…

Jenkins 多分支管道

如果您正在寻找一个基于拉取请求或分支的自动化 Jenkins 持续集成和交付 (CI/CD) 流水线,本指南将帮助您全面了解如何使用 Jenkins 多分支流水线实现它。 Jenkins 的多分支流水线是设计 CI/CD 工作流的最佳方式之一,因为它完全基于 git(源代…

C语言之图像文件的属性

🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 图像文件属性提取系统设计与实现 目录 设计题目设计内容系统分析总体设计详细设计程序实现…

LeetCode hot 100—分割等和子集

题目 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 示例 1: 输入:nums [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。…

高等数学同步测试卷 同济7版 试卷部分 上 做题记录 上册期中同步测试卷 B卷

上册期中同步测试卷 B卷 一、单项选择题(本大题共5小题,每小题3分,总计15分) 1. 2. 3. 4. 5. 由f(2/n), n→∞可知 2/n→0, 即x→0. 二、填空题(本大题共5小题,每小题3分,总计15分) 6. 7. 8. 9. 10. 三、求解下列各题(本大题共5小…

【算法】快速排序、归并排序(非递归版)

目录 一、快速排序&#xff08;非递归&#xff09; 1.原理 2.实现 2.1 stack 2.2 partition(array,left,right) 2.3 pivot - 1 > left 二、归并排序&#xff08;非递归&#xff09; 1.原理 2.实现 2.1 gap 2.1.1 i 2*gap 2.1.2 gap * 2 2.1.3 gap < array.…

【实战中提升自己】内网安全部署之dot1x部署 本地与集成AD域的主流方式(附带MAC认证)

1 dot1x部署【用户名密码认证&#xff0c;也可以解决私接无线AP等功能】 说明&#xff1a;如果一个网络需要通过用户名认证才能访问内网&#xff0c;而认证失败只能访问外网与服务器&#xff0c;可以部署dot1x功能。它能实现的效果是&#xff0c;当内部用户输入正常的…

[matlab]南海地形眩晕图代码

[matlab]南海地形眩晕图代码 请ChatGPT帮写个南海地形眩晕图代码 图片 图片 代码 .rtcContent { padding: 30px; } .lineNode {font-size: 12pt; font-family: "Times New Roman", Menlo, Monaco, Consolas, "Courier New", monospace; font-style: n…

Web安全和渗透测试--day6--sql注入--part 1

场景&#xff1a; win11家庭版&#xff0c;edge浏览器 &#xff0c; sqlin靶场 定义&#xff1a; SQL 注入&#xff08;SQL Injection&#xff09;是一种常见的网络安全攻击方式&#xff0c;攻击者通过在 Web 应用程序中输入恶意的 SQL 代码&#xff0c;绕过应用程序的安全机…

[SpringBoot]快速入门搭建springboot

默认有spring基础&#xff0c;不会一行代码一行代码那么细致地讲。 SpringBoot的作用 Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的。就像我们整个SSM框架时&#xff0c;就常常会碰到版本导致包名对不上、Bean非法参数类型的一系列问题&#xff08;原出…

理解.NET Core中的配置Configuration

什么是配置 .NET中的配置&#xff0c;本质上就是key-value键值对&#xff0c;并且key和value都是字符串类型。 在.NET中提供了多种配置提供程序来对不同的配置进行读取、写入、重载等操作&#xff0c;这里我们以为.NET 的源码项目为例&#xff0c;来看下.NET中的配置主要是有…