SM2 签名与验签前后端对接指南
本文档旨在指导如何使用 Hutool(Java)和 sm-crypto(TypeScript)库,实现基于 SM2 算法的签名与验签功能。确保前后端在签名与验证过程中,参数传递和密钥格式一致,避免因格式不匹配导致的验证失败。
目录
前提条件
- 后端:Java 环境,推荐使用 Hutool 库。本文版本
5.8.35
- 前端:TypeScript 或者 JavaScript环境,使用 sm-crypto 库。本文版本·
^0.3.13
- 熟悉 SM2 算法的基本原理。
密钥格式说明
私钥[要是位数不对,检查下什么语言生成的,java的没有无符号,所以可能要加前导0]
- 格式:16 进制字符串,长度为 64 字符
- 示例:
4BD9A450D7E68A5D7E08EB7A0BFA468FD3EB32B71126246E66249A73A9E4D44A
公钥[可根据自己项目来,但是你要不理解密钥封装格式,就按这个来]
- 格式:
- 非压缩格式:以
04
开头,后跟 X 和 Y 坐标,每个坐标 64 字符(总长度 130 字符)。 - 压缩格式(可选):以
02
或03
开头,后跟 X 坐标(64 字符),新版规范的明确了02
和03
。
- 非压缩格式:以
- 示例(非压缩):
04970AB36C3B870FBC04041087DB1BC36FB4C6E125B5EA406DB0EC3E2F80F0A55D8AFF28357A0BB215ADC2928BE76F1AFF869BF4C0A3852A78F3B827812C650AD3
后端实现(Java + Hutool)
依赖引入
确保在项目中引入 Hutool 库。如果使用 Maven,可以在 pom.xml
中添加:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.35</version> <!-- 请根据实际情况选择版本 -->
</dependency>
SM2 签名与验签代码示例
以下是使用 Hutool 实现 SM2 签名与验签的完整代码示例:
package org.dromara.hutool.crypto.asymmetric;
import org.dromara.hutool.core.codec.binary.HexUtil;
public class SM2Example {
public void sm2Verify1Test(){
// 私钥(16 进制,64 字符)
final String privateKey = "4BD9A450D7E68A5D7E08EB7A0BFA468FD3EB32B71126246E66249A73A9E4D44A";
// 公钥(非压缩格式,130 字符,以04开头)
final String publicKey = "04970AB36C3B870FBC04041087DB1BC36FB4C6E125B5EA406DB0EC3E2F80F0A55D8AFF28357A0BB215ADC2928BE76F1AFF869BF4C0A3852A78F3B827812C650AD3";
// 待签名数据
final String data = "旧信";
// 初始化 SM2 实例
final SM2 sm2 = new SM2(privateKey, publicKey);
// 签名(可选)
// final String signHex = sm2.signHex(data.getBytes(), "1234567812345678".getBytes());
// System.out.println("Hutool签名的hex:" + signHex);
// 示例签名(TypeScript 生成的签名)
final String tsSignHex = "3046022100970a8f60bdb6e3c6248e4da9f99acbddf32451b658be846fad5680e1f2fecccb02210081725f32b56afd60aac928c7462df5a0b384b756df1bb6ae8994b9b6f201cd38";
// 验签
final boolean verify1 = sm2.verify(data.getBytes(), HexUtil.decode(tsSignHex));
System.out.println("验签结果: " + verify1);
}
public static void main(String[] args) {
SM2Example example = new SM2Example();
example.sm2Verify1Test();
}
}
说明:
- 私钥与公钥:需确保私钥为 64 字符的 16 进制字符串,公钥为以
04
开头的 130 字符非压缩格式。 - 签名:可以使用 Hutool 生成签名,也可以接受前端生成的签名(如示例中的
tsSignHex
)。 - 验签:通过
sm2.verify
方法,传入原始数据和签名,返回验签结果。
前端实现(TypeScript + sm-crypto)
安装 sm-crypto
使用 npm 或 yarn 安装 sm-crypto
:
npm install sm-crypto
或
yarn add sm-crypto
SM2 签名与验签代码示例
以下是使用 sm-crypto 实现 SM2 签名与验签的完整代码示例:
import { sm2 } from 'sm-crypto';
/**
* 使用 SM2 算法进行签名和验签
* @param data - 待签名的数据
* @param privateKey - 用于签名的私钥(16 进制字符串,64 字符)
* @param publicKey - 用于验证签名的公钥(非压缩格式,130 字符)
* @returns {string} 返回签名结果以及验签结果
*/
const sm2Test = (data: string, privateKey: string, publicKey: string): string => {
// 使用私钥对数据进行签名
const sign: string = sm2.doSignature(data, privateKey, {
der: true,
hash: true,
userId: '1234567812345678'
});
// 使用公钥验证签名
const isValid: boolean = sm2.doVerifySignature(data, sign, publicKey, {
der: true,
hash: true,
userId: '1234567812345678'
});
// 返回签名及验证结果
return `TS生成的签名: ${sign}\n签名验证结果: ${isValid}`;
};
// 测试数据
const privateKey = "4BD9A450D7E68A5D7E08EB7A0BFA468FD3EB32B71126246E66249A73A9E4D44A";
const publicKey = "04970AB36C3B870FBC04041087DB1BC36FB4C6E125B5EA406DB0EC3E2F80F0A55D8AFF28357A0BB215ADC2928BE76F1AFF869BF4C0A3852A78F3B827812C650AD3";
const data = '旧信';
// 执行测试
console.log(sm2Test(data, privateKey, publicKey));
说明:
- 私钥与公钥:确保私钥为 64 字符的 16 进制字符串,公钥为以
04
开头的 130 字符非压缩格式。 - 签名:
der: true
:生成的签名使用 DER 编码。hash: true
:对数据进行哈希处理(默认使用 SM3)。userId
:用户标识,需与后端保持一致。
- 验签:使用
sm2.doVerifySignature
方法,传入原始数据、签名和公钥,返回验签结果。
完整示例与测试
1. 前端生成签名并发送至后端
假设前端使用上述 TypeScript 代码生成签名,并通过 API 将数据和签名发送至后端:
// 生成签名和验证结果
const result = sm2Test(data, privateKey, publicKey);
console.log(result);
// 示例输出
// TS生成的签名: 3046022100970a8f60bdb6e3c6248e4da9f99acbddf32451b658be846fad5680e1f2fecccb02210081725f32b56afd60aac928c7462df5a0b384b756df1bb6ae8994b9b6f201cd38
// 签名验证结果: true
// 将 data 和 sign 发送至后端
2. 后端接收数据并进行验签
后端接收 data
和 sign
后,使用 Hutool 进行验签:
public void sm2VerifyFromFrontEnd(String data, String signHex){
final String privateKey = "4BD9A450D7E68A5D7E08EB7A0BFA468FD3EB32B71126246E66249A73A9E4D44A";
final String publicKey = "04970AB36C3B870FBC04041087DB1BC36FB4C6E125B5EA406DB0EC3E2F80F0A55D8AFF28357A0BB215ADC2928BE76F1AFF869BF4C0A3852A78F3B827812C650AD3";
final SM2 sm2 = new SM2(privateKey, publicKey);
final boolean isValid = sm2.verify(data.getBytes(), HexUtil.decodeHex(signHex));
System.out.println("前端签名验证结果: " + isValid);
}
调用示例:
public static void main(String[] args) {
SM2Example example = new SM2Example();
String data = "lin";
String signHex = "d2f1bbdfcc77e6051caa1729dfd13ed35617ecf92f2d8eca82c2c9fe3c3bd59a1643de0e7ff201acae1f6067f4cbe9fb1467003ebf948994f63d7748c9ec5127";
example.sm2VerifyFromFrontEnd(data, signHex);
}
预期输出:
前端签名验证结果: true
注意事项
- 密钥格式一致性:确保前后端使用的私钥、公钥格式一致,避免因格式不匹配导致验签失败。[格式真的很多,但是私钥就是一个整数,不同格式就是对它的编码而已]
- 用户标识(User ID):SM2 签名过程中,用户标识需要保持前后端一致。本示例中使用
1234567812345678
[hutool默认是这个,sm-crypto默认应该也是,我记得规范写了这个值],但是GmSSL
库中默认ID字符串为16个字节的ASCII字符串anonym@gmssl.org
,不包含末尾的0。 - 签名编码:
- 前端生成的签名需与后端解析方式一致。本示例中,前端使用
der: false
生成r||s
格式的签名,后端需使用相应方式进行验签。
- 前端生成的签名需与后端解析方式一致。本示例中,前端使用
- 字符编码:确保前后端对待签名数据(如
data
)的字符编码一致,推荐使用UTF-8
。 - 安全性:妥善保管私钥,避免泄露。传输过程中建议使用 HTTPS 等安全协议。
通过以上步骤,您可以实现前后端基于 SM2 算法的签名与验签功能,确保数据传输的完整性和真实性。如在实际应用中遇到问题,建议参考相关文档或社区资源寻求帮助。
标签:与验,示例,SM2,Hutool,签名,sm,data,crypto From: https://www.cnblogs.com/lylhqy/p/18648417