开放接口签名怎么算?这个Java工具类14个方法兼容微信支付宝

简介: 这是一个专为支付与开放平台设计的Java签名工具类(JDK17+),提供14个方法:支持参数自动字典序拼接、多算法摘要/HMAC签名(MD5/SHA256/HmacSHA256等)、大小写灵活输出、常量时间安全验签,并兼容微信、支付宝等老协议,开箱即用。

📋 目录

一、工具类功能

参数表按 key 字典序拼成签名原文,再走摘要式或 HMAC-SHA256 出签名,回调验签、请求签名、兼容微信支付宝老协议都能用,拿回来是十六进制签名串与验签结论。

二、所需环境

JDK 17或以上

三、方法与调用示例(按需调用)

🔢 方法序号:1

🎯 方法用途:

  • 把参数表按 key 字典序拼成签名原文,sign、sign_type 与空值参数一律不进原文
  • 对接支付网关、开放平台回调时先拿它出原文,再交给具体算法

🧩 方法定义:public static String buildSignSource(Map<String, ?> params)

🗣️ 入参解释:public static String buildSignSource(参数表)

📤 返回值解释:String,形如 k1=v1&k2=v2 的签名原文

💡 边界口径:

  • 排序按 key 的字典序走,与参数表原本的插入顺序无关
  • 值为 null 与空串的参数被跳过,各家平台在这点上是一致的
  • 集合与数组按出现顺序展开成同名多项,tag=b&tag=a 这种写法
  • 原文不做 URL 编码,这是它与 UrlCodecUtils.encodeQuery 的本质区别,拿编码后的串去签名一定对不上
  • 原样拼接的边界有歧义:值里含 & 或 = 时能拼出同一份原文,参与签名的字段不能来自不可信输入的自由拼接
  • 参数表传 null 抛 IllegalArgumentException,key 为 null 的条目被直接跳过

🧪 方法示例:

  • buildSignSource(params) 拿到 appid=wx08804b9c9f8f8e3a&body=测试商品-上海&nonce_str=593EF8934B5A94F0C2&out_trade_no=ORD20260925001&total_fee=1
  • buildSignSource(multiValue) 拿到 amount=12800&tag=b&tag=a,集合按出现顺序展开
  • buildSignSource(Map.of("a", "1&b=2")) 拿到 a=1&b=2,与两个参数拼出的原文完全一样
  • buildSignSource(null) 抛 参数Map不能为null

🔢 方法序号:2

🎯 方法用途:

  • 在默认剔除清单之上再加本平台特有的字段,比如某些平台要把 sign_key、attach 摘掉
  • 各家协议要求剔除的字段不同,用这一版就不用自己先复制一份 Map 再 remove

🧩 方法定义:public static String buildSignSource(Map<String, ?> params, Set<String> excludeKeys)

🗣️ 入参解释:public static String buildSignSource(参数表, 额外剔除字段)

📤 返回值解释:String,剔除后按字典序拼出的签名原文

💡 边界口径:

  • 自定义清单是追加在 sign、sign_type 之上的,没法靠它把默认剔除关掉
  • excludeKeys 传 null 时只按默认清单剔,不报错
  • 其余排序、空值跳过、不做 URL 编码的口径与单参版一致

🧪 方法示例:

  • buildSignSource(withExtra, Set.of("sign_key")) 拿到 appid=wx08804b9c9f8f8e3a&body=测试商品-上海&nonce_str=593EF8934B5A94F0C2&out_trade_no=ORD20260925001&total_fee=1,与默认清单剔完的结果一模一样
  • buildSignSource(params, null) 拿到 body=测试商品-上海&out_trade_no=ORD20260925001&total_fee=1
  • buildSignSource(Map.of("body", "x", "partner", "y"), Set.of("partner")) 拿到 body=x

🔢 方法序号:3

🎯 方法用途:

  • 对现成的签名原文做摘要式签名,原文末尾拼上 &key=密钥 再整体取摘要,输出小写 Hex
  • 微信 JS-SDK 这类要自己拼原文(jsapi_ticket=...&noncestr=...&url=...)的协议只能走这一层

🧩 方法定义:public static String signSourceByDigest(String signSource, String secret, String digestAlgorithm)

🗣️ 入参解释:public static String signSourceByDigest(签名原文, 密钥, 摘要算法)

📤 返回值解释:String,小写十六进制的签名值

💡 边界口径:

  • 算法取 ALG_MD5、ALG_SHA256 这类常量,白名单外的名字抛 IllegalArgumentException 并列出支持范围
  • 密钥拼在原文末尾的 &key= 之后,这是历史协议的固定写法
  • 摘要式为兼容老协议保留,新接入用 HMAC,涉及资金的验签用非对称算法
  • 原文为 null、密钥为 null 或空白都抛 IllegalArgumentException,空原文照样能算

🧪 方法示例:

  • signSourceByDigest(source, SECRET, ApiSignUtils.ALG_SHA256) 拿到 0f0d73df94313475f5585dcb66f4caabecc465f01605630afbb02dfa13b18491
  • signSourceByDigest(jsApiSource, SECRET, ApiSignUtils.ALG_SHA256) 拿到 8b9bb3ee34396e862a5bb946a7c63b4cad0a1bcc6839372547df4594b0317ce5
  • signSourceByDigest("", SECRET, ApiSignUtils.ALG_MD5) 拿到 0b4ad1d96492c0442d79efa9125434f4,空原文也能算
  • signSourceByDigest(null, SECRET, ApiSignUtils.ALG_SHA256) 抛 签名原文不能为null
  • signByDigest(params, SECRET, "SM3") 抛 不支持的摘要算法: SM3,仅支持 MD5/SHA-1/SHA-256/SHA-512

🔢 方法序号:4

🎯 方法用途:

  • 摘要式签名并要求输出大写 Hex,部分银行网关的字段就是这个形态
  • 只是大小写不同的展示形式,签名值本身与三参版一致

🧩 方法定义:public static String signSourceByDigest(String signSource, String secret, String digestAlgorithm, boolean uppercase)

🗣️ 入参解释:public static String signSourceByDigest(签名原文, 密钥, 摘要算法, 是否大写)

📤 返回值解释:String,uppercase 为 true 时给大写 Hex

💡 边界口径:

  • 转大写用的是 Locale.ROOT,不受系统语言环境影响
  • 验签侧比的是解码后的字节,发出去是大写、对方按小写验也能过
  • 参数校验与三参版相同

🧪 方法示例:

  • signSourceByDigest(source, SECRET, ApiSignUtils.ALG_SHA256, true) 拿到 0F0D73DF94313475F5585DCB66F4CAABECC465F01605630AFBB02DFA13B18491
  • verifySourceByDigest(source, SECRET, ApiSignUtils.ALG_MD5, 大写签名) 拿到 true

🔢 方法序号:5

🎯 方法用途:

  • 对签名原文做 HMAC 签名,输出小写 Hex,新接入首选 HmacSHA256
  • 密钥不进原文、而是当 HMAC 的 key 用,比把密钥拼在明文尾巴上稳

🧩 方法定义:public static String signSourceByHmac(String signSource, String key, String hmacAlgorithm)

🗣️ 入参解释:public static String signSourceByHmac(签名原文, 密钥, HMAC算法)

📤 返回值解释:String,小写十六进制的 HMAC 签名值

💡 边界口径:

  • HMAC 算法白名单是 HmacMD5、HmacSHA1、HmacSHA256、HmacSHA512,写别的直接抛异常
  • 原文为 null、密钥为 null 或空白都抛 IllegalArgumentException
  • 原文这层不做任何拼接与排序,传进来什么就签什么

🧪 方法示例:

  • signSourceByHmac(source, SECRET, "HmacSHA256") 拿到 9aa4610b16258cd2970cdaa371c699a93ad8916889a527c89cd456c7600bf27c
  • signSourceByHmac(source, " ", "HmacSHA256") 抛 密钥不能为null或空白
  • signSourceByHmac(source, SECRET, "HmacSM3") 抛 不支持的HMAC算法: HmacSM3,仅支持 HmacMD5/HmacSHA1/HmacSHA256/HmacSHA512

🔢 方法序号:6

🎯 方法用途:

  • HMAC 签名并控制输出大小写,网关要求大写 Hex 时用
  • 与三参版只差展示形式,签名值一致

🧩 方法定义:public static String signSourceByHmac(String signSource, String key, String hmacAlgorithm, boolean uppercase)

🗣️ 入参解释:public static String signSourceByHmac(签名原文, 密钥, HMAC算法, 是否大写)

📤 返回值解释:String,uppercase 为 true 时给大写 Hex

💡 边界口径:

  • 大写只影响字符形态,验签比的是字节
  • 参数校验与三参版相同

🧪 方法示例:

  • signSourceByHmac(source, SECRET, "HmacSHA256", true) 拿到 9AA4610B16258CD2970CDAA371C699A93AD8916889A527C89CD456C7600BF27C

🔢 方法序号:7

🎯 方法用途:

  • 参数表一步出摘要式签名,内部就是拼原文再做摘要
  • 兼容微信公众号、支付宝老接口这类 MD5(原文&key=密钥) 的历史协议

🧩 方法定义:public static String signByDigest(Map<String, ?> params, String secret, String digestAlgorithm)

🗣️ 入参解释:public static String signByDigest(参数表, 密钥, 摘要算法)

📤 返回值解释:String,小写十六进制的签名值

💡 边界口径:

  • 原文口径与 buildSignSource(params) 完全一致,sign 与空值不参与
  • 回调验签时对方带回来的 sign 会被自动剔掉,不会把自己算进去
  • MD5 只用于兼容,新接入别用

🧪 方法示例:

  • signByDigest(params, SECRET, ApiSignUtils.ALG_MD5) 拿到 d7922d5ef9ab694bc6b87abb5d6c5f86
  • signByDigest(params, SECRET, ApiSignUtils.ALG_SHA256) 拿到 0f0d73df94313475f5585dcb66f4caabecc465f01605630afbb02dfa13b18491,与先拼原文再签同值

🔢 方法序号:8

🎯 方法用途:

  • 参数表一步出摘要式签名并指定大写 Hex
  • 老协议里要求签名字段大写的网关用这一版

🧩 方法定义:public static String signByDigest(Map<String, ?> params, String secret, String digestAlgorithm, boolean uppercase)

🗣️ 入参解释:public static String signByDigest(参数表, 密钥, 摘要算法, 是否大写)

📤 返回值解释:String,指定大小写形态的签名值

💡 边界口径:

  • 只是把三参版的结果整体转大写,原文与算法口径不变
  • 参数表为 null 同样抛 IllegalArgumentException

🧪 方法示例:

  • signByDigest(params, SECRET, ApiSignUtils.ALG_MD5, true) 拿到 D7922D5EF9AB694BC6B87ABB5D6C5F86

🔢 方法序号:9

🎯 方法用途:

  • 参数表一步出 HMAC 签名,新接入的首选写法
  • 不想自己先调一次拼原文时,用它把两步并成一步

🧩 方法定义:public static String signByHmac(Map<String, ?> params, String key, String hmacAlgorithm)

🗣️ 入参解释:public static String signByHmac(参数表, 密钥, HMAC算法)

📤 返回值解释:String,小写十六进制的签名值

💡 边界口径:

  • 与先 buildSignSource 再 signSourceByHmac 结果同值,两条路径可以混用
  • 算法白名单、密钥与原文的校验口径与原文级入口一致

🧪 方法示例:

  • signByHmac(params, SECRET, "HmacSHA256") 拿到 9aa4610b16258cd2970cdaa371c699a93ad8916889a527c89cd456c7600bf27c,与原文级入口同值

🔢 方法序号:10

🎯 方法用途:

  • 参数表一步出 HMAC 签名并指定大写输出
  • 对端字段要求大写时用它,其余口径与三参版相同

🧩 方法定义:public static String signByHmac(Map<String, ?> params, String key, String hmacAlgorithm, boolean uppercase)

🗣️ 入参解释:public static String signByHmac(参数表, 密钥, HMAC算法, 是否大写)

📤 返回值解释:String,指定大小写形态的签名值

💡 边界口径:

  • 大写只改展示形式,验签比的是字节
  • 需要去掉 =、走 URL 安全字母形的 Base64 签名不属于本类范围

🧪 方法示例:

  • signByHmac(params, SECRET, "HmacSHA256", true) 拿到 9AA4610B16258CD2970CDAA371C699A93AD8916889A527C89CD456C7600BF27C

🔢 方法序号:11

🎯 方法用途:

  • 校验摘要式签名,走常量时间比较,避免按字节早退带来的计时侧信道
  • 已经自己拼好原文(比如微信 JS-SDK)就验原文这一层

🧩 方法定义:public static boolean verifySourceByDigest(String signSource, String secret, String digestAlgorithm, String expectedSign)

🗣️ 入参解释:public static boolean verifySourceByDigest(签名原文, 密钥, 摘要算法, 待验签名)

📤 返回值解释:boolean,签名与原文匹配返回 true

💡 边界口径:

  • 比的是解码后的字节,待验签名的大小写、首尾空白都不影响结果
  • 签名值缺失、长度不对、含非十六进制字符一律判 false,不抛异常
  • 会先算一次签名,所以原文、密钥、算法这类调用方错误照样抛 IllegalArgumentException
  • 只保证签名与原文一致,防重放还得自己配 nonce 与时间戳校验

🧪 方法示例:

  • verifySourceByDigest(source, SECRET, ApiSignUtils.ALG_MD5, legacyMd5) 拿到 true
  • 待验签名用大写形态传,同样拿到 true
  • verifySourceByDigest(source, SECRET, "SM3", "anyhex") 抛 不支持的摘要算法: SM3,仅支持 MD5/SHA-1/SHA-256/SHA-512

🔢 方法序号:12

🎯 方法用途:

  • 校验 HMAC 签名,同样走常量时间比较
  • 对端把签名转成大写、或者串里带了空白时不会因为格式判错

🧩 方法定义:public static boolean verifySourceByHmac(String signSource, String key, String hmacAlgorithm, String expectedSign)

🗣️ 入参解释:public static boolean verifySourceByHmac(签名原文, 密钥, HMAC算法, 待验签名)

📤 返回值解释:boolean,签名与原文匹配返回 true

💡 边界口径:

  • 待验签名先按十六进制解析,解不出就判 false
  • 大小写、首尾空白不参与比较
  • 算法名非法、密钥空白这类调用方错误仍抛 IllegalArgumentException

🧪 方法示例:

  • verifySourceByHmac(source, SECRET, "HmacSHA256", hmacHex.toUpperCase()) 拿到 true
  • 把签名改成小写再套上首尾空白,verifyByHmac(params, SECRET, "HmacSHA256", " 大写串 ") 仍拿到 true

🔢 方法序号:13

🎯 方法用途:

  • 对整张回调参数表验摘要式签名,一步完成拼原文与比较
  • 支付回调通知的验签入口,sign 字段本身会被自动剔掉

🧩 方法定义:public static boolean verifyByDigest(Map<String, ?> params, String secret, String digestAlgorithm, String expectedSign)

🗣️ 入参解释:public static boolean verifyByDigest(参数表, 密钥, 摘要算法, 待验签名)

📤 返回值解释:boolean,参数表与签名匹配返回 true

💡 边界口径:

  • 等于 buildSignSource(params) 再走原文级验签,剔除与排序口径完全一致
  • 忘了剔 sign 这类字段导致验不过的情况在这一版不会发生
  • 签名值非法只判 false,参数表为 null 抛异常

🧪 方法示例:

  • verifyByDigest(params, SECRET, ApiSignUtils.ALG_MD5, legacyMd5) 拿到 true

🔢 方法序号:14

🎯 方法用途:

  • 对整张参数表验 HMAC 签名,公网接口收到请求时的第一道校验
  • 金额、订单号被改一分都过不了,签名值本身残缺也不会把异常抛给调用方

🧩 方法定义:public static boolean verifyByHmac(Map<String, ?> params, String key, String hmacAlgorithm, String expectedSign)

🗣️ 入参解释:public static boolean verifyByHmac(参数表, 密钥, HMAC算法, 待验签名)

📤 返回值解释:boolean,参数表与签名匹配返回 true

💡 边界口径:

  • 参数字段被改、被删、多出未签名字段都会判 false
  • 待验签名为 null、空白、长度为奇数、含非十六进制字符都判 false 而不抛异常,公网收到的就是这种输入
  • 值为 null 与空串的参数本来就不进原文,这类字段改了不影响验签结果
  • 验过签名不等于请求没被重放,nonce 与时间戳要自己校验

🧪 方法示例:

  • verifyByHmac(tampered, SECRET, "HmacSHA256", hmacHex) 拿到 false,total_fee 被改了一分钱
  • 参数表原样不动时拿到 true
  • 待验签名传 null 拿到 false,不抛异常
  • 待验签名截掉最后一位(长度变奇数)拿到 false
  • 待验签名塞进非十六进制字符拿到 false
  • 待验签名传空白串拿到 false

四、工具类源码(可直接复制)

📦 源码文件

相关文章
|
云安全 安全 网络安全
|
5天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
1951 1
|
12天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
1965 39
|
9天前
|
Web App开发 人工智能 前端开发
2026 年 AI 浏览器怎么选?Ego Lite、Tabbit、Chrome DevTools MCP 深度对比,开发者到底该用哪个?
2026年AI浏览器选型指南:Ego Lite专注多Agent协同(Mac优先),Tabbit国产首选,原生支持Windows与Codex控制;Chrome DevTools MCP非浏览器而是开发者利器,深度集成Console/Network/Performance调试能力,让AI真正“看懂并验证”前端代码。
1299 3
|
6天前
|
安全 应用服务中间件 Go
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
|
13天前
|
JSON 程序员 数据格式
最新版Notepad++下载、安装、配置保姆级教程(附官网安装包)
Notepad++ 是一款免费开源、轻量快速的 Windows 文本编辑器,支持多标签、语法高亮、代码折叠与宏录制,体积小、启动快、资源占用低,兼容 x64/x32/ARM 架构,是程序员和文字工作者的高效利器。
2147 0
|
13天前
|
安全 Linux 芯片
【万字长文】Ollama下载、安装、配置、使用保姆级教程(2026最新图解)
Ollama 是一款免费开源的本地大模型运行工具,支持 DeepSeek、Qwen 等主流模型,一条命令即可下载运行。2026 最新版 v0.35.1 官方安装包(1.47GB)已提供国内直连下载,纯本地、离线可用、无广告、无隐私泄露风险。
1195 4
|
17天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
4天前
|
安全 程序员 应用服务中间件
PHP 被曝两大致命漏洞,请所有程序员抓紧做这件事!
PHP 官方发布了紧急安全更新,一口气修复了两个严重漏洞。如果网站和服务器用了 PHP 应该怎么处理?漏洞的原理是什么?本文给你讲清楚
577 0