📋 目录
一、工具类功能
给接口签名、Webhook 验签、令牌防篡改算 HMAC 并常量时间比对,支持 HmacMD5 到 HmacSHA512 四种算法,输出十六进制、Base64 或原始字节,新接入一律选 HmacSHA256。
二、所需环境
JDK 17或以上
三、方法与调用示例(按需调用)
🔢 方法序号:1
🎯 方法用途:
- 算 HmacMD5 并给小写十六进制串,输出固定 32 位
- 只为还在用 HmacMD5 的遗留系统保留,新接入别选它
🧩 方法定义:public static String hmacMd5(String data, String key)
🗣️ 入参解释:public static String hmacMd5(正文, 密钥)
📤 返回值解释:String,32 位小写十六进制的 MAC
💡 边界口径:
- 正文与密钥都按 UTF-8 取字节,双方必须约定同一套编码
- 空正文是合法输入,只有 null 被拒绝
- 正文为 null 抛
IllegalArgumentException,密钥为 null 或空白同样抛 - MD5 已经不该再用于新签名方案,能用 SHA-256 就用 SHA-256
🧪 方法示例:
hmacMd5(BODY, SECRET)拿到d6dcbdcd2d45983d049e4e7003fc3b95- 正文传 null 抛
待认证数据不能为null - 密钥给空白串抛
密钥不能为null或空白
🔢 方法序号:2
🎯 方法用途:
- 算 HmacSHA1 并给小写十六进制串,输出 40 位
- 部分开放平台的历史签名要求是它,新接入不选
🧩 方法定义:public static String hmacSha1(String data, String key)
🗣️ 入参解释:public static String hmacSha1(正文, 密钥)
📤 返回值解释:String,40 位小写十六进制的 MAC
💡 边界口径:
- 与
hmacMd5同一条路径,只是算法换成HmacSHA1 - 参数校验、UTF-8 取字节、空正文合法这些口径完全一致
🧪 方法示例:
hmacSha1(BODY, SECRET)拿到61dfc9cbe98cfafc960917405584cfa91564b3fc,长度 40
🔢 方法序号:3
🎯 方法用途:
- 算 HmacSHA256 并给小写十六进制串,新接入的推荐选择
- 接口签名、Webhook 校验、令牌防篡改都从它起步
🧩 方法定义:public static String hmacSha256(String data, String key)
🗣️ 入参解释:public static String hmacSha256(正文, 密钥)
📤 返回值解释:String,64 位小写十六进制的 MAC
💡 边界口径:
- 等价于
hmacHex(HMAC_SHA256, data, key),输出长度固定 64 位 - 同一输入反复计算结果稳定,可以拿它做缓存键与重放对比
Mac实例非线程安全,本类每次调用新建,可并发使用
🧪 方法示例:
hmacSha256(BODY, SECRET)拿到d237de3947138319e4b6d18eaca7d0e26a86816f95b9dd3e0eb6fa21482ea191hmacSha256("", SECRET)拿到2dadd0e065f61424af1a9e2ab71633c0e7bc6a18b34f06fd1da80ef0ca9e7150,空正文照样出签名- 上一步的空串签名再交给
verifyHex验,拿到true
🔢 方法序号:4
🎯 方法用途:
- 算 HmacSHA512 并给小写十六进制串,输出 128 位
- 对方协议点名要 SHA-512,或想要更长摘要时用
🧩 方法定义:public static String hmacSha512(String data, String key)
🗣️ 入参解释:public static String hmacSha512(正文, 密钥)
📤 返回值解释:String,128 位小写十六进制的 MAC
💡 边界口径:
- 原始字节固定 64 个,编码成十六进制才是 128 位,签名字段要留够宽度
- 口径与其余具名入口一致,只是摘要更长
🧪 方法示例:
hmacSha512(BODY, SECRET)拿到823a8add31a9b58ea2ddf4ea4cdc318925b4aa853739715ff63c873787d43a373f8950506d61b858855a9dbb6050585f05ce01b36772fbf94e3516c2805940c4hmac("HmacSHA512", "payload-with-中文", "secret-key").length拿到64
🔢 方法序号:5
🎯 方法用途:
- 指定算法名算 HMAC 出十六进制,算法名不必逐字对得上 JCA
- 配置项里存的常常是
HMAC-SHA256、hmac_sha256这类产品名,靠它归一命中
🧩 方法定义:public static String hmacHex(String algorithm, String data, String key)
🗣️ 入参解释:public static String hmacHex(算法名, 正文, 密钥)
📤 返回值解释:String,小写十六进制的 MAC
💡 边界口径:
- 归一规则是去首尾空白、转大写、去掉
_与-,四个算法的别名都能命中 - 别名表之外的名字抛
IllegalArgumentException并列出支持范围,不做模糊猜测 - 算法名为 null 或空白同样抛异常
- 结果是稳定值,同一输入换台机器也算得一样
🧪 方法示例:
hmacHex(HmacUtils.HMAC_SHA256, BODY, SECRET)拿到d237de3947138319e4b6d18eaca7d0e26a86816f95b9dd3e0eb6fa21482ea191hmacHex("HMAC-SHA256", BODY, SECRET)也拿到d237de3947138319e4b6d18eaca7d0e26a86816f95b9dd3e0eb6fa21482ea191hmacHex("hmac_sha256", BODY, SECRET)同样拿到d237de3947138319e4b6d18eaca7d0e26a86816f95b9dd3e0eb6fa21482ea191hmacHex("SM4", BODY, SECRET)抛不支持的HMAC算法: SM4,仅支持 HmacMD5/HmacSHA1/HmacSHA256/HmacSHA512hmacHex(" ", BODY, SECRET)抛HMAC算法不能为null或空白
🔢 方法序号:6
🎯 方法用途:
- 指定算法算 HMAC 并输出标准 Base64,含 padding
- 签名要放进 header 或 JSON 字段、嫌十六进制太长时用这一版
🧩 方法定义:public static String hmacBase64(String algorithm, String data, String key)
🗣️ 入参解释:public static String hmacBase64(算法名, 正文, 密钥)
📤 返回值解释:String,标准 Base64 形态的 MAC
💡 边界口径:
- 与
hmacHex是同一份字节的两种编码,长度约为十六进制的四分之三 - 出的是标准字母表;对方给 URL 安全或无 padding 的写法,验签侧仍然认
- 参数校验口径同
hmacHex
🧪 方法示例:
hmacBase64(HmacUtils.HMAC_SHA256, BODY, SECRET)拿到0jfeOUcTgxnkttGOrKfQ4mqGgW+Vud0+Drb6IUguoZE=verifyBase64(HmacUtils.HMAC_SHA512, BODY, SECRET, b64)拿到true,b64 就是hmacBase64(HmacUtils.HMAC_SHA512, BODY, SECRET)的结果
🔢 方法序号:7
🎯 方法用途:
- 字节形态的十六进制出口,密钥含
0x00与高字节也不失真 - 数据是已序列化的报文、文件内容时走这个入口,别先转成字符串
🧩 方法定义:public static String hmacHex(String algorithm, byte[] data, byte[] key)
🗣️ 入参解释:public static String hmacHex(算法名, 字节正文, 字节密钥)
📤 返回值解释:String,小写十六进制的 MAC
💡 边界口径:
- 字节按原样进 MAC 计算,不做任何字符集解释
- 空密钥数组被拒:空密钥的 MAC 没有意义,不该静默算出一个值
- 正文是空数组则合法
🧪 方法示例:
hmacHex("HmacSHA256", raw, rawKey)拿到b759f847ecaa80661dc79a4a11012258261078bced6a4d27148041b96dac3632,rawKey 是含0x00的四个字节hmacHex("HmacSHA256", new byte[]{1}, new byte[0])抛密钥字节不能为null或空数组- 字符串入口
hmacHex("HmacSHA256", "payload-with-中文", "secret-key")与字节入口比对,拿到true
🔢 方法序号:8
🎯 方法用途:
- 字节形态的 Base64 出口,二进制内容要放进文本字段时用
- 密钥本身是字节数组的配置项走这一版
🧩 方法定义:public static String hmacBase64(String algorithm, byte[] data, byte[] key)
🗣️ 入参解释:public static String hmacBase64(算法名, 字节正文, 字节密钥)
📤 返回值解释:String,标准 Base64 形态的 MAC
💡 边界口径:
- 与字节版
hmacHex同一份字节,只是换了编码 - 校验口径与字节版
hmacHex一致
🧪 方法示例:
hmacBase64("HmacSHA256", raw, rawKey)拿到t1n4R+yqgGYdx5pKEQEiWCYQeLztak0nFIBBuW2sNjI=
🔢 方法序号:9
🎯 方法用途:
- 取 MAC 的原始字节,编码方式与后续派生自己决定
- 要把签名再拼进密钥派生、或自己转成别的编码时用
🧩 方法定义:public static byte[] hmac(String algorithm, String data, String key)
🗣️ 入参解释:public static byte[] hmac(算法名, 正文, 密钥)
📤 返回值解释:byte[],MAC 原始字节,SHA-256 是 32 个
💡 边界口径:
- 返回的数组与
hmacHex、hmacBase64是同一份字节 - 数组可写可改,不想被下游改动就自己复制一份
- 字符串入口固定按 UTF-8 取字节
🧪 方法示例:
hmac(HmacUtils.HMAC_SHA256, BODY, SECRET)拿到32 字节hmacHex(HmacUtils.HMAC_SHA256, BODY, SECRET).length() == macBytes.length * 2拿到true,两种出口同源
🔢 方法序号:10
🎯 方法用途:
- 字节形态取 MAC 原始字节,用于原始报文与文件内容
- 二进制协议要比字节签名时先用它拿值
🧩 方法定义:public static byte[] hmac(String algorithm, byte[] data, byte[] key)
🗣️ 入参解释:public static byte[] hmac(算法名, 字节正文, 字节密钥)
📤 返回值解释:byte[],MAC 原始字节
💡 边界口径:
- 与字符串入口取的是同一套 UTF-8 字节,两侧结果一致
- 空密钥数组抛
IllegalArgumentException,空正文数组合法
🧪 方法示例:
Arrays.equals(macBytes, stringPath)拿到true,两个入口算出的是同一份 MAC
🔢 方法序号:11
🎯 方法用途:
- 校验十六进制 MAC,常量时间比较,忽略预期值的大小写与空白
- 对方给的签名可能是大写、可能带分组空格时用它,别直接比字符串
🧩 方法定义:public static boolean verifyHex(String algorithm, String data, String key, String expectedHex)
🗣️ 入参解释:public static boolean verifyHex(算法名, 正文, 密钥, 预期签名)
📤 返回值解释:boolean,比对通过返回 true
💡 边界口径:
- 比的是解码后的字节,
d237…与D237…判成同一个签名 - 预期值里的空白会被剥掉,分组换行不影响判定;正文本身的空白不参与忽略
- 预期值是非法十六进制或长度为奇数时判不通过,返回
false而不是抛异常 - 预期值为 null 或空白属参数问题,抛
IllegalArgumentException
🧪 方法示例:
verifyHex(HmacUtils.HMAC_SHA512, BODY, SECRET, HmacUtils.hmacHex(HmacUtils.HMAC_SHA512, BODY, SECRET).toUpperCase())拿到trueverifyHex(HmacUtils.HMAC_SHA512, BODY + " ", SECRET, HmacUtils.hmacHex(HmacUtils.HMAC_SHA512, BODY, SECRET))拿到false,正文多一个空格就判不过- 预期值截成奇数长度
d237de3时拿到false - 预期值传 null 抛
预期MAC不能为null或空白
🔢 方法序号:12
🎯 方法用途:
- 校验 Base64 MAC,标准与 URL 安全、有无需 padding、折行写法都能识别
- 网关把签名头折过行、前端给的串没补
=时用这一版
🧩 方法定义:public static boolean verifyBase64(String algorithm, String data, String key, String expectedBase64)
🗣️ 入参解释:public static boolean verifyBase64(算法名, 正文, 密钥, 预期签名)
📤 返回值解释:boolean,比对通过返回 true
💡 边界口径:
- 解码依次试标准、URL 安全、补 padding 三种写法,解开再比字节
- 三种写法都解不开就判不通过返回
false,不把网关错误记成系统故障 - 预期值为 null 或空白属参数问题,抛
IllegalArgumentException
🧪 方法示例:
verifyBase64(HmacUtils.HMAC_SHA512, BODY, SECRET, b64)标准形态拿到true- 同一个 MAC 换成 URL 安全且无 padding 的
urlSafeNoPadding仍拿到true - 中间插了回车换行的
folded也拿到true - 预期值是垃圾串时拿到
false
🔢 方法序号:13
🎯 方法用途:
- 直接比对 MAC 原始字节,二进制协议与自定义编码报文用
- 手里已经是
byte[]时省掉再编码成字符串的一步
🧩 方法定义:public static boolean verify(String algorithm, byte[] data, byte[] key, byte[] expectedMac)
🗣️ 入参解释:public static boolean verify(算法名, 字节正文, 字节密钥, 预期字节)
📤 返回值解释:boolean,字节完全一致返回 true
💡 边界口径:
- 用
MessageDigest.isEqual常量时间比较,长度不同直接判不通过 - 预期字节为 null 返回
false而不抛异常,这点与两个字符串版验签入口不同 - 正文与密钥的校验口径同
hmac字节入口
🧪 方法示例:
verify(HmacUtils.HMAC_SHA256, BODY.getBytes(StandardCharsets.UTF_8), SECRET.getBytes(StandardCharsets.UTF_8), macBytes)拿到true- 预期字节传 null 拿到
false,不抛异常
四、工具类源码(可直接复制)
📦 源码文件