简介:这份资源面向参与国家医保平台对接的Java与C#开发人员及测试工程师,聚焦国密算法SM2、SM3、SM4在医保业务场景下的加解密验证需求,帮助解决接口联调中签名、验签、加密、解密等环节的算法一致性问题。压缩包共171个文件,约75.48MB,以66个dll动态库、8个exe可执行程序、2个jar包及2个lib库文件构成核心运行与调用组件,另含md说明文档、license与policy授权策略文件、properties与cfg配置项、log日志及certs证书等辅助内容,整体结构偏向可直接运行的测试工具形态。资源同时提供Java与C#两套SM2、SM3、SM4源码,便于读者对照两种语言实现理解国密算法的调用方式与参数配置差异。目前已有417人学习下载,适合需要快速搭建医保国密测试环境、验证算法正确性或排查对接异常的技术人员参考使用。
1. 医保接口联调现场:为什么一个 SM2 签名能卡住整条链路
做过医保接口对接的同行大概都经历过这种场面:医院端 HIS 厂商说签名没问题,医保平台侧返回“验签失败”,双方各执一词,日志里只有一串 Base64 密文,谁也说不清是 SM2 签名值算错了,还是 SM4 报文加密的填充方式对不上。国家医保平台强制要求使用国密算法,SM2 负责签名验签和密钥交换,SM4 负责报文对称加密,这套组合和常见的 RSA+AES 完全不是一回事,很多通用调试工具根本不支持。
这份“国家医保 SM2、SM4 测试工具”就是冲着这个场景来的。它把 SM2 签名验签、SM4 加解密、密钥对生成、摘要计算这几件事打包成一个可本地运行的测试环境,让你在正式联调之前先把算法层面的问题排干净。适合三类人:正在做医保接口对接的后端开发、需要验证厂商实现是否合规的测试人员、以及想搞明白国密算法在医保报文里到底怎么用的技术负责人。下面我从工具的能力边界讲起,一路拆到参数配置和踩坑记录。
2. SM2/SM4 在医保报文里的分工:先搞清算法边界再动手
2.1 医保接口的加密签名流程拆解
医保平台的标准报文交互大致是这样的:业务数据先转成 JSON 或 XML,然后用 SM4 对报文主体做对称加密,SM4 的密钥本身再用 SM2 公钥加密后附在报文头里,同时发送方用 SM2 私钥对报文摘要做签名。接收方拿到之后,先用 SM2 私钥解出 SM4 密钥,再用 SM4 解密报文主体,最后用 SM2 公钥验签。整条链路里 SM2 和 SM4 各司其职,任何一个环节的参数不对,最终都会表现为“验签失败”或“解密乱码”。
这里有个容易混淆的点:SM2 既是非对称加密算法,也是签名算法,还包含密钥交换协议。医保场景里主要用到的是 SM2 签名验签和 SM2 加密解密这两个功能。SM4 则是对称分组加密,分组长度 128 位,密钥长度也是 128 位。工具需要同时覆盖这两类操作,才能完整模拟一次报文交互。
2.2 为什么通用工具搞不定国密
OpenSSL 在 1.1.1 版本之后才开始逐步支持国密算法,但默认编译选项里往往没开 SM2/SM4,即使开了,SM2 的签名格式(DER 编码 vs 裸 r||s)和医保平台要求的格式也可能不一致。在线加解密网站更不用说了,基本只支持 RSA/AES,而且把密钥传到第三方平台本身就有合规风险。
我一般会建议团队在本地搭一个独立的测试环境,用专门的国密工具来做算法验证。这份测试工具的价值就在于它把 SM2/SM4 的常用操作都封装好了,不需要你自己去编译 OpenSSL 或者写一堆 BouncyCastle 的调用代码。它支持的操作包括:生成 SM2 密钥对、SM2 签名、SM2 验签、SM2 加密、SM2 解密、SM4 加密、SM4 解密、SM3 摘要计算。覆盖了医保报文交互的全部算法需求。
2.3 工具的核心能力与适用边界
| 能力项 | 说明 | 医保场景对应 |
|---|---|---|
| SM2 密钥对生成 | 生成符合国密标准的公私钥 | 机构侧密钥初始化 |
| SM2 签名/验签 | 支持 DER 和裸签名两种格式 | 报文签名与平台验签 |
| SM2 加密/解密 | 公钥加密、私钥解密 | SM4 密钥的安全传输 |
| SM4 加密/解密 | ECB/CBC 模式,支持多种填充 | 报文主体加解密 |
| SM3 摘要 | 计算报文的 SM3 哈希值 | 签名前的摘要计算 |
需要明确的是,这个工具是算法层面的测试工具,不是完整的医保接口模拟器。它不会帮你构造完整的医保报文头、不会处理 HTTP 传输、也不涉及业务逻辑校验。它的定位是:当你怀疑是算法参数出了问题的时候,用它来快速验证 SM2/SM4 的计算结果是否正确。搞清楚这个边界很重要,否则你会花时间去找一个它根本不提供的功能。
3. 把工具跑起来:环境准备与 SM2 签名验签实操
3.1 运行环境与依赖准备
工具本身是一个独立的可执行程序,不需要安装额外的运行时环境。但如果你需要从源码构建,或者想把它集成到自己的测试流水线里,就需要准备以下环境:
# 确认系统架构(工具通常提供 x86_64 和 ARM64 两个版本) uname -m # 查看操作系统版本,确认 glibc 版本是否满足要求 ldd --version # 如果需要从源码构建,确认 Java 环境(部分实现基于 Java) java -version这里的关键参数是系统架构和 glibc 版本。我遇到过在 CentOS 7 上跑不起来的情况,原因是 glibc 版本太低,工具依赖的某些系统调用不存在。解决办法要么升级系统,要么用静态编译的版本。另外,如果你打算把工具集成到 CI 流水线里,建议用 Docker 封装一层,避免不同构建节点的环境差异导致结果不一致。
3.2 生成 SM2 密钥对并导出
第一步永远是生成密钥对。医保场景里,机构侧需要生成自己的 SM2 密钥对,把公钥报送给医保平台备案,私钥自己保管好。工具生成密钥对的操作大概是这样的:
# 生成 SM2 密钥对,输出到指定目录 ./sm-test-tool keygen --algorithm SM2 --output ./keys/ # 查看生成的密钥文件 ls -la ./keys/ # 预期输出: # sm2_private.pem (私钥文件) # sm2_public.pem (公钥文件)生成之后一定要检查密钥格式。医保平台通常要求公钥以特定的格式报送,有的是 PEM 格式,有的是 HEX 裸公钥(04 开头的 130 位十六进制字符串)。工具一般会同时提供这两种导出方式。私钥文件权限要设成 600,别问我怎么知道的,血泪经验。
3.3 SM2 签名与验签的完整操作
签名验签是医保联调里出问题最多的环节。工具的操作流程如下:
# 对一段测试数据做 SM2 签名 ./sm-test-tool sign \ --algorithm SM2 \ --private-key ./keys/sm2_private.pem \ --data "test medical insurance payload" \ --output ./signature.txt # 查看签名结果 cat ./signature.txt # 输出类似:MEUCIQDx...(DER 编码的签名值) # 用公钥验签 ./sm-test-tool verify \ --algorithm SM2 \ --public-key ./keys/sm2_public.pem \ --data "test medical insurance payload" \ --signature ./signature.txt # 预期输出:Verify OK这里有几个参数需要特别注意。--data传入的是原始数据,工具内部会先做 SM3 摘要再签名,不需要你手动计算摘要。如果你的实现是先算 SM3 再调签名接口,那验签的时候也要对应先算 SM3。签名格式方面,工具默认输出 DER 编码,但有些医保平台要求裸签名(r||s 各 32 字节),这时候需要加--format raw参数。我建议在联调初期就把两种格式都测一遍,确认平台侧到底认哪种。
3.4 SM4 加解密与填充模式选择
SM4 的坑主要在填充模式和 IV(初始化向量)上。医保报文常用的模式是 ECB 和 CBC,填充方式一般是 PKCS7。工具的操作示例:
# SM4 CBC 模式加密 ./sm-test-tool sm4-encrypt \ --mode CBC \ --key 0123456789abcdef0123456789abcdef \ --iv 00000000000000000000000000000000 \ --padding PKCS7 \ --data "medical insurance message body" \ --output ./encrypted.bin # SM4 解密 ./sm-test-tool sm4-decrypt \ --mode CBC \ --key 0123456789abcdef0123456789abcdef \ --iv 00000000000000000000000000000000 \ --padding PKCS7 \ --data ./encrypted.bin密钥和 IV 都是 32 位十六进制字符串(对应 16 字节)。ECB 模式不需要 IV,但安全性不如 CBC。医保平台如果指定了 ECB,那就用 ECB,别自己改成 CBC,否则平台侧解不出来。填充方式如果平台没明确说,默认用 PKCS7,这是最常见的。如果解密出来末尾有乱码,大概率是填充方式不对,可以试试 NoPadding 或者 ZeroPadding。
4. 联调避坑:五条血泪排查记录
4.1 现象:验签一直失败,但签名值看起来正常
原因:最常见的情况是签名格式不匹配。你的工具输出的是 DER 编码签名,但医保平台期望的是裸签名(r||s)。另一种可能是双方对原始数据的处理不一致,比如你签的是原始字符串,平台验签时先做了 Base64 解码或者字符集转换。
解决:先用工具的--format raw参数输出裸签名再试一次。如果还不行,把签名前的原始数据用十六进制打印出来,和平台侧确认他们验签时用的数据是否完全一致。字符集问题也很常见,UTF-8 和 GBK 编码的同一段中文,SM3 摘要完全不同。
4.2 现象:SM4 解密出来是乱码
原因:填充模式不对,或者 IV 不对。CBC 模式下 IV 错了,第一个分组解密出来一定是乱的。ECB 模式下如果误传了 IV,有些实现会忽略,有些会报错。
解决:先确认平台侧用的模式是 ECB 还是 CBC。如果是 CBC,IV 通常是全零或者由平台在报文头里下发。填充方式优先试 PKCS7,不行再试 NoPadding。如果解密结果只有末尾几个字节乱,那基本就是填充问题。
4.3 现象:SM2 加密后的密文平台解不出来
原因:SM2 加密有两种模式,C1C3C2 和 C1C2C3,区别在于密文各部分的排列顺序。医保平台通常要求 C1C3C2 模式,但有些老版本实现用的是 C1C2C3。
解决:工具一般会提供--cipher-mode参数来切换。先确认平台文档里写的是哪种模式,如果没有文档,两种都试一遍。另外注意 C1 部分是椭圆曲线点,格式是 04||X||Y,有些实现会省略 04 前缀,这也是常见的兼容性问题。
4.4 现象:密钥对生成后平台备案不通过
原因:公钥格式不符合平台要求。有的平台要求 PEM 格式,有的要求 HEX 裸公钥,还有的要求把公钥做 Base64 编码后再报送。
解决:用工具导出多种格式的公钥,逐一比对平台要求。特别注意 HEX 格式下有没有 04 前缀,以及大小写是否敏感。私钥一定要保管好,不要和公钥一起打包发送。
4.5 现象:本地测试通过,一上生产就报错
原因:生产环境的字符集、换行符或者数据长度和测试环境不一致。比如测试用的短报文和生产的长报文在 SM4 分组填充时行为不同,或者生产环境的换行符是 CRLF 而测试环境是 LF。
解决:用生产环境的真实报文样本(脱敏后)做一次完整的加解密和签名验签测试。特别注意报文长度是否超过了 SM4 单次加密的限制,如果报文很长,需要确认平台侧是否要求分块加密。
5. 进阶技巧:用脚本批量验证与结果比对
工具的命令行接口可以很方便地集成到自动化测试脚本里。我一般会写一个批量验证脚本,把常见的参数组合都跑一遍,快速定位是哪个参数出了问题。
import subprocess import json # 定义需要测试的参数组合 test_cases = [ {"mode": "CBC", "padding": "PKCS7", "cipher_mode": "C1C3C2"}, {"mode": "CBC", "padding": "PKCS7", "cipher_mode": "C1C2C3"}, {"mode": "ECB", "padding": "PKCS7", "cipher_mode": "C1C3C2"}, {"mode": "CBC", "padding": "NoPadding", "cipher_mode": "C1C3C2"}, ] test_data = "test medical insurance payload for batch verification" for case in test_cases: # 构造命令行参数 cmd = [ "./sm-test-tool", "sm4-encrypt", "--mode", case["mode"], "--padding", case["padding"], "--key", "0123456789abcdef0123456789abcdef", "--data", test_data ] if case["mode"] == "CBC": cmd.extend(["--iv", "00000000000000000000000000000000"]) # 执行加密 result = subprocess.run(cmd, capture_output=True, text=True) # 记录结果 print(f"Mode: {case['mode']}, Padding: {case['padding']}, " f"CipherMode: {case['cipher_mode']}, " f"ExitCode: {result.returncode}") # 如果加密成功,尝试解密验证 if result.returncode == 0: decrypt_cmd = [ "./sm-test-tool", "sm4-decrypt", "--mode", case["mode"], "--padding", case["padding"], "--key", "0123456789abcdef0123456789abcdef", "--data", result.stdout.strip() ] if case["mode"] == "CBC": decrypt_cmd.extend(["--iv", "00000000000000000000000000000000"]) decrypt_result = subprocess.run(decrypt_cmd, capture_output=True, text=True) # 比对解密结果和原始数据是否一致 match = decrypt_result.stdout.strip() == test_data print(f" Decrypt match: {match}")这段脚本的逻辑很直接:遍历不同的模式、填充方式和密文排列组合,对同一段测试数据做加密再解密,检查解密结果是否和原始数据一致。关键参数是test_cases列表,你可以根据实际联调情况往里加组合。subprocess.run的capture_output=True会把命令的输出捕获下来,方便后续比对。
跑完这个脚本,你就能得到一张参数组合与结果对应表,拿这张表去和医保平台侧对,很快就能锁定是哪个参数没对齐。我一般会在联调开始前就把这张表跑出来,省得后面来回扯皮。
从那以后我每次对接新的医保接口,都会先用这套工具把 SM2 签名验签和 SM4 加解密的参数组合跑一遍,确认算法层面没问题了再去调业务逻辑。这个习惯帮我省下了大量排查时间,也避免了在联调群里反复解释“我的签名没问题”的尴尬。希望帮到你。
本文还有配套的精品资源,点击获取