Java SM3 使用手册
Java SM3 使用手册
使用 SM3Util 完成一次性摘要和 HMAC。摘要是公开内容指纹;HMAC 使用共享 key 认证消息。需要非对称身份认证时使用 SM2 签名。
摘要与 HMAC
package cn.gmkit;
import cn.gmkit.core.Bytes;
import cn.gmkit.core.GmkitException;
import cn.gmkit.core.Texts;
import cn.gmkit.sm3.SM3Util;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ManualJavaSm3Test {
@Test
void digestsAndAuthenticatesExplicitBytes() {
// 1. 准备参数:固定向量使用 abc,业务消息使用正常金额与篡改金额。
String plaintext = "order=GMKIT-DEMO-0001&amount=88.00";
String tampered = "order=GMKIT-DEMO-0001&amount=99.00";
byte[] hmacKey = Texts.bytes("merchant-demo-key", StandardCharsets.UTF_8);
// 2. 计算 SM3 摘要:固定向量必须等于 64 个小写 Hex 字符。
assertEquals(
"66c7f0f462eeedd9d1f2d46bdc10e4e2"
+ "4167c4875cf2f7a2297da02b8f4ba8e0",
SM3Util.digestHex("abc", StandardCharsets.UTF_8));
// 3. 比对文本与字节:显式 UTF-8 的 String 重载必须等于 byte[] 重载。
byte[] textDigest = SM3Util.digest(plaintext, StandardCharsets.UTF_8);
byte[] byteDigest = SM3Util.digest(Texts.bytes(plaintext, StandardCharsets.UTF_8));
assertArrayEquals(textDigest, byteDigest);
// 4. 计算 HMAC-SM3:共享 key 认证正常业务消息。
byte[] mac = SM3Util.hmac(hmacKey, plaintext, StandardCharsets.UTF_8);
// 5. HMAC 成功断言:接收端重新计算后使用常量时间字节比较。
assertTrue(Bytes.constantTimeEquals(
mac,
SM3Util.hmac(hmacKey, plaintext, StandardCharsets.UTF_8)));
// 6. 篡改断言:金额变化后 HMAC-SM3 必须不同。
assertFalse(Bytes.constantTimeEquals(
mac,
SM3Util.hmac(hmacKey, tampered, StandardCharsets.UTF_8)));
// 7. 非法输入断言:null 消息不能被当作空消息计算摘要。
assertThrows(GmkitException.class, () -> SM3Util.digest((byte[]) null));
}
}重载选择
协议给出的 HMAC key 若是 Hex,先用 HexCodec.decodeStrict 得到字节。把 Hex 文本直接 getBytes(UTF_8) 会使用字符 0、1 的编码,结果与协议 key 不同。
验证 HMAC
接收端使用相同 key、相同消息字节和相同算法重新计算 HMAC,再调用 Bytes.constantTimeEquals 比较 byte[]。长度不同或内容不同返回 false。
HMAC 校验失败后拒绝消息。不要把普通 SM3 摘要当作 HMAC,也不要把 HMAC 当作密码存储算法。
状态与错误
Java 的 SM3 和 SM3Util 都是一次性无状态入口,没有 TypeScript 增量类的 update/reset 语义。大流的分块摘要需要调用 BC 流式接口或在应用层选择已有流式封装,本版本不要伪造不存在的 GMKit 增量 API。
null 输入、非法编码和底层摘要初始化问题抛 GmkitException。空 byte[] 是合法消息,可得到 SM3 空消息摘要。
全部 String、byte[]、Charset、Hex 和 Base64 重载见 Java SM3 API。