SM2加密传输解决方案
后端
引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.71</version>
</dependency>
密钥对生成
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.SM2;
import cn.rs.crypto.core.KeyPairInfo;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.SecureRandom;
/**
* 国密sm2密钥对生成工具
*
* @author 吕晓飞
* @date 2022-08-31 18:07
* @version 1.0
*/
public class Sm2KeyGenUtil {
/**
* 生成SM2算法的密钥对 - hutool
*
* @return 密钥对
*/
public static KeyPairInfo genSM2KeyUseHutool() {
// sm2密钥生成
KeyPair pair = SecureUtil.generateKeyPair("SM2");
SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic());
String privatekey = Hex.toHexString(sm2.getD());
System.out.println("privatekey-->" + privatekey);
String publickey = Hex.toHexString(sm2.getQ(false));
System.out.println("publickey-->" + publickey);
return new KeyPairInfo(publickey, privatekey);
}
/**
* 生成SM2算法的密钥对 -- 自行实现
*
* @return 密钥对
*/
public static KeyPairInfo genSM2Key() {
try {
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");// SM2默认曲线
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
// 上面的代码都是直接用maven依赖中的包直接import就可以用了
// 还有一些更底层的写法,可以自己搜索一下,图方便的这个挺好的
// 私钥,16进制格式,自己保存
BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String privateKeyHex = privatekey.toString(16);
System.out.println("private Key :" + privateKeyHex);
// 公钥,16进制格式,Q值发给前端
ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
System.out.println("Public Key :" + publicKeyHex);
return new KeyPairInfo(publicKeyHex, privateKeyHex);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
import lombok.*;
/**
* 密钥对
*
* @author 吕晓飞
* @version 1.0
* @since 2022-08-31 18:04
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class KeyPairInfo {
/**
* 公钥, 用来加密
*/
private String publicKey;
/**
* 私钥, 用来解密
*/
private String privateKey;
}
新建两个txt文件, 改名为public_key.pem和private_key.pem将生成的公钥和私钥粘贴到文件里保存
将这两个文件放到maven工程的src/main/resources/sm2/目录下
import org.bouncycastle.crypto.engines.SM2Engine;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
/**
* 国密sm2算法<br>
* 最初无法解密前端传递的数据 详见https://gitee.com/dromara/hutool/issues/I3AEPJ <br>
* 需要前端加密的数据是否是04开头, 如果不是需要手动加上04<br>
* 参考 https://blog.csdn.net/ww_1997/article/details/115379258
*
* @author 吕晓飞
* @date 2022-08-18 15:50
* @version 1.0
*/
public class SM2Util {
/**
* 公钥
*/
private static String puclicKey = null;
/**
* 私钥
*/
private static String privateKey = null;
/**
* 国密sm2算法
*/
private static SM2 sm2 = null;
static {
puclicKey = ResourceUtil.readUtf8Str("sm2/public_key.pem");
privateKey = ResourceUtil.readUtf8Str("sm2/private_key.pem");
sm2 = SmUtil.sm2(privateKey, puclicKey);
sm2.usePlainEncoding();
// 加密方式
// https://blog.csdn.net/weixin_44724660/article/details/122319132
// 1 - C1C3C2,0 - C1C2C3,默认为1, 与前端保持一致, 设置为C1C2C3
sm2.setMode(SM2Engine.Mode.C1C2C3);
}
/**
* 加密字符串
*
* @param dataStr 原始数据字符串
* @return java.lang.String 加密后的16进制字符串
*/
public static String encode(String dataStr) {
return sm2.encryptHex(dataStr, KeyType.PublicKey);
}
/**
* 解密字符串
*
* @param encrypedHexStr 加密后的16进制字符串
* @return java.lang.String 解密后的字符串
*/
public static String decode(String encrypedHexStr) {
if (!encrypedHexStr.startsWith("04")) {
encrypedHexStr = "04" + encrypedHexStr;
}
return sm2.decryptStr(encrypedHexStr, KeyType.PrivateKey);
}
}
前端
引入依赖
npm install --save sm-crypto
const sm2 = require('sm-crypto').sm2;
// 公钥 - 用来加密 - 由后端生成发送给前端
let publicKey = "0463462d2611966c08430219d64b2b1166807951bee2f9b41a615f1d20c3e701a5fe2c239454b7646161b6b3e14ee0c8b36d44f6fa4727e8a0b04180ebf3c6a127";
// 私钥 - 用来解密 - 由后端生成发送给前端
let privateKey = "1776f049d4bddea7829a8dcade066d96798cf720fe142e5a1b3b9f7263e8fe16";
// 验证公钥
let verifyResult = sm2.verifyPublicKey(publicKey);
console.log(verifyResult);
// 1-C1C3C2 / 0-C1C2C3,默认为1, 与后端保持一致, 这里我们设为0 -C1C2C3
const cipherMode = 0;
// 要加密的字符串
let str = "admin";
// 加密
let encryptData = sm2.doEncrypt(str, publicKey, cipherMode);
console.log(encryptData);
// 解密
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode);
console.log(decryptData);
前端一般只需要加密, 所以只需要配置下公钥就行, 私钥出于安全考虑可以不作声明
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!