本站勉强运行 1957 天 02 小时 10 分 12 秒

SM2 签名与验签前后端对接指南(基于 Hutool 和 sm-crypto)

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 字符)。
    • 压缩格式(可选):以 0203 开头,后跟 X 坐标(64 字符),新版规范的明确了0203
  • 示例(非压缩)
    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. 后端接收数据并进行验签

后端接收 datasign 后,使用 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

注意事项

  1. 密钥格式一致性:确保前后端使用的私钥、公钥格式一致,避免因格式不匹配导致验签失败。[格式真的很多,但是私钥就是一个整数,不同格式就是对它的编码而已]
  2. 用户标识(User ID):SM2 签名过程中,用户标识需要保持前后端一致。本示例中使用 1234567812345678[hutool默认是这个,sm-crypto默认应该也是,我记得规范写了这个值],但是GmSSL库中默认ID字符串为16个字节的ASCII字符串anonym@gmssl.org,不包含末尾的0。
  3. 签名编码
    • 前端生成的签名需与后端解析方式一致。本示例中,前端使用 der: false 生成 r||s 格式的签名,后端需使用相应方式进行验签。
  4. 字符编码:确保前后端对待签名数据(如 data)的字符编码一致,推荐使用 UTF-8
  5. 安全性:妥善保管私钥,避免泄露。传输过程中建议使用 HTTPS 等安全协议。

通过以上步骤,您可以实现前后端基于 SM2 算法的签名与验签功能,确保数据传输的完整性和真实性。如在实际应用中遇到问题,建议参考相关文档或社区资源寻求帮助。

posted @   旧信  阅读(619)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示