diff --git a/base-buildpackage/pom.xml b/base-buildpackage/pom.xml
index 718ed2d5..b1782b5e 100644
--- a/base-buildpackage/pom.xml
+++ b/base-buildpackage/pom.xml
@@ -20,6 +20,61 @@
base-webapp
${revision}
+
+
+
+ org.bouncycastle
+ bcmail-jdk15on
+ 1.56
+
+
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.57
+
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.57
+
+
+
+
+ commons-codec
+ commons-codec
+ 1.9
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.9
+
+
+
+
+ com.xiaoleilu
+ hutool-all
+ 3.0.9
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 3.3.0
+
+
+
+
+ com.squareup.okio
+ okio
+ 1.8.0
+
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SHA1.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SHA1.java
new file mode 100644
index 00000000..f7c99a65
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SHA1.java
@@ -0,0 +1,21 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.security.MessageDigest;
+
+import javax.xml.bind.DatatypeConverter;
+
+public class SHA1 {
+
+ public static String digest(String content) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+ messageDigest.update(content.getBytes("UTF-8"));
+ byte[] digestBytes = messageDigest.digest();
+ return DatatypeConverter.printBase64Binary(digestBytes);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cipher.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cipher.java
new file mode 100644
index 00000000..b074d671
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cipher.java
@@ -0,0 +1,105 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * 国密加密类实现
+ *
+ */
+public class SM2Cipher {
+
+ private int ct;
+
+ private ECPoint p2;
+
+ private SM3Digest sm3keybase;// SM3摘要
+
+ private SM3Digest sm3c3;
+
+ private byte key[];
+
+ private byte keyOff;
+
+ public SM2Cipher() {
+ this.ct = 1;
+ this.key = new byte[32];
+ this.keyOff = 0;
+ }
+
+ /**
+ * 重置
+ */
+ private void Reset() {
+ this.sm3keybase = new SM3Digest();
+ this.sm3c3 = new SM3Digest();
+
+ byte p[] = SM2Util.byteConvert32Bytes(p2.getXCoord().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.sm3c3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(p2.getYCoord().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.ct = 1;
+ NextKey();
+ }
+
+ private void NextKey() {
+ SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
+ sm3keycur.update((byte) (ct >> 24 & 0xff));
+ sm3keycur.update((byte) (ct >> 16 & 0xff));
+ sm3keycur.update((byte) (ct >> 8 & 0xff));
+ sm3keycur.update((byte) (ct & 0xff));
+ sm3keycur.doFinal(key, 0);
+ this.keyOff = 0;
+ this.ct++;
+ }
+
+ public ECPoint Init_enc(SM2Factory sm2, ECPoint userKey) {
+ AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
+ BigInteger k = ecpriv.getD();
+ ECPoint c1 = ecpub.getQ();
+ this.p2 = userKey.multiply(k);
+ Reset();
+ return c1;
+ }
+
+ public void Encrypt(byte data[]) {
+ this.sm3c3.update(data, 0, data.length);
+ for (int i = 0; i < data.length; i++) {
+ if (keyOff == key.length) {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+ }
+
+ public void Init_dec(BigInteger userD, ECPoint c1) {
+ this.p2 = c1.multiply(userD);
+ Reset();
+ }
+
+ public void Decrypt(byte data[]) {
+ for (int i = 0; i < data.length; i++) {
+ if (keyOff == key.length) {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+
+ this.sm3c3.update(data, 0, data.length);
+ }
+
+ public void Dofinal(byte c3[]) {
+ byte p[] = SM2Util.byteConvert32Bytes(p2.getYCoord().toBigInteger());
+ this.sm3c3.update(p, 0, p.length);
+ this.sm3c3.doFinal(c3, 0);
+ Reset();
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cryptor.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cryptor.java
new file mode 100644
index 00000000..923d7ba1
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Cryptor.java
@@ -0,0 +1,173 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+
+/**
+ * 报文加解密工具类
+ *
+ */
+public class SM2Cryptor {
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ // 算法名称
+ public static final String ALGORITHM_NAME = "sm4";
+
+ // P5填充
+ public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
+
+ /**
+ * SHA256加密
+ *
+ * @param str
+ * @return
+ */
+ public static String sha256(String str) {
+ MessageDigest messageDigest = null;
+ String enencdeStr = "";
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-256");
+ byte[] hash = messageDigest.digest(str.getBytes("UTF-8"));
+ enencdeStr = Hex.encodeHexString(hash);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return enencdeStr;
+ }
+
+ /**
+ * MD5加密
+ *
+ * @param hash
+ * @return
+ */
+ public static String md5(String hash) {
+ String md5Str = DigestUtils.md5Hex(hash);
+ return md5Str;
+ }
+
+ /**
+ *
+ * sm3加密处理
+ *
+ * @param data
+ * @return 2019年11月26日
+ *
+ */
+ public static String sm3(String data) {
+ String charset = "UTF-8";
+ String sm3Data = "";
+ try {
+ byte[] dataBytes = data.getBytes(charset);
+ byte[] hashBytes = hash(dataBytes);
+ sm3Data = ByteUtils.toHexString(hashBytes);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return sm3Data;
+ }
+
+ /**
+ *
+ * 返回长度为32位的byte数组 生成对应的hash值
+ *
+ * @param dataBytes
+ * @return 2019年10月28日
+ *
+ */
+ public static byte[] hash(byte[] dataBytes) {
+ SM3Digest digest = new SM3Digest();
+ digest.update(dataBytes, 0, dataBytes.length);
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+ return hash;
+ }
+
+ /**
+ * 报文体加密
+ *
+ * @param key
+ * @param data
+ * @return
+ */
+ public static String encrypt(String key, String data) {
+
+ String encrypted = "";
+
+ try {
+ String charset = "UTF-8";
+
+ String sha256Key = sha256(key);
+ String sm3Key = sm3(sha256Key);
+ String md5Key = md5(sm3Key);
+
+ byte[] keyBytes = ByteUtils.fromHexString(md5Key);
+ byte[] dataBytes = data.getBytes(charset);
+
+ Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING,
+ BouncyCastleProvider.PROVIDER_NAME);
+ SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM_NAME);
+ cipher.init(Cipher.ENCRYPT_MODE, sm4Key);
+ byte[] encryptBytes = cipher.doFinal(dataBytes);
+ String hexSignature = ByteUtils.toHexString(encryptBytes).toUpperCase();
+ byte[] signBytes = hexSignature.getBytes(charset);
+ encrypted = DatatypeConverter.printBase64Binary(signBytes);
+ return encrypted;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return encrypted;
+ }
+
+ /**
+ * 报文体解密
+ *
+ * @param key
+ * @param signature
+ * @return
+ */
+ public static String decrypt(String key, String encrypted) {
+
+ String decrypted = "";
+
+ try {
+
+ String sha256Key = sha256(key);
+ String sm3Key = sm3(sha256Key);
+ String md5Key = md5(sm3Key);
+ byte[] keyBytes = ByteUtils.fromHexString(md5Key);
+
+ byte[] encryptBytes = DatatypeConverter.parseBase64Binary(encrypted);
+ String hexSignature = new String(encryptBytes).toLowerCase();
+ byte[] cipherBytes = ByteUtils.fromHexString(hexSignature);
+
+ Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING,
+ BouncyCastleProvider.PROVIDER_NAME);
+ SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM_NAME);
+ cipher.init(Cipher.DECRYPT_MODE, sm4Key);
+ byte[] doFinal = cipher.doFinal(cipherBytes);
+ decrypted = new String(doFinal);
+ return decrypted;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return decrypted;
+ }
+
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2EnDecryptor.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2EnDecryptor.java
new file mode 100644
index 00000000..b81ccd98
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2EnDecryptor.java
@@ -0,0 +1,79 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * SM2加解密工具类
+ *
+ */
+public class SM2EnDecryptor {
+
+ /**
+ * SM2加密
+ *
+ * @param hexStrPub 公钥信息
+ * @param data 加密数据信息
+ * @return
+ */
+ public static String encrypt(byte[] hexStrPub, byte[] data) {
+ byte[] source = new byte[data.length];
+ byte[] formatedPubKey;
+ System.arraycopy(data, 0, source, 0, data.length);
+ SM2Cipher cipher2sm2 = new SM2Cipher();
+ SM2Factory sm2Factory = SM2Factory.getInstance();
+ if (hexStrPub.length == 64) {
+ formatedPubKey = new byte[65];
+ formatedPubKey[0] = 0x04;
+ System.arraycopy(hexStrPub, 0, formatedPubKey, 1, hexStrPub.length);
+ } else {
+ formatedPubKey = hexStrPub;
+ }
+ ECPoint ecPoint = sm2Factory.ecc_curve.decodePoint(formatedPubKey);
+ ECPoint c1 = cipher2sm2.Init_enc(sm2Factory, ecPoint);
+ cipher2sm2.Encrypt(source);
+ byte[] c3 = new byte[32];
+ cipher2sm2.Dofinal(c3);
+ return SM2Util.byteToHex(c1.getEncoded(false)) + SM2Util.byteToHex(source) + SM2Util.byteToHex(c3);
+ }
+
+ /**
+ * SM2解密
+ *
+ * @param privateKey 私钥信息
+ * @param encryptedData 加密后的数据信息
+ * @return
+ * @throws IOException
+ */
+ public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException {
+ if (privateKey == null || privateKey.length == 0) {
+ return null;
+ }
+ if (encryptedData == null || encryptedData.length == 0) {
+ return null;
+ }
+ // 加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
+ String data = SM2Util.byteToHex(encryptedData);
+ /***
+ * 分解加密字串 (C1 = C1标志位2位 + C1实体部分128位 = 130) (C3 = C3实体部分64位 = 64) (C2 = encryptedData.length
+ * * 2 - C1长度 - C2长度)
+ */
+ byte[] c1Bytes = SM2Util.hexToByte(data.substring(0, 130));
+ int c2Len = encryptedData.length - 97;
+ byte[] c2 = SM2Util.hexToByte(data.substring(130, 130 + 2 * c2Len));
+ byte[] c3 = SM2Util.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));
+
+ SM2Factory sm2 = SM2Factory.getInstance();
+ BigInteger userD = new BigInteger(1, privateKey);
+ // 通过C1实体字节来生成ECPoint
+ ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
+ SM2Cipher cipher = new SM2Cipher();
+ cipher.Init_dec(userD, c1);
+ cipher.Decrypt(c2);
+ cipher.Dofinal(c3);
+ // 返回解密结果
+ return c2;
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Factory.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Factory.java
new file mode 100644
index 00000000..29b52c30
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Factory.java
@@ -0,0 +1,197 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+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.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.ECFieldElement.Fp;
+
+/**
+ * 国密工厂类
+ *
+ */
+@SuppressWarnings(value = { "deprecation", "static-access" })
+public class SM2Factory {
+
+ /*-----------------------国密算法相关参数begin-----------
+ * ------------------*/
+ // A 第一系数
+ private static final BigInteger a = new BigInteger(
+ "fffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc", 16);
+
+ // B 第二系数
+ private static final BigInteger b = new BigInteger(
+ "28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93", 16);
+
+ // 曲线X系数
+ private static final BigInteger gx = new BigInteger(
+ "32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7", 16);
+
+ // 曲线Y系数
+ private static final BigInteger gy = new BigInteger(
+ "bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0", 16);
+
+ // 生产者顺序系数
+ private static final BigInteger n = new BigInteger(
+ "fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123", 16);
+
+ // 素数
+ private static final BigInteger p = new BigInteger(
+ "fffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff", 16);
+
+ // 因子系数 1
+ // private static final int h = 1;
+
+ /*-----------------------国密算法相关参数end-----------------------------*/
+ // 一些必要类
+ public final ECFieldElement ecc_gx_fieldelement;
+
+ public final ECFieldElement ecc_gy_fieldelement;
+
+ public final ECCurve ecc_curve;
+
+ public final ECPoint ecc_point_g;
+
+ public final ECDomainParameters ecc_bc_spec;
+
+ public final ECKeyPairGenerator ecc_key_pair_generator;
+
+ /**
+ * 初始化方法
+ *
+ * @return
+ */
+ public static SM2Factory getInstance() {
+ return new SM2Factory();
+ }
+
+ public SM2Factory() {
+
+ this.ecc_gx_fieldelement = new Fp(this.p, this.gx);
+ this.ecc_gy_fieldelement = new Fp(this.p, this.gy);
+
+ this.ecc_curve = new ECCurve.Fp(this.p, this.a, this.b);
+
+ this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);
+ this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.n);
+
+ ECKeyGenerationParameters ecc_ecgenparam;
+ ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
+
+ this.ecc_key_pair_generator = new ECKeyPairGenerator();
+ this.ecc_key_pair_generator.init(ecc_ecgenparam);
+ }
+
+ /**
+ * 根据私钥、曲线参数计算Z
+ *
+ * @param userId
+ * @param userKey
+ * @return
+ */
+ public byte[] sm2GetZ(byte[] userId, ECPoint userKey) {
+
+ SM3Digest sm3 = new SM3Digest();
+
+ int len = userId.length * 8;
+ sm3.update((byte) (len >> 8 & 0xFF));
+ sm3.update((byte) (len & 0xFF));
+ sm3.update(userId, 0, userId.length);
+
+ byte[] p = SM2Util.byteConvert32Bytes(this.a);
+ sm3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(this.b);
+ sm3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(this.gx);
+ sm3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(this.gy);
+ sm3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(userKey.normalize().getXCoord().toBigInteger());
+ sm3.update(p, 0, p.length);
+
+ p = SM2Util.byteConvert32Bytes(userKey.normalize().getYCoord().toBigInteger());
+ sm3.update(p, 0, p.length);
+
+ byte[] md = new byte[sm3.getDigestSize()];
+ sm3.doFinal(md, 0);
+ return md;
+ }
+
+ /**
+ * 签名相关值计算
+ *
+ * @param md
+ * @param userD
+ * @param userKey
+ * @param sm2Result
+ */
+ public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result) {
+
+ BigInteger e = new BigInteger(1, md);
+ BigInteger k = null;
+ ECPoint kp = null;
+ BigInteger r = null;
+ BigInteger s = null;
+ do {
+ do {
+ // 正式环境
+ AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();
+ k = ecpriv.getD();
+ kp = ecpub.getQ();
+ // r
+ r = e.add(kp.getXCoord().toBigInteger());
+ r = r.mod(this.n);
+ } while (r.equals(BigInteger.ZERO) || r.add(k).equals(this.n));
+
+ // (1 + dA)~-1
+ BigInteger da_1 = userD.add(BigInteger.ONE);
+ da_1 = da_1.modInverse(this.n);
+ // s
+ s = r.multiply(userD);
+ s = k.subtract(s).mod(this.n);
+ s = da_1.multiply(s).mod(this.n);
+ } while (s.equals(BigInteger.ZERO));
+
+ sm2Result.r = r;
+ sm2Result.s = s;
+ }
+
+ /**
+ * 验签
+ *
+ * @param md sm3摘要
+ * @param userKey 根据公钥decode一个ecpoint对象
+ * @param r 没有特殊含义
+ * @param s 没有特殊含义
+ * @param sm2Result 接收参数的对象
+ */
+ public void sm2Verify(byte md[], ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result) {
+
+ sm2Result.R = null;
+ BigInteger e = new BigInteger(1, md);
+ BigInteger t = r.add(s).mod(this.n);
+ if (t.equals(BigInteger.ZERO)) {
+ return;
+ } else {
+ ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);
+ x1y1 = x1y1.add(userKey.multiply(t));
+ sm2Result.R = e.add(x1y1.normalize().getXCoord().toBigInteger()).mod(this.n);
+ return;
+ }
+ }
+
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Result.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Result.java
new file mode 100644
index 00000000..5030f10a
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Result.java
@@ -0,0 +1,33 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * SM2
+ *
+ */
+public class SM2Result {
+
+ public SM2Result() {}
+
+ public BigInteger r;// 签名r
+
+ public BigInteger s;
+
+ public BigInteger R;// 验签R
+
+ public byte[] sa;// 密钥交换
+
+ public byte[] sb;
+
+ public byte[] s1;
+
+ public byte[] s2;
+
+ public ECPoint keyra;
+
+ public ECPoint keyrb;
+
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Sign.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Sign.java
new file mode 100644
index 00000000..0f86b01d
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Sign.java
@@ -0,0 +1,151 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+/**
+ * SM2签名所计算的值 可以根据实际情况增加删除字段属性
+ *
+ */
+public class SM2Sign {
+
+ // 16进制的私钥
+ public String sm2_userd;
+
+ // 椭圆曲线点X
+ public String x_coord;
+
+ // 椭圆曲线点Y
+ public String y_coord;
+
+ // SM3摘要Z
+ public String sm3_z;
+
+ // 明文数据16进制
+ public String sign_express;
+
+ // SM3摘要值
+ public String sm3_digest;
+
+ // R
+ public String sign_r;
+
+ // S
+ public String sign_s;
+
+ // R
+ public String verify_r;
+
+ // S
+ public String verify_s;
+
+ // 签名值
+ public String sm2_sign;
+
+ // sign 签名 verfiy验签
+ public String sm2_type;
+
+ // 是否验签成功 true false
+ public boolean isVerify;
+
+ public String getX_coord() {
+ return x_coord;
+ }
+
+ public void setX_coord(String x_coord) {
+ this.x_coord = x_coord;
+ }
+
+ public String getY_coord() {
+ return y_coord;
+ }
+
+ public void setY_coord(String y_coord) {
+ this.y_coord = y_coord;
+ }
+
+ public String getSm3_z() {
+ return sm3_z;
+ }
+
+ public void setSm3_z(String sm3_z) {
+ this.sm3_z = sm3_z;
+ }
+
+ public String getSm3_digest() {
+ return sm3_digest;
+ }
+
+ public void setSm3_digest(String sm3_digest) {
+ this.sm3_digest = sm3_digest;
+ }
+
+ public String getSm2_sign() {
+ return sm2_sign;
+ }
+
+ public void setSm2_sign(String sm2_sign) {
+ this.sm2_sign = sm2_sign;
+ }
+
+ public String getSign_express() {
+ return sign_express;
+ }
+
+ public void setSign_express(String sign_express) {
+ this.sign_express = sign_express;
+ }
+
+ public String getSm2_userd() {
+ return sm2_userd;
+ }
+
+ public void setSm2_userd(String sm2_userd) {
+ this.sm2_userd = sm2_userd;
+ }
+
+ public String getSm2_type() {
+ return sm2_type;
+ }
+
+ public void setSm2_type(String sm2_type) {
+ this.sm2_type = sm2_type;
+ }
+
+ public boolean isVerify() {
+ return isVerify;
+ }
+
+ public void setVerify(boolean isVerify) {
+ this.isVerify = isVerify;
+ }
+
+ public String getSign_r() {
+ return sign_r;
+ }
+
+ public void setSign_r(String sign_r) {
+ this.sign_r = sign_r;
+ }
+
+ public String getSign_s() {
+ return sign_s;
+ }
+
+ public void setSign_s(String sign_s) {
+ this.sign_s = sign_s;
+ }
+
+ public String getVerify_r() {
+ return verify_r;
+ }
+
+ public void setVerify_r(String verify_r) {
+ this.verify_r = verify_r;
+ }
+
+ public String getVerify_s() {
+ return verify_s;
+ }
+
+ public void setVerify_s(String verify_s) {
+ this.verify_s = verify_s;
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2SignVerify.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2SignVerify.java
new file mode 100644
index 00000000..56c5e9f3
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2SignVerify.java
@@ -0,0 +1,132 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+
+/**
+ * 国密算法的签名、验签
+ *
+ */
+public class SM2SignVerify {
+
+ /**
+ * 默认USERID
+ */
+ public static String USER_ID = "1234567812345678";
+
+ /**
+ * 私钥签名 使用SM3进行对明文数据计算一个摘要值
+ *
+ * @param privatekey 私钥
+ * @param sourceData 明文数据
+ * @return 签名后的值
+ * @throws Exception
+ */
+ public static SM2Sign sign(byte[] privatekey, byte[] sourceData) throws Exception {
+ SM2Sign sm2SignVO = new SM2Sign();
+ sm2SignVO.setSm2_type("sign");
+ SM2Factory factory = SM2Factory.getInstance();
+ BigInteger userD = new BigInteger(1, privatekey);
+ sm2SignVO.setSm2_userd(userD.toString(16));
+
+ ECPoint userKey = factory.ecc_point_g.multiply(userD);
+ sm2SignVO.setX_coord(userKey.getXCoord().toBigInteger().toString(16));
+ sm2SignVO.setY_coord(userKey.getYCoord().toBigInteger().toString(16));
+
+ SM3Digest sm3Digest = new SM3Digest();
+ byte[] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
+ sm2SignVO.setSm3_z(SM2Util.getHexString(z));
+ sm2SignVO.setSign_express(SM2Util.getHexString(sourceData));
+ sm3Digest.update(z, 0, z.length);
+ sm3Digest.update(sourceData, 0, sourceData.length);
+ byte[] md = new byte[32];
+ sm3Digest.doFinal(md, 0);
+ sm2SignVO.setSm3_digest(SM2Util.getHexString(md));
+
+ SM2Result sm2Result = new SM2Result();
+ factory.sm2Sign(md, userD, userKey, sm2Result);
+ sm2SignVO.setSign_r(sm2Result.r.toString(16));
+ sm2SignVO.setSign_s(sm2Result.s.toString(16));
+
+ ASN1Integer d_r = new ASN1Integer(sm2Result.r);
+ ASN1Integer d_s = new ASN1Integer(sm2Result.s);
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+ v2.add(d_r);
+ v2.add(d_s);
+ DERSequence sign = new DERSequence(v2);
+ String result = ByteUtils.toHexString(sign.getEncoded());
+ sm2SignVO.setSm2_sign(result);
+ return sm2SignVO;
+ }
+
+ /**
+ * 验证签名(验签)
+ *
+ * @param publicKey 公钥信息
+ * @param sourceData 密文信息
+ * @param signData 签名信息
+ * @return 验签的对象 包含了相关参数和验签结果
+ */
+ @SuppressWarnings("unchecked")
+ public static SM2Sign validateSign(byte[] publicKey, byte[] sourceData, byte[] signData) {
+ try {
+ byte[] formatedPubKey;
+ SM2Sign verifyVo = new SM2Sign();
+ verifyVo.setSm2_type("verify");
+ if (publicKey.length == 64) {
+ // 添加一字节标识,用于ECPoint解析
+ formatedPubKey = new byte[65];
+ formatedPubKey[0] = 0x04;
+ System.arraycopy(publicKey, 0, formatedPubKey, 1, publicKey.length);
+ } else {
+ formatedPubKey = publicKey;
+ }
+ SM2Factory factory = SM2Factory.getInstance();
+ ECPoint userKey = factory.ecc_curve.decodePoint(formatedPubKey);
+
+ SM3Digest sm3Digest = new SM3Digest();
+ byte[] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
+ verifyVo.setSm3_z(SM2Util.getHexString(z));
+ sm3Digest.update(z, 0, z.length);
+ sm3Digest.update(sourceData, 0, sourceData.length);
+ byte[] md = new byte[32];
+ sm3Digest.doFinal(md, 0);
+ verifyVo.setSm3_digest(SM2Util.getHexString(md));
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(signData);
+ ASN1InputStream dis = new ASN1InputStream(bis);
+ SM2Result sm2Result = null;
+ ASN1Primitive derObj = dis.readObject();
+ dis.close();
+ bis.close();
+ Enumeration e = ((ASN1Sequence) derObj).getObjects();
+ BigInteger r = ((ASN1Integer) e.nextElement()).getValue();
+ BigInteger s = ((ASN1Integer) e.nextElement()).getValue();
+ sm2Result = new SM2Result();
+ sm2Result.r = r;
+ sm2Result.s = s;
+ verifyVo.setVerify_r(sm2Result.r.toString(16));
+ verifyVo.setVerify_s(sm2Result.s.toString(16));
+ factory.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);
+ boolean verifyFlag = sm2Result.r.equals(sm2Result.R);
+ verifyVo.setVerify(verifyFlag);
+ return verifyVo;
+ } catch (IllegalArgumentException e) {
+ System.out.println(e);
+ return null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Util.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Util.java
new file mode 100644
index 00000000..98985d83
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM2Util.java
@@ -0,0 +1,609 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import java.math.BigInteger;
+
+public class SM2Util {
+ /**
+ * 整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] intToBytes(int num) {
+ byte[] bytes = new byte[4];
+ bytes[0] = (byte) (0xff & (num >> 0));
+ bytes[1] = (byte) (0xff & (num >> 8));
+ bytes[2] = (byte) (0xff & (num >> 16));
+ bytes[3] = (byte) (0xff & (num >> 24));
+ return bytes;
+ }
+
+ /**
+ * 四个字节的字节数据转换成一个整形数据
+ *
+ * @param bytes 4个字节的字节数组
+ * @return 一个整型数据
+ */
+ public static int byteToInt(byte[] bytes) {
+ int num = 0;
+ int temp;
+ temp = (0x000000ff & (bytes[0])) << 0;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[1])) << 8;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[2])) << 16;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[3])) << 24;
+ num = num | temp;
+ return num;
+ }
+
+ /**
+ * 长整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个长整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] longToBytes(long num) {
+ byte[] bytes = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = (byte) (0xff & (num >> (i * 8)));
+ }
+
+ return bytes;
+ }
+
+ /**
+ * 大数字转换字节流(字节数组)型数据
+ *
+ * @param n
+ * @return
+ */
+ public static byte[] byteConvert32Bytes(BigInteger n) {
+ byte tmpd[] = (byte[]) null;
+ if (n == null) {
+ return null;
+ }
+
+ if (n.toByteArray().length == 33) {
+ tmpd = new byte[32];
+ System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32);
+ } else if (n.toByteArray().length == 32) {
+ tmpd = n.toByteArray();
+ } else {
+ tmpd = new byte[32];
+ for (int i = 0; i < 32 - n.toByteArray().length; i++) {
+ tmpd[i] = 0;
+ }
+ System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length);
+ }
+ return tmpd;
+ }
+
+ /**
+ * 换字节流(字节数组)型数据转大数字
+ *
+ * @param b
+ * @return
+ */
+ public static BigInteger byteConvertInteger(byte[] b) {
+ if (b[0] < 0) {
+ byte[] temp = new byte[b.length + 1];
+ temp[0] = 0;
+ System.arraycopy(b, 0, temp, 1, b.length);
+ return new BigInteger(temp);
+ }
+ return new BigInteger(b);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @return
+ */
+ public static String getHexString(byte[] bytes) {
+ return getHexString(bytes, true);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @param upperCase
+ * @return
+ */
+ public static String getHexString(byte[] bytes, boolean upperCase) {
+ String ret = "";
+ for (int i = 0; i < bytes.length; i++) {
+ ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1);
+ }
+ return upperCase ? ret.toUpperCase() : ret;
+ }
+
+ /**
+ * 打印十六进制字符串
+ *
+ * @param bytes
+ */
+ public static void printHexString(byte[] bytes) {
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(bytes[i] & 0xFF);
+ if (hex.length() == 1) {
+ hex = '0' + hex;
+ }
+ }
+ }
+
+ /**
+ * Convert hex string to byte[]
+ *
+ * @param hexString the hex string
+ * @return byte[]
+ */
+ public static byte[] hexStringToBytes(String hexString) {
+ if (hexString == null || hexString.equals("")) {
+ return null;
+ }
+ hexString = hexString.toUpperCase();
+ int length = hexString.length() / 2;
+ char[] hexChars = hexString.toCharArray();
+ byte[] d = new byte[length];
+ for (int i = 0; i < length; i++) {
+ int pos = i * 2;
+ d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return d;
+ }
+
+ /**
+ * Convert char to byte
+ *
+ * @param c char
+ * @return byte
+ */
+ public static byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+
+ /**
+ * 用于建立十六进制字符的输出的小写字符数组
+ */
+ private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f' };
+
+ /**
+ * 用于建立十六进制字符的输出的大写字符数组
+ */
+ private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F' };
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data) {
+ return encodeHex(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制char[]
+ */
+ protected static char[] encodeHex(byte[] data, char[] toDigits) {
+ int l = data.length;
+ char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return out;
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data) {
+ return encodeHexString(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data, boolean toLowerCase) {
+ return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制String
+ */
+ protected static String encodeHexString(byte[] data, char[] toDigits) {
+ return new String(encodeHex(data, toDigits));
+ }
+
+ /**
+ * 将十六进制字符数组转换为字节数组
+ *
+ * @param data 十六进制char[]
+ * @return byte[]
+ * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
+ */
+ public static byte[] decodeHex(char[] data) {
+ int len = data.length;
+
+ if ((len & 0x01) != 0) {
+ throw new RuntimeException("Odd number of characters.");
+ }
+
+ byte[] out = new byte[len >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < len; i++) {
+ int f = toDigit(data[j], j) << 4;
+ j++;
+ f = f | toDigit(data[j], j);
+ j++;
+ out[i] = (byte) (f & 0xFF);
+ }
+
+ return out;
+ }
+
+ /**
+ * 将十六进制字符转换成一个整数
+ *
+ * @param ch 十六进制char
+ * @param index 十六进制字符在字符数组中的位置
+ * @return 一个整数
+ * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常
+ */
+ protected static int toDigit(char ch, int index) {
+ int digit = Character.digit(ch, 16);
+ if (digit == -1) {
+ throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
+ }
+ return digit;
+ }
+
+ /**
+ * 数字字符串转ASCII码字符串
+ *
+ * @param String 字符串
+ * @return ASCII字符串
+ */
+ public static String StringToAsciiString(String content) {
+ String result = "";
+ int max = content.length();
+ for (int i = 0; i < max; i++) {
+ char c = content.charAt(i);
+ String b = Integer.toHexString(c);
+ result = result + b;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制转字符串
+ *
+ * @param hexString 十六进制字符串
+ * @param encodeType 编码类型4:Unicode,2:普通编码
+ * @return 字符串
+ */
+ public static String hexStringToString(String hexString, int encodeType) {
+ String result = "";
+ int max = hexString.length() / encodeType;
+ for (int i = 0; i < max; i++) {
+ char c = (char) hexStringToAlgorism(hexString.substring(i * encodeType, (i + 1) * encodeType));
+ result += c;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制字符串装十进制
+ *
+ * @param hex 十六进制字符串
+ * @return 十进制数值
+ */
+ public static int hexStringToAlgorism(String hex) {
+ hex = hex.toUpperCase();
+ int max = hex.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = hex.charAt(i - 1);
+ int algorism = 0;
+ if (c >= '0' && c <= '9') {
+ algorism = c - '0';
+ } else {
+ algorism = c - 55;
+ }
+ result += Math.pow(16, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十六转二进制
+ *
+ * @param hex 十六进制字符串
+ * @return 二进制字符串
+ */
+ public static String hexStringToBinary(String hex) {
+ hex = hex.toUpperCase();
+ String result = "";
+ int max = hex.length();
+ for (int i = 0; i < max; i++) {
+ char c = hex.charAt(i);
+ switch (c) {
+ case '0':
+ result += "0000";
+ break;
+ case '1':
+ result += "0001";
+ break;
+ case '2':
+ result += "0010";
+ break;
+ case '3':
+ result += "0011";
+ break;
+ case '4':
+ result += "0100";
+ break;
+ case '5':
+ result += "0101";
+ break;
+ case '6':
+ result += "0110";
+ break;
+ case '7':
+ result += "0111";
+ break;
+ case '8':
+ result += "1000";
+ break;
+ case '9':
+ result += "1001";
+ break;
+ case 'A':
+ result += "1010";
+ break;
+ case 'B':
+ result += "1011";
+ break;
+ case 'C':
+ result += "1100";
+ break;
+ case 'D':
+ result += "1101";
+ break;
+ case 'E':
+ result += "1110";
+ break;
+ case 'F':
+ result += "1111";
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * ASCII码字符串转数字字符串
+ *
+ * @param String ASCII字符串
+ * @return 字符串
+ */
+ public static String AsciiStringToString(String content) {
+ String result = "";
+ int length = content.length() / 2;
+ for (int i = 0; i < length; i++) {
+ String c = content.substring(i * 2, i * 2 + 2);
+ int a = hexStringToAlgorism(c);
+ char b = (char) a;
+ String d = String.valueOf(b);
+ result += d;
+ }
+ return result;
+ }
+
+ /**
+ * 将十进制转换为指定长度的十六进制字符串
+ *
+ * @param algorism int 十进制数字
+ * @param maxLength int 转换后的十六进制字符串长度
+ * @return String 转换后的十六进制字符串
+ */
+ public static String algorismToHexString(int algorism, int maxLength) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+ }
+ return patchHexString(result.toUpperCase(), maxLength);
+ }
+
+ /**
+ * 字节数组转为普通字符串(ASCII对应的字符)
+ *
+ * @param bytearray byte[]
+ * @return String
+ */
+ public static String byteToString(byte[] bytearray) {
+ String result = "";
+ char temp;
+
+ int length = bytearray.length;
+ for (int i = 0; i < length; i++) {
+ temp = (char) bytearray[i];
+ result += temp;
+ }
+ return result;
+ }
+
+ /**
+ * 二进制字符串转十进制
+ *
+ * @param binary 二进制字符串
+ * @return 十进制数值
+ */
+ public static int binaryToAlgorism(String binary) {
+ int max = binary.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = binary.charAt(i - 1);
+ int algorism = c - '0';
+ result += Math.pow(2, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十进制转换为十六进制字符串
+ *
+ * @param algorism int 十进制的数字
+ * @return String 对应的十六进制字符串
+ */
+ public static String algorismToHEXString(int algorism) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+
+ }
+ result = result.toUpperCase();
+
+ return result;
+ }
+
+ /**
+ * HEX字符串前补0,主要用于长度位数不足。
+ *
+ * @param str String 需要补充长度的十六进制字符串
+ * @param maxLength int 补充后十六进制字符串的长度
+ * @return 补充结果
+ */
+ static public String patchHexString(String str, int maxLength) {
+ String temp = "";
+ for (int i = 0; i < maxLength - str.length(); i++) {
+ temp = "0" + temp;
+ }
+ str = (temp + str).substring(0, maxLength);
+ return str;
+ }
+
+ /**
+ * 将一个字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @param radix int 要转换的字符串是什么进制的,如16 8 10.
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt, int radix) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s, radix);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 将一个十进制形式的数字字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 十六进制串转化为byte数组
+ *
+ * @return the array of byte
+ */
+ public static byte[] hexToByte(String hex) throws IllegalArgumentException {
+ if (hex.length() % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ char[] arr = hex.toCharArray();
+ byte[] b = new byte[hex.length() / 2];
+ for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
+ String swap = "" + arr[i++] + arr[i];
+ int byteint = Integer.parseInt(swap, 16) & 0xFF;
+ b[j] = new Integer(byteint).byteValue();
+ }
+ return b;
+ }
+
+ /**
+ * 字节数组转换为十六进制字符串
+ *
+ * @param b byte[] 需要转换的字节数组
+ * @return String 十六进制字符串
+ */
+ public static String byteToHex(byte b[]) {
+ if (b == null) {
+ throw new IllegalArgumentException("Argument b ( byte array ) is null! ");
+ }
+ String hs = "";
+ String stmp = "";
+ for (int n = 0; n < b.length; n++) {
+ stmp = Integer.toHexString(b[n] & 0xff);
+ if (stmp.length() == 1) {
+ hs = hs + "0" + stmp;
+ } else {
+ hs = hs + stmp;
+ }
+ }
+ return hs.toUpperCase();
+ }
+
+ public static byte[] subByte(byte[] input, int startIndex, int length) {
+ byte[] bt = new byte[length];
+ for (int i = 0; i < length; i++) {
+ bt[i] = input[i + startIndex];
+ }
+ return bt;
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3.java
new file mode 100644
index 00000000..725ab141
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3.java
@@ -0,0 +1,258 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+/**
+ * SM3
+ *
+ */
+public class SM3 {
+
+ public static final byte[] iv = { 0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 0x14, (byte) 0xb2, (byte) 0xb9,
+ 0x17, 0x24, 0x42, (byte) 0xd7, (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30,
+ (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3, (byte) 0x8d, (byte) 0xee, 0x4d,
+ (byte) 0xb0, (byte) 0xfb, 0x0e, 0x4e };
+
+ public static int[] Tj = new int[64];
+
+ static {
+ for (int i = 0; i < 16; i++) {
+ Tj[i] = 0x79cc4519;
+ }
+
+ for (int i = 16; i < 64; i++) {
+ Tj[i] = 0x7a879d8a;
+ }
+ }
+
+ public static byte[] CF(byte[] V, byte[] B) {
+ int[] v, b;
+ v = convert(V);
+ b = convert(B);
+ return convert(CF(v, b));
+ }
+
+ private static int[] convert(byte[] arr) {
+ int[] out = new int[arr.length / 4];
+ byte[] tmp = new byte[4];
+ for (int i = 0; i < arr.length; i += 4) {
+ System.arraycopy(arr, i, tmp, 0, 4);
+ out[i / 4] = bigEndianByteToInt(tmp);
+ }
+ return out;
+ }
+
+ private static byte[] convert(int[] arr) {
+ byte[] out = new byte[arr.length * 4];
+ byte[] tmp = null;
+ for (int i = 0; i < arr.length; i++) {
+ tmp = bigEndianIntToByte(arr[i]);
+ System.arraycopy(tmp, 0, out, i * 4, 4);
+ }
+ return out;
+ }
+
+ public static int[] CF(int[] V, int[] B) {
+ int a, b, c, d, e, f, g, h;
+ int ss1, ss2, tt1, tt2;
+ a = V[0];
+ b = V[1];
+ c = V[2];
+ d = V[3];
+ e = V[4];
+ f = V[5];
+ g = V[6];
+ h = V[7];
+
+ int[][] arr = expand(B);
+ int[] w = arr[0];
+ int[] w1 = arr[1];
+
+ for (int j = 0; j < 64; j++) {
+ ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j));
+ ss1 = bitCycleLeft(ss1, 7);
+ ss2 = ss1 ^ bitCycleLeft(a, 12);
+ tt1 = FFj(a, b, c, j) + d + ss2 + w1[j];
+ tt2 = GGj(e, f, g, j) + h + ss1 + w[j];
+ d = c;
+ c = bitCycleLeft(b, 9);
+ b = a;
+ a = tt1;
+ h = g;
+ g = bitCycleLeft(f, 19);
+ f = e;
+ e = P0(tt2);
+ }
+
+ int[] out = new int[8];
+ out[0] = a ^ V[0];
+ out[1] = b ^ V[1];
+ out[2] = c ^ V[2];
+ out[3] = d ^ V[3];
+ out[4] = e ^ V[4];
+ out[5] = f ^ V[5];
+ out[6] = g ^ V[6];
+ out[7] = h ^ V[7];
+
+ return out;
+ }
+
+ private static int[][] expand(int[] B) {
+ int W[] = new int[68];
+ int W1[] = new int[64];
+ for (int i = 0; i < B.length; i++) {
+ W[i] = B[i];
+ }
+
+ for (int i = 16; i < 68; i++) {
+ W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15)) ^ bitCycleLeft(W[i - 13], 7)
+ ^ W[i - 6];
+ }
+
+ for (int i = 0; i < 64; i++) {
+ W1[i] = W[i] ^ W[i + 4];
+ }
+
+ int arr[][] = new int[][] { W, W1 };
+ return arr;
+ }
+
+ private static byte[] bigEndianIntToByte(int num) {
+ return back(SM2Util.intToBytes(num));
+ }
+
+ private static int bigEndianByteToInt(byte[] bytes) {
+ return SM2Util.byteToInt(back(bytes));
+ }
+
+ private static int FFj(int X, int Y, int Z, int j) {
+ if (j >= 0 && j <= 15) {
+ return FF1j(X, Y, Z);
+ } else {
+ return FF2j(X, Y, Z);
+ }
+ }
+
+ private static int GGj(int X, int Y, int Z, int j) {
+ if (j >= 0 && j <= 15) {
+ return GG1j(X, Y, Z);
+ } else {
+ return GG2j(X, Y, Z);
+ }
+ }
+
+ // 逻辑位运算函数
+ private static int FF1j(int X, int Y, int Z) {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int FF2j(int X, int Y, int Z) {
+ int tmp = ((X & Y) | (X & Z) | (Y & Z));
+ return tmp;
+ }
+
+ private static int GG1j(int X, int Y, int Z) {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int GG2j(int X, int Y, int Z) {
+ int tmp = (X & Y) | (~X & Z);
+ return tmp;
+ }
+
+ private static int P0(int X) {
+ int y = rotateLeft(X, 9);
+ y = bitCycleLeft(X, 9);
+ int z = rotateLeft(X, 17);
+ z = bitCycleLeft(X, 17);
+ int t = X ^ y ^ z;
+ return t;
+ }
+
+ private static int P1(int X) {
+ int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23);
+ return t;
+ }
+
+ /**
+ * 对最后一个分组字节数据padding
+ *
+ * @param in
+ * @param bLen 分组个数
+ * @return
+ */
+ public static byte[] padding(byte[] in, int bLen) {
+ int k = 448 - (8 * in.length + 1) % 512;
+ if (k < 0) {
+ k = 960 - (8 * in.length + 1) % 512;
+ }
+ k += 1;
+ byte[] padd = new byte[k / 8];
+ padd[0] = (byte) 0x80;
+ long n = in.length * 8 + bLen * 512;
+ byte[] out = new byte[in.length + k / 8 + 64 / 8];
+ int pos = 0;
+ System.arraycopy(in, 0, out, 0, in.length);
+ pos += in.length;
+ System.arraycopy(padd, 0, out, pos, padd.length);
+ pos += padd.length;
+ byte[] tmp = back(SM2Util.longToBytes(n));
+ System.arraycopy(tmp, 0, out, pos, tmp.length);
+ return out;
+ }
+
+ /**
+ * 字节数组逆序
+ *
+ * @param in
+ * @return
+ */
+ private static byte[] back(byte[] in) {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = in[out.length - i - 1];
+ }
+
+ return out;
+ }
+
+ public static int rotateLeft(int x, int n) {
+ return (x << n) | (x >> (32 - n));
+ }
+
+ private static int bitCycleLeft(int n, int bitLen) {
+ bitLen %= 32;
+ byte[] tmp = bigEndianIntToByte(n);
+ int byteLen = bitLen / 8;
+ int len = bitLen % 8;
+ if (byteLen > 0) {
+ tmp = byteCycleLeft(tmp, byteLen);
+ }
+
+ if (len > 0) {
+ tmp = bitSmall8CycleLeft(tmp, len);
+ }
+
+ return bigEndianByteToInt(tmp);
+ }
+
+ private static byte[] bitSmall8CycleLeft(byte[] in, int len) {
+ byte[] tmp = new byte[in.length];
+ int t1, t2, t3;
+ for (int i = 0; i < tmp.length; i++) {
+ t1 = (byte) ((in[i] & 0x000000ff) << len);
+ t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len));
+ t3 = (byte) (t1 | t2);
+ tmp[i] = (byte) t3;
+ }
+
+ return tmp;
+ }
+
+ private static byte[] byteCycleLeft(byte[] in, int byteLen) {
+ byte[] tmp = new byte[in.length];
+ System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen);
+ System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen);
+ return tmp;
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3Digest.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3Digest.java
new file mode 100644
index 00000000..9fdc3f23
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SM3Digest.java
@@ -0,0 +1,128 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import org.bouncycastle.util.encoders.Hex;
+
+public class SM3Digest {
+
+ /** SM3值的长度 */
+ private static final int BYTE_LENGTH = 32;
+
+ /** SM3分组长度 */
+ private static final int BLOCK_LENGTH = 64;
+
+ /** 缓冲区长度 */
+ private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1;
+
+ /** 缓冲区 */
+ private byte[] xBuf = new byte[BUFFER_LENGTH];
+
+ /** 缓冲区偏移量 */
+ private int xBufOff;
+
+ /** 初始向量 */
+ private byte[] V = SM3.iv.clone();
+
+ private int cntBlock = 0;
+
+ public SM3Digest() {
+ }
+
+ public SM3Digest(SM3Digest t) {
+ System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length);
+ this.xBufOff = t.xBufOff;
+ System.arraycopy(t.V, 0, this.V, 0, t.V.length);
+ }
+
+ /**
+ * SM3结果输出
+ *
+ * @param out 保存SM3结构的缓冲区
+ * @param outOff 缓冲区偏移量
+ * @return
+ */
+ public int doFinal(byte[] out, int outOff) {
+ byte[] tmp = doFinal();
+ System.arraycopy(tmp, 0, out, 0, tmp.length);
+ return BYTE_LENGTH;
+ }
+
+ public void reset() {
+ xBufOff = 0;
+ cntBlock = 0;
+ V = SM3.iv.clone();
+ }
+
+ /**
+ * 明文输入
+ *
+ * @param in 明文输入缓冲区
+ * @param inOff 缓冲区偏移量
+ * @param len 明文长度
+ */
+ public void update(byte[] in, int inOff, int len) {
+ int partLen = BUFFER_LENGTH - xBufOff;
+ int inputLen = len;
+ int dPos = inOff;
+ if (partLen < inputLen) {
+ System.arraycopy(in, dPos, xBuf, xBufOff, partLen);
+ inputLen -= partLen;
+ dPos += partLen;
+ doUpdate();
+ while (inputLen > BUFFER_LENGTH) {
+ System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH);
+ inputLen -= BUFFER_LENGTH;
+ dPos += BUFFER_LENGTH;
+ doUpdate();
+ }
+ }
+
+ System.arraycopy(in, dPos, xBuf, xBufOff, inputLen);
+ xBufOff += inputLen;
+ }
+
+ private void doUpdate() {
+ byte[] B = new byte[BLOCK_LENGTH];
+ for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) {
+ System.arraycopy(xBuf, i, B, 0, B.length);
+ doHash(B);
+ }
+ xBufOff = 0;
+ }
+
+ private void doHash(byte[] B) {
+ byte[] tmp = SM3.CF(V, B);
+ System.arraycopy(tmp, 0, V, 0, V.length);
+ cntBlock++;
+ }
+
+ private byte[] doFinal() {
+ byte[] B = new byte[BLOCK_LENGTH];
+ byte[] buffer = new byte[xBufOff];
+ System.arraycopy(xBuf, 0, buffer, 0, buffer.length);
+ byte[] tmp = SM3.padding(buffer, cntBlock);
+ for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) {
+ System.arraycopy(tmp, i, B, 0, B.length);
+ doHash(B);
+ }
+ return V;
+ }
+
+ public void update(byte in) {
+ byte[] buffer = new byte[] { in };
+ update(buffer, 0, 1);
+ }
+
+ public int getDigestSize() {
+ return BYTE_LENGTH;
+ }
+
+ public static void main(String[] args) {
+ byte[] md = new byte[32];
+ byte[] msg1 = "ererfeiisgod".getBytes();
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ System.out.println(s);
+ }
+}
diff --git a/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SPDBSMEncryptor.java b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SPDBSMEncryptor.java
new file mode 100644
index 00000000..17a036c1
--- /dev/null
+++ b/base-buildpackage/src/main/java/com/hzya/frame/finance/utils/pufabank/SPDBSMEncryptor.java
@@ -0,0 +1,171 @@
+package com.hzya.frame.finance.utils.pufabank;
+
+import okhttp3.*;
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
+
+import javax.xml.bind.DatatypeConverter;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
+
+public class SPDBSMEncryptor {
+
+ /*ClientID和密钥:X-SPDB-ClientID、X-SPDB-ClientID-Secret
+ * 浦发API开发平台上创建APP给您邮件返回的,或我们提供给您测试使用的
+ * 生产请使用API开发平台上创建APP给您邮件返回的生产环境密钥
+ */
+ private static final String clientId = "f42d86e9-debd-4423-84af-74549a51c7c2";
+ //加解密样例仅供参考,秘钥有过期时间,建议在生产环境不要将秘钥硬编码在代码中
+ private static final String secret = "NmZkMC00ZDMpLWFyYzgtN2JwN2ZmY2J1NmF4MC40NzQ0ODA5MzA2MzE4OTg2NzAu";
+
+ /*
+ *请求头ContentType,一般为application/json,API对外接口文档中有报文体格式
+ *图文上传接口为form-data,如果是图文信息上传,请参考国密普通验签图片上传接口测试工程.zip
+ */
+ private static final String contentType = "application/json;charset=utf-8";
+
+ /*您的私钥,测试环境中请将您对应的公钥请提供给我们,我们需要在门户配置,否则无法通过验签
+ * 公私钥生成可进入本项目下nodejs文件夹下运行 node getKeyPaireHex.js 即可生成公私密钥
+ * 加解密样例仅供参考,秘钥有过期时间,建议在生产环境不要将秘钥硬编码在代码中
+ */
+ private static final String privatekey = "ae14b3717d6b44b55e500a807248aab425e1d1677df1718410c453f3fe257541";
+
+ /*
+ *浦发公钥,在用户中心我的APP开发者公钥中查看
+ *加解密样例仅供参考,秘钥有过期时间,建议在生产环境不要将秘钥硬编码在代码中
+ */
+ private static final String spdb_publicKey = "04e63345389de1dbc822881f80c2a82e95f638dd9881ebea8bb63a00ba5580559f77db5cc08adc087428fdbab81fe4dc580d48735cd111bdc9ae898f7b57628f09";
+
+ /**
+ * 请求编码格式
+ */
+ private static final String charset = "UTF-8";
+
+
+ private static final OkHttpClient client = new OkHttpClient.Builder()
+ .connectionPool(new ConnectionPool(500, 5, TimeUnit.MINUTES)).connectTimeout(10000, TimeUnit.MILLISECONDS)
+ .readTimeout(60000, TimeUnit.MILLISECONDS).build();
+
+
+ /**
+ * 获取防重放参数
+ * @param forbidden 是否需使用防重放参数
+ * @param data 报文体
+ * @return
+ */
+ public static String getNonceParam( boolean forbidden ,String data){
+ //防重放参数。毫秒级的时间戳,与浦发服务器(北京时间)时间误差不能超过15分钟。样例:1543461367533
+ long timestamp = 0L;
+ //防重放参数。X-SPDB-Nonce为交易序号,需保证唯一。样例:TRANS10145581
+ String nonce = "";
+ /*
+ * 防重放参数
+ * 如需使用防重放参数,需要对‘时间戳’和‘防重放参数’加签
+ */
+ String newBodyData = data;
+
+ if (forbidden) {//如需使用防重放参数
+ //对应请求报文头参数 X-SPDB-Timestamp
+ timestamp = System.currentTimeMillis();// X-SPDB-Timestamp
+ //对应请求报文头参数 X-SPDB-Nonce
+ nonce = "TRAN10145581";// X-SPDB-Nonce
+ //注意:签名原文需要拼接防重放参数 例:{"name":"zhangshan"}1591077686410TRAN10145581
+ newBodyData = data + timestamp + nonce;
+ System.out.println("签名原文:" + newBodyData);
+ }
+ return newBodyData;
+ }
+
+
+ public static Request createRequest(String requestMethod,String url,String data,String signature){
+ Request request =null;
+ String encrypted = SM2Cryptor.encrypt(secret, data);
+ if("GET".equals(requestMethod) || "get".equals(requestMethod)){
+ url = url + "?encryptBody=" + encrypted;
+ request = new Request.Builder().url(url).get()
+ .addHeader("Content-Type", contentType) //Content-Type
+ .addHeader("X-SPDB-Client-ID", clientId) //Client-ID
+ .addHeader("X-SPDB-SIGNATURE", signature) //签名结果
+ .addHeader("X-SPDB-SM", "true") //使用国密
+ .addHeader("X-SPDB-Encryption", "true") //使用全报文加密
+ //X-SPDB-Timestamp、X-SPDB-Nonce两个参数为防重放参数,不需要时不传
+ //.addHeader("X-SPDB-Timestamp", String.valueOf(timestamp)) //防重放参数-毫秒级时间戳
+ //.addHeader("X-SPDB-Nonce", nonce) //防重放参数
+ .build();
+
+ }else if("POST".equals(requestMethod) || "post".equals(requestMethod)){
+ //发送HTTP请求
+ MediaType mediaType = MediaType.parse(contentType);
+ // 数据加密-使用国密SM2算法加密 注意加密数据不添加防重放参数
+ RequestBody body = RequestBody.create(mediaType, encrypted);
+ request = new Request.Builder().url(url).post(body)
+ .addHeader("Content-Type", contentType) //Content-Type
+ .addHeader("X-SPDB-Client-ID", clientId) //Client-ID
+ .addHeader("X-SPDB-SIGNATURE", signature) //签名结果
+ .addHeader("X-SPDB-SM", "true") //使用国密
+ .addHeader("X-SPDB-Encryption", "true") //使用全报文加密
+ //X-SPDB-Timestamp、X-SPDB-Nonce两个参数为防重放参数,不需要时不传
+ //.addHeader("X-SPDB-Timestamp", String.valueOf(timestamp)) //防重放参数-毫秒级时间戳
+ //.addHeader("X-SPDB-Nonce", nonce) //防重放参数
+ .build();
+ }
+ return request;
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ try {
+ /**
+ * 请求的URL,请使用对API外接口文档中提供的URL
+ */
+ String url = "https://etest4.spdb.com.cn/spdb/uat/api/corporateAccounts/banks/transDetails";
+ /*
+ * 请求报文体 {"name":"zhangshan"} 注意换行符和空格
+ * 如果是图文信息上传,请参考国密普通验签图片上传接口测试工程.zip
+ */
+ String data = "{\"clientNo\": \"2678987519\",\"pyAcctNo\": \"88010078801000025122\",\"startDate\":\"20250801\",\"endDate\": \"20250831\",\"acptNum\": \"1\",\"queryNum\": \"10\"}";
+ System.out.println("原报文体:" + data);
+ /**
+ * 是否获取防重放参数
+ */
+ String newBodyData = getNonceParam(false, data);
+
+ // 数据加密-使用国密SM2算法加密 注意加密数据不添加防重放参数
+ String encrypted = SM2Cryptor.encrypt(secret, data);
+ System.out.println("加密报文体:" + encrypted);
+ // 数据解密
+ String decrypted = SM2Cryptor.decrypt(secret, encrypted);
+ System.out.println("解密报文体:" + decrypted);
+
+ byte[] dataBytes = newBodyData.getBytes(charset);
+
+ SM2Sign sign = SM2SignVerify.sign(ByteUtils.fromHexString(privatekey), dataBytes);
+ byte[] signBytes = sign.getSm2_sign().getBytes(charset);
+ String signature = DatatypeConverter.printBase64Binary(signBytes);
+ System.out.println("signature:" + signature);
+
+ //发送HTTP请求
+ Request request = createRequest("post", url, data, signature);
+ Response response = client.newCall(request).execute();
+
+ String resBody = new String(response.body().bytes(), "UTF-8");
+ System.out.println("加密响应报文:" + resBody);
+
+ String decryptBody = SM2Cryptor.decrypt(secret, resBody);
+ System.out.println("明文响应报文:" + decryptBody);
+
+ String sg = new String(response.header("X-SPDB-SIGNATURE").getBytes(), "UTF-8");
+
+ String digest = SHA1.digest(decryptBody);
+ byte[] bytes = digest.getBytes(charset);
+
+ byte[] signatureBytes = ByteUtils.fromHexString(new String(DatatypeConverter.parseBase64Binary(sg)));
+ SM2Sign sm2Sign = SM2SignVerify.validateSign(ByteUtils.fromHexString(spdb_publicKey), bytes, signatureBytes);
+
+ boolean validateSign = sm2Sign.isVerify();
+ System.out.println("验签结果:" + validateSign);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/base-buildpackage/src/main/resources/lib/bcmail-jdk15on-1.56.jar b/base-buildpackage/src/main/resources/lib/bcmail-jdk15on-1.56.jar
new file mode 100644
index 00000000..2d27a4d8
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/bcmail-jdk15on-1.56.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/bcpkix-jdk15on-1.57.jar b/base-buildpackage/src/main/resources/lib/bcpkix-jdk15on-1.57.jar
new file mode 100644
index 00000000..5ce7d5c5
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/bcpkix-jdk15on-1.57.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/bcprov-jdk15on-1.57.jar b/base-buildpackage/src/main/resources/lib/bcprov-jdk15on-1.57.jar
new file mode 100644
index 00000000..5a10986b
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/bcprov-jdk15on-1.57.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/commons-codec-1.9.jar b/base-buildpackage/src/main/resources/lib/commons-codec-1.9.jar
new file mode 100644
index 00000000..ef35f1c5
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/commons-codec-1.9.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/commons-lang3-3.9.jar b/base-buildpackage/src/main/resources/lib/commons-lang3-3.9.jar
new file mode 100644
index 00000000..0d896939
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/commons-lang3-3.9.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/hutool-all-3.0.9.jar b/base-buildpackage/src/main/resources/lib/hutool-all-3.0.9.jar
new file mode 100644
index 00000000..9e9bcce2
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/hutool-all-3.0.9.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/okhttp-3.3.0.jar b/base-buildpackage/src/main/resources/lib/okhttp-3.3.0.jar
new file mode 100644
index 00000000..cbcfa64b
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/okhttp-3.3.0.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/okio-1.8.0.jar b/base-buildpackage/src/main/resources/lib/okio-1.8.0.jar
new file mode 100644
index 00000000..bb70f758
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/okio-1.8.0.jar differ
diff --git a/base-buildpackage/src/main/resources/lib/spdb-api-1.0-SNAPSHOT.jar b/base-buildpackage/src/main/resources/lib/spdb-api-1.0-SNAPSHOT.jar
new file mode 100644
index 00000000..75865b95
Binary files /dev/null and b/base-buildpackage/src/main/resources/lib/spdb-api-1.0-SNAPSHOT.jar differ