diff --git a/jsowell-common/pom.xml b/jsowell-common/pom.xml
index 401a3605d..6cb2978a2 100644
--- a/jsowell-common/pom.xml
+++ b/jsowell-common/pom.xml
@@ -178,6 +178,20 @@
3.10.2
+
+ Sm2Util
+ Sm2Util
+ 1.0.0
+
+
+
+
+ org.bouncycastle
+ bcpg-jdk18on
+ 1.77
+
+
+
diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/Sm2Util.java b/jsowell-common/src/main/java/com/jsowell/common/util/Sm2Util.java
new file mode 100644
index 000000000..61e0608df
--- /dev/null
+++ b/jsowell-common/src/main/java/com/jsowell/common/util/Sm2Util.java
@@ -0,0 +1,246 @@
+package com.jsowell.common.util;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import com.alibaba.fastjson2.JSONObject;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.*;
+import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.security.*;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.TreeMap;
+
+/**
+ * 国密二加密 Util
+ *
+ * @author Lemon
+ * @Date 2024/5/7 10:23:56
+ */
+public class Sm2Util {
+
+ static{
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ /**
+ * 随机生成sm2的公钥私钥对
+ * @return 公钥私钥对
+ */
+ public static KeyPair generateSm2KeyPair() {
+ try {
+ final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
+ // 获取一个椭圆曲线类型的密钥对生成器
+ final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
+ SecureRandom random = new SecureRandom();
+ // 使用SM2的算法区域初始化密钥生成器
+ kpg.initialize(sm2Spec, random);
+ // 获取密钥对
+ KeyPair keyPair = kpg.generateKeyPair();
+ return keyPair;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @param data 加密前的json数据
+ * @param platformPublicKeyStr 点行平台的国密二公钥
+ * @param thirdPartyPrivateKeyStr 第三方平台生成的国密二私钥
+ * @return 用于http发送请求的数据
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws NoSuchProviderException
+ * @throws CryptoException
+ */
+ public static String generateEncryptedRequestInfo(JSONObject data, String platformPublicKeyStr, String thirdPartyPrivateKeyStr)
+ throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, CryptoException {
+ String sign = sign(thirdPartyPrivateKeyStr, data);
+ data.put("sign",sign);
+ return encrypt(platformPublicKeyStr, data.toString());
+ }
+
+ /**
+ *
+ * @param publicKeyStr 以BASE64表示的sm2公钥
+ * @param data 待加密字符串, UTF-8编码
+ * @return 以BASE64表示的加密结果
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidKeySpecException
+ */
+ private static String encrypt(String publicKeyStr, String data) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
+ BCECPublicKey publicKey = getPublicKey(Base64.decode(publicKeyStr));
+ //通过公钥对象获取公钥的基本域参数。
+ ECParameterSpec ecParameterSpec = publicKey.getParameters();
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
+ ecParameterSpec.getG(), ecParameterSpec.getN());
+ //通过公钥值和公钥基本参数创建公钥参数对象
+ ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
+ //根据加密模式实例化SM2公钥加密引擎
+ SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ //初始化加密引擎
+ sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
+ byte[] arrayOfBytes = null;
+ try {
+ //将明文字符串转换为指定编码的字节串
+ byte[] in = data.getBytes("utf-8");
+ //通过加密引擎对字节数串行加密
+ arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ //将加密后的字节串转换为BASE 64字符串
+ return Base64.toBase64String(arrayOfBytes);
+ }
+
+ /**
+ *
+ * @param privateKeyStr 以BASE64表示的sm2私钥
+ * @param cipherData 以BASE64表示的加密结果
+ * @return 解密后字符串, UTF-8编码
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidKeySpecException
+ */
+ private static String decrypt(String privateKeyStr, String cipherData) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
+ BCECPrivateKey privateKey = getPrivateKey(Base64.decode(privateKeyStr));
+ byte[] cipherDataByte = Base64.decode(cipherData);
+ //通过私钥对象获取私钥的基本域参数。
+ ECParameterSpec ecParameterSpec = privateKey.getParameters();
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
+ ecParameterSpec.getG(), ecParameterSpec.getN());
+ //通过私钥值和私钥钥基本参数创建私钥参数对象
+ ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
+ ecDomainParameters);
+ //通过解密模式创建解密引擎并初始化
+ SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ sm2Engine.init(false, ecPrivateKeyParameters);
+ String result = null;
+ try {
+ //通过解密引擎对密文字节串进行解密
+ byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
+ //将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)
+ result = new String(arrayOfBytes, "utf-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param privateKeyStr 以BASE64表示的sm2私钥
+ * @param dataJson 待签名json数据
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidKeySpecException
+ * @throws CryptoException
+ */
+ private static String sign(String privateKeyStr, JSONObject dataJson) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, CryptoException {
+ BCECPrivateKey privateKey = getPrivateKey(Base64.decode(privateKeyStr));
+ ECParameterSpec ecParameterSpec = privateKey.getParameters();
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
+ ecParameterSpec.getG(), ecParameterSpec.getN());
+ //通过私钥值和私钥钥基本参数创建私钥参数对象
+ ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
+ ecDomainParameters);
+
+ //创建签名实例
+ SM2Signer sm2Signer = new SM2Signer();
+
+ //初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
+ try {
+ sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(ecPrivateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ String content = getSignContent(dataJson);
+ byte[] message = content.getBytes();
+ sm2Signer.update(message, 0, message.length);
+ //生成签名,签名分为两部分r和s,分别对应索引0和1的数组
+ byte[] signBytes = sm2Signer.generateSignature();
+ return Base64.toBase64String(signBytes);
+ }
+
+ /**
+ *
+ * @param publicKeyStr 以BASE64表示的sm2公钥
+ * @param dataJson 含有签名的待验签json数据
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws InvalidKeySpecException
+ */
+ private static boolean verify(String publicKeyStr, JSONObject dataJson) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
+ BCECPublicKey publicKey = getPublicKey(Base64.decode(publicKeyStr));
+ ECParameterSpec ecParameterSpec = publicKey.getParameters();
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
+ ecParameterSpec.getG(), ecParameterSpec.getN());
+ //通过公钥值和公钥基本参数创建公钥参数对象
+ ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
+ //创建签名实例
+ SM2Signer sm2Signer = new SM2Signer();
+ ParametersWithID parametersWithID = new ParametersWithID(ecPublicKeyParameters, Strings.toByteArray("1234567812345678"));
+ sm2Signer.init(false, parametersWithID);
+ String sign = (String) dataJson.remove("sign");
+ String content = getSignContent(dataJson);
+ byte[] message = content.getBytes();
+ sm2Signer.update(message, 0, message.length);
+ //验证签名结果
+ return sm2Signer.verifySignature(Base64.decode(sign));
+ }
+
+ private static String getSignContent(JSONObject rawData){
+ JSONObject data = new JSONObject(new TreeMap<>());
+ rawData.forEach((k,v) -> data.put(k,v));
+ StringBuffer sb = new StringBuffer();
+ data.forEach((k,v)->{
+ if (v != null && !"".equals(v)) {
+ sb.append(k + "=" + v + "&");
+ }
+ });
+ String stringData = sb.toString();
+ return stringData == null || stringData.isEmpty() ? "" : stringData.substring(0,stringData.length()-1);
+ }
+
+ private static BCECPrivateKey getPrivateKey(byte[] privateBytes) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
+ PKCS8EncodedKeySpec eks2 = new PKCS8EncodedKeySpec(privateBytes);
+ KeyFactory kf= KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
+ return (BCECPrivateKey) kf.generatePrivate(eks2);
+ }
+
+ private static BCECPublicKey getPublicKey(byte[] publicBytes) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
+ X509EncodedKeySpec eks = new X509EncodedKeySpec(publicBytes);
+ KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
+ return (BCECPublicKey) kf.generatePublic(eks);
+ }
+
+ public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, CryptoException {
+// KeyPair keyPair = generateSm2KeyPair();
+// System.out.println(Base64.toBase64String(keyPair.getPublic().getEncoded()));
+// System.out.println(Base64.toBase64String(keyPair.getPrivate().getEncoded()));
+ String platformPublicKeyStr = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEYPOlKmr/XY+na8KxiNvRui1esFugt4tT2AVk+eRlH4KCYLabDZDordal3kcn4UNM7t6J+dyhcfLstNWXpf4lQA==";
+ String thirdPartyPrivateKeyStr = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgCMref1FGlPZ9RfeJw/cnU5uEvFZNhHt7OvF4sgXnBjWgCgYIKoEcz1UBgi2hRANCAARj6kqkCaeNJSxWExQFsot1OuSCFrQOblhKx0U/y8GhgSND2MOAM08yXzl308waLqLt+jcsLF2UTW6XfrZNS5pk";
+ JSONObject data = new JSONObject();
+ data.put("outTradeId","202212231641900215435329");
+ data.put("plateNum","浙B12345");
+ data.put("startTime","2023-01-09 10:00:01");
+ data.put("endTime","2023-01-09 11:00:02");
+ data.put("recordTime","2023-01-09 12:00:03");
+ data.put("payAmount","12.34");
+ System.out.println(data);
+ System.out.println(generateEncryptedRequestInfo(data,platformPublicKeyStr,thirdPartyPrivateKeyStr));
+ }
+}