diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f1a63c30..f48d23439 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,13 @@
* 【crypto 】 PemUtil.readPemKey支持EC(pr#1366@Github)
* 【extra 】 Ftp等cd方法增加同步(issue#1397@Github)
* 【core 】 StrUtil增加endWithAnyIgnoreCase(issue#I37I0B@Gitee)
+* 【crypto 】 Sm2增加getD和getQ方法(issue#I37Z4C@Gitee)
### Bug修复
* 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题
* 【json 】 修复SQLEXception导致的栈溢出(issue#1399@Github)
* 【extra 】 修复Ftp中异常参数没有传入问题(issue#1397@Github)
+* 【crypto 】 修复Sm2使用D构造空指针问题(issue#I37Z4C@Gitee)
-------------------------------------------------------------------------------------------------------------
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/HexUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/HexUtil.java
index 5872918eb..cf59864ed 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/HexUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/HexUtil.java
@@ -26,7 +26,7 @@ public class HexUtil {
/**
* 判断给定字符串是否为16进制数
- * 如果是,需要使用对应数字类型对象的decode
方法解码
+ * 如果是,需要使用对应数字类型对象的{@code decode}方法解码
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
*
* @param value 值
@@ -74,7 +74,7 @@ public class HexUtil {
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
- * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @param toLowerCase {@code true} 传换成小写格式 , {@code false} 传换成大写格式
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
@@ -116,7 +116,7 @@ public class HexUtil {
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
- * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @param toLowerCase {@code true} 传换成小写格式 , {@code false} 传换成大写格式
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
@@ -421,4 +421,4 @@ public class HexUtil {
return digit;
}
// ---------------------------------------------------------------------------------------- Private method end
-}
\ No newline at end of file
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java
index 412e0cca2..918b1086b 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java
@@ -41,15 +41,28 @@ public class BCUtil {
}
/**
- * 编码压缩EC公钥(基于BouncyCastle)
+ * 编码压缩EC公钥(基于BouncyCastle),即Q值
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html
*
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
- * @return 压缩得到的X
+ * @return 压缩得到的Q
* @since 4.4.4
*/
public static byte[] encodeECPublicKey(PublicKey publicKey) {
- return ((BCECPublicKey) publicKey).getQ().getEncoded(true);
+ return encodeECPublicKey(publicKey, true);
+ }
+
+ /**
+ * 编码压缩EC公钥(基于BouncyCastle),即Q值
+ * 见:https://www.cnblogs.com/xinzhao/p/8963724.html
+ *
+ * @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
+ * @param isCompressed 是否压缩
+ * @return 得到的Q
+ * @since 5.5.9
+ */
+ public static byte[] encodeECPublicKey(PublicKey publicKey, boolean isCompressed) {
+ return ((BCECPublicKey) publicKey).getQ().getEncoded(isCompressed);
}
/**
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java
index 30d5264c7..73b12a8c1 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java
@@ -87,7 +87,7 @@ public class ECKeyUtil {
* @param x 公钥X
* @param y 公钥Y
* @param domainParameters ECDomainParameters
- * @return ECPublicKeyParameters
+ * @return ECPublicKeyParameters,x或y为{@code null}则返回{@code null}
*/
public static ECPublicKeyParameters toPublicParams(String x, String y, ECDomainParameters domainParameters) {
return toPublicParams(SecureUtil.decode(x), SecureUtil.decode(y), domainParameters);
@@ -102,6 +102,9 @@ public class ECKeyUtil {
* @return ECPublicKeyParameters
*/
public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) {
+ if(null == xBytes || null == yBytes){
+ return null;
+ }
return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters);
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java
index e5a3b8cac..861768492 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java
@@ -26,7 +26,7 @@ import java.math.BigInteger;
/**
* SM国密算法工具类
- * 此工具类依赖org.bouncycastle:bcpkix-jdk15on
+ * 此工具类依赖org.bouncycastle:bcprov-jdk15to18
*
* @author looly
* @since 4.3.2
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java
index 84b0f6e18..e22c9402c 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java
@@ -471,6 +471,27 @@ public class SM2 extends AbstractAsymmetricCrypto {
return this;
}
+ /**
+ * 获得私钥D值(编码后的私钥)
+ *
+ * @return D值
+ * @since 5.5.9
+ */
+ public byte[] getD() {
+ return this.privateKeyParams.getD().toByteArray();
+ }
+
+ /**
+ * 获得公钥Q值(编码后的公钥)
+ *
+ * @param isCompressed 是否压缩
+ * @return Q值
+ * @since 5.5.9
+ */
+ public byte[] getQ(boolean isCompressed) {
+ return this.publicKeyParams.getQ().getEncoded(isCompressed);
+ }
+
// ------------------------------------------------------------------------------------------------------------------------- Private method start
/**
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/asymmetric/SM2Test.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/asymmetric/SM2Test.java
index 5d74d706f..0c4bfe152 100644
--- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/asymmetric/SM2Test.java
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/asymmetric/SM2Test.java
@@ -1,7 +1,6 @@
package cn.hutool.crypto.test.asymmetric;
import cn.hutool.core.codec.Base64;
-import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
@@ -31,7 +30,6 @@ public class SM2Test {
@Test
public void generateKeyPairTest() {
KeyPair pair = SecureUtil.generateKeyPair("SM2");
- Console.log(HexUtil.encodeHexStr(pair.getPublic().getEncoded()));
Assert.assertNotNull(pair.getPrivate());
Assert.assertNotNull(pair.getPublic());
}
@@ -114,6 +112,36 @@ public class SM2Test {
Assert.assertEquals(text.toString(), decryptStr2);
}
+ @Test
+ public void sm2SignTest(){
+ //需要签名的明文,得到明文对应的字节数组
+ byte[] dataBytes = "我是一段测试aaaa".getBytes();
+
+ //指定的私钥
+ String privateKeyHex = "1ebf8b341c695ee456fd1a41b82645724bc25d79935437d30e7e4b0a554baa5e";
+ final SM2 sm2 = new SM2(privateKeyHex, null, null);
+ sm2.usePlainEncoding();
+ byte[] sign = sm2.sign(dataBytes, null);
+ // 64位签名
+ Assert.assertEquals(64, sign.length);
+ }
+
+ @Test
+ public void sm2VerifyTest(){
+ //指定的公钥
+ String publicKeyHex = "04db9629dd33ba568e9507add5df6587a0998361a03d3321948b448c653c2c1b7056434884ab6f3d1c529501f166a336e86f045cea10dffe58aa82ea13d7253763";
+ //需要加密的明文,得到明文对应的字节数组
+ byte[] dataBytes = "我是一段测试aaaa".getBytes();
+ //签名值
+ String signHex = "2881346e038d2ed706ccdd025f2b1dafa7377d5cf090134b98756fafe084dddbcdba0ab00b5348ed48025195af3f1dda29e819bb66aa9d4d088050ff148482a1";
+
+ final SM2 sm2 = new SM2(null, ECKeyUtil.toSm2PublicParams(publicKeyHex));
+ sm2.usePlainEncoding();
+
+ boolean verify = sm2.verify(dataBytes, HexUtil.decodeHex(signHex));
+ Assert.assertTrue(verify);
+ }
+
@Test
public void sm2SignAndVerifyTest() {
String content = "我是Hanley.";
@@ -207,8 +235,12 @@ public class SM2Test {
// 生成的签名是64位
sm2.usePlainEncoding();
+
String sign = "DCA0E80A7F46C93714B51C3EFC55A922BCEF7ECF0FE9E62B53BA6A7438B543A76C145A452CA9036F3CB70D7E6C67D4D9D7FE114E5367A2F6F5A4D39F2B10F3D6";
Assert.assertTrue(sm2.verifyHex(data, sign));
+
+ String sign2 = sm2.signHex(data, id);
+ Assert.assertTrue(sm2.verifyHex(data, sign2));
}
@Test
@@ -224,7 +256,6 @@ public class SM2Test {
final SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters);
sm2.setMode(SM2Engine.Mode.C1C2C3);
final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
- Console.log(encryptHex);
final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey);
Assert.assertEquals(data, decryptStr);