package xiaozhi.common.utils;
|
|
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.crypto.engines.SM2Engine;
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.math.ec.ECPoint;
|
import org.bouncycastle.util.encoders.Hex;
|
|
import java.math.BigInteger;
|
import java.security.*;
|
import java.security.spec.ECGenParameterSpec;
|
import java.nio.charset.StandardCharsets;
|
import java.util.HashMap;
|
import java.util.Map;
|
|
/**
|
* SM2加密工具类(采用十六进制格式,与chancheng-archive-service项目保持一致)
|
*/
|
public class SM2Utils {
|
|
/**
|
* 公钥常量
|
*/
|
public static final String KEY_PUBLIC_KEY = "publicKey";
|
/**
|
* 私钥返回值常量
|
*/
|
public static final String KEY_PRIVATE_KEY = "privateKey";
|
|
static {
|
Security.addProvider(new BouncyCastleProvider());
|
}
|
|
/**
|
* SM2加密算法
|
*
|
* @param publicKey 十六进制公钥
|
* @param data 明文数据
|
* @return 十六进制密文
|
*/
|
public static String encrypt(String publicKey, String data) {
|
try {
|
// 获取一条SM2曲线参数
|
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
|
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
|
//提取公钥点
|
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
|
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
|
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
|
|
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
|
// 设置sm2为加密模式
|
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
|
|
byte[] in = data.getBytes(StandardCharsets.UTF_8);
|
byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
|
return Hex.toHexString(arrayOfBytes);
|
} catch (Exception e) {
|
throw new RuntimeException("SM2加密失败", e);
|
}
|
}
|
|
/**
|
* SM2解密算法
|
*
|
* @param privateKey 十六进制私钥
|
* @param cipherData 十六进制密文数据
|
* @return 明文
|
*/
|
public static String decrypt(String privateKey, String cipherData) {
|
try {
|
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
|
if (!cipherData.startsWith("04")) {
|
cipherData = "04" + cipherData;
|
}
|
byte[] cipherDataByte = Hex.decode(cipherData);
|
BigInteger privateKeyD = new BigInteger(privateKey, 16);
|
//获取一条SM2曲线参数
|
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
//构造domain参数
|
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
|
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
|
|
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
|
// 设置sm2为解密模式
|
sm2Engine.init(false, privateKeyParameters);
|
|
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
|
return new String(arrayOfBytes, StandardCharsets.UTF_8);
|
} catch (Exception e) {
|
throw new RuntimeException("SM2解密失败", e);
|
}
|
}
|
|
/**
|
* 生成密钥对
|
*/
|
public static Map<String, String> createKey() {
|
try {
|
ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
|
// 获取一个椭圆曲线类型的密钥对生成器
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
|
// 使用SM2参数初始化生成器
|
kpg.initialize(sm2Spec);
|
// 获取密钥对
|
KeyPair keyPair = kpg.generateKeyPair();
|
PublicKey publicKey = keyPair.getPublic();
|
BCECPublicKey p = (BCECPublicKey) publicKey;
|
PrivateKey privateKey = keyPair.getPrivate();
|
BCECPrivateKey s = (BCECPrivateKey) privateKey;
|
|
Map<String, String> result = new HashMap<>();
|
result.put(KEY_PUBLIC_KEY, Hex.toHexString(p.getQ().getEncoded(false)));
|
result.put(KEY_PRIVATE_KEY, Hex.toHexString(s.getD().toByteArray()));
|
return result;
|
} catch (Exception e) {
|
throw new RuntimeException("生成SM2密钥对失败", e);
|
}
|
}
|
|
|
}
|