Browse Source

fix sm2 bugs

Looly 4 years ago
parent
commit
3bc5054a9a

+ 2 - 0
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)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 4 - 4
hutool-core/src/main/java/cn/hutool/core/util/HexUtil.java

@@ -26,7 +26,7 @@ public class HexUtil {
 
 	/**
 	 * 判断给定字符串是否为16进制数<br>
-	 * 如果是,需要使用对应数字类型对象的<code>decode</code>方法解码<br>
+	 * 如果是,需要使用对应数字类型对象的{@code decode}方法解码<br>
 	 * 例如:{@code Integer.decode}方法解码int类型的16进制数字
 	 *
 	 * @param value 值
@@ -74,7 +74,7 @@ public class HexUtil {
 	 * 将字节数组转换为十六进制字符数组
 	 *
 	 * @param data        byte[]
-	 * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
+	 * @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 <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
+	 * @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
-}
+}

+ 16 - 3
hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java

@@ -41,15 +41,28 @@ public class BCUtil {
 	}
 
 	/**
-	 * 编码压缩EC公钥(基于BouncyCastle)<br>
+	 * 编码压缩EC公钥(基于BouncyCastle),即Q值<br>
 	 * 见: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值<br>
+	 * 见: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);
 	}
 
 	/**

+ 4 - 1
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);
 	}
 

+ 1 - 1
hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java

@@ -26,7 +26,7 @@ import java.math.BigInteger;
 
 /**
  * SM国密算法工具类<br>
- * 此工具类依赖org.bouncycastle:bcpkix-jdk15on
+ * 此工具类依赖org.bouncycastle:bcprov-jdk15to18
  *
  * @author looly
  * @since 4.3.2

+ 21 - 0
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java

@@ -471,6 +471,27 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 		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
 
 	/**

+ 34 - 3
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());
 	}
@@ -115,6 +113,36 @@ public class SM2Test {
 	}
 
 	@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);