后端
引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
密钥对生成
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.SecureRandom;
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 cn.hutool.crypto.SecureUtil;
/**
* 国密sm2密钥对生成工具
*
* @author 吕晓飞
* @date 2022-08-31 18:07
* @version 1.0
*/
public class Sm2KeyGenUtil {
/**
* sm2密钥生成
*/
private static KeyPair pair = SecureUtil.generateKeyPair("SM2");
/**
* 生成SM2算法的密钥对
*
* @return
*/
public static RSAKey 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 RSAKey(publicKeyHex, privateKeyHex);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 生成SM2算法的密钥对 - hutool生成的密钥对前端无法识别, 使用网上提供的代码生成
*
* @return
*/
@Deprecated
public static RSAKey genSM2Key_bak() {
String publickey = Hex.toHexString(pair.getPublic().getEncoded());
String privatekey = Hex.toHexString(pair.getPrivate().getEncoded());
System.out.println("publickey-->" + publickey);
System.out.println("privatekey-->" + privatekey);
return new RSAKey(publickey, privatekey);
}
}
package com.geoway.privilege.utils.crypto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 非对称算法密钥
*
* @author 吕晓飞
* @date 2022-08-31 17:32
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RSAKey {
/**
* 公钥16进制, 用来加密
*/
private String publicKey;
/**
* 私钥16进制, 用来解密
*/
private String privateKey;
}
新建两个txt文件, 改名为public_key.pem和private_key.pem将生成的公钥和私钥粘贴到文件里保存
将这两个文件放到maven工程的src/main/resources/sm2/目录下
package com.geoway.privilege.utils.crypto;
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);
前端一般只需要加密, 所以只需要配置下公钥就行, 私钥出于安全考虑可以不作声明
标签:加密传输,hutool,String,解决方案,sm2,SM2,org,import,crypto From: https://www.cnblogs.com/iminifly/p/16720715.html