mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add method
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.5.9 (2021-02-25)
|
# 5.5.9 (2021-02-26)
|
||||||
|
|
||||||
### 新特性
|
### 新特性
|
||||||
* 【crypto 】 PemUtil.readPemKey支持EC(pr#1366@Github)
|
* 【crypto 】 PemUtil.readPemKey支持EC(pr#1366@Github)
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* 【cache 】 AbstractCache增加keySet方法(issue#I37Z4C@Gitee)
|
* 【cache 】 AbstractCache增加keySet方法(issue#I37Z4C@Gitee)
|
||||||
* 【core 】 NumberWordFormatter增加formatSimple方法(pr#1436@Github)
|
* 【core 】 NumberWordFormatter增加formatSimple方法(pr#1436@Github)
|
||||||
* 【crypto 】 增加读取openSSL生成的sm2私钥
|
* 【crypto 】 增加读取openSSL生成的sm2私钥
|
||||||
|
* 【crypto 】 增加众多方法,SM2兼容各类密钥格式(issue#I37Z75@Gitee)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题
|
* 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题
|
||||||
|
@@ -11,6 +11,7 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -694,4 +695,14 @@ public class CollUtilTest {
|
|||||||
|
|
||||||
Assert.assertEquals(0, CollUtil.page(3, 5, objects).size());
|
Assert.assertEquals(0, CollUtil.page(3, 5, objects).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subtractToListTest(){
|
||||||
|
List<Long> list1 = Arrays.asList(1L, 2L, 3L);
|
||||||
|
List<Long> list2 = Arrays.asList(2L, 3L);
|
||||||
|
|
||||||
|
List<Long> result = CollUtil.subtractToList(list1, list2);
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
Assert.assertEquals(1L, result.get(0), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,11 @@ import org.bouncycastle.crypto.params.ECDomainParameters;
|
|||||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||||
|
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
|
||||||
|
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
|
||||||
import org.bouncycastle.math.ec.ECCurve;
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
||||||
import org.bouncycastle.util.BigIntegers;
|
import org.bouncycastle.util.BigIntegers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -20,6 +24,7 @@ import java.security.InvalidKeyException;
|
|||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EC密钥参数相关工具类封装
|
* EC密钥参数相关工具类封装
|
||||||
@@ -45,7 +50,21 @@ public class ECKeyUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据私钥参数获取公钥参数
|
||||||
|
*
|
||||||
|
* @param privateKeyParameters 私钥参数
|
||||||
|
* @return 公钥参数
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters getPublicParams(ECPrivateKeyParameters privateKeyParameters) {
|
||||||
|
final ECDomainParameters domainParameters = privateKeyParameters.getParameters();
|
||||||
|
final ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), privateKeyParameters.getD());
|
||||||
|
return new ECPublicKeyParameters(q, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------- Public Key
|
//--------------------------------------------------------------------------- Public Key
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 ECPublicKeyParameters
|
* 转换为 ECPublicKeyParameters
|
||||||
*
|
*
|
||||||
@@ -91,8 +110,8 @@ public class ECKeyUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为ECPublicKeyParameters
|
* 转换为ECPublicKeyParameters
|
||||||
*
|
*
|
||||||
* @param x 公钥X
|
* @param x 公钥X
|
||||||
* @param y 公钥Y
|
* @param y 公钥Y
|
||||||
* @param domainParameters ECDomainParameters
|
* @param domainParameters ECDomainParameters
|
||||||
* @return ECPublicKeyParameters,x或y为{@code null}则返回{@code null}
|
* @return ECPublicKeyParameters,x或y为{@code null}则返回{@code null}
|
||||||
*/
|
*/
|
||||||
@@ -109,7 +128,7 @@ public class ECKeyUtil {
|
|||||||
* @return ECPublicKeyParameters
|
* @return ECPublicKeyParameters
|
||||||
*/
|
*/
|
||||||
public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) {
|
public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) {
|
||||||
if(null == xBytes || null == yBytes){
|
if (null == xBytes || null == yBytes) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters);
|
return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters);
|
||||||
@@ -187,6 +206,7 @@ public class ECKeyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------- Private Key
|
//--------------------------------------------------------------------------- Private Key
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 ECPrivateKeyParameters
|
* 转换为 ECPrivateKeyParameters
|
||||||
*
|
*
|
||||||
@@ -220,7 +240,7 @@ public class ECKeyUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为 ECPrivateKeyParameters
|
* 转换为 ECPrivateKeyParameters
|
||||||
*
|
*
|
||||||
* @param d 私钥d值16进制字符串
|
* @param d 私钥d值16进制字符串
|
||||||
* @param domainParameters ECDomainParameters
|
* @param domainParameters ECDomainParameters
|
||||||
* @return ECPrivateKeyParameters
|
* @return ECPrivateKeyParameters
|
||||||
*/
|
*/
|
||||||
@@ -272,10 +292,11 @@ public class ECKeyUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将SM2算法的{@link ECPrivateKey} 转换为 {@link PrivateKey}
|
* 将SM2算法的{@link ECPrivateKey} 转换为 {@link PrivateKey}
|
||||||
|
*
|
||||||
* @param privateKey {@link ECPrivateKey}
|
* @param privateKey {@link ECPrivateKey}
|
||||||
* @return {@link PrivateKey}
|
* @return {@link PrivateKey}
|
||||||
*/
|
*/
|
||||||
public static PrivateKey toSm2PrivateKey(ECPrivateKey privateKey){
|
public static PrivateKey toSm2PrivateKey(ECPrivateKey privateKey) {
|
||||||
try {
|
try {
|
||||||
final PrivateKeyInfo info = new PrivateKeyInfo(
|
final PrivateKeyInfo info = new PrivateKeyInfo(
|
||||||
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SmUtil.ID_SM2_PUBLIC_KEY_PARAM), privateKey);
|
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SmUtil.ID_SM2_PUBLIC_KEY_PARAM), privateKey);
|
||||||
@@ -284,4 +305,92 @@ public class ECKeyUtil {
|
|||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link OpenSSHPrivateKeySpec}
|
||||||
|
*
|
||||||
|
* @param key 私钥,需为PKCS#1格式
|
||||||
|
* @return {@link OpenSSHPrivateKeySpec}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static KeySpec createOpenSSHPrivateKeySpec(byte[] key) {
|
||||||
|
return new OpenSSHPrivateKeySpec(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link OpenSSHPublicKeySpec}
|
||||||
|
*
|
||||||
|
* @param key 公钥,需为PKCS#1格式
|
||||||
|
* @return {@link OpenSSHPublicKeySpec}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static KeySpec createOpenSSHPublicKeySpec(byte[] key) {
|
||||||
|
return new OpenSSHPublicKeySpec(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试解析转换各种类型私钥为{@link ECPrivateKeyParameters},支持包括:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>D值</li>
|
||||||
|
* <li>PKCS#8</li>
|
||||||
|
* <li>PKCS#1</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param privateKeyBytes 私钥
|
||||||
|
* @return {@link ECPrivateKeyParameters}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters decodePrivateKeyParams(byte[] privateKeyBytes) {
|
||||||
|
try {
|
||||||
|
// 尝试D值
|
||||||
|
return toSm2PrivateParams(privateKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKey privateKey;
|
||||||
|
//尝试PKCS#8
|
||||||
|
try {
|
||||||
|
privateKey = KeyUtil.generatePrivateKey("sm2", privateKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 尝试PKCS#1
|
||||||
|
privateKey = KeyUtil.generatePrivateKey("sm2", createOpenSSHPrivateKeySpec(privateKeyBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return toPrivateParams(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试解析转换各种类型公钥为{@link ECPublicKeyParameters},支持包括:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Q值</li>
|
||||||
|
* <li>X.509</li>
|
||||||
|
* <li>PKCS#1</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param publicKeyBytes 公钥
|
||||||
|
* @return {@link ECPublicKeyParameters}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters decodePublicKeyParams(byte[] publicKeyBytes) {
|
||||||
|
try {
|
||||||
|
// 尝试Q值
|
||||||
|
return toSm2PublicParams(publicKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKey publicKey;
|
||||||
|
//尝试X.509
|
||||||
|
try {
|
||||||
|
publicKey = KeyUtil.generatePublicKey("sm2", publicKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 尝试PKCS#1
|
||||||
|
publicKey = KeyUtil.generatePublicKey("sm2", createOpenSSHPublicKeySpec(publicKeyBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return toPublicParams(publicKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -256,8 +256,8 @@ public class KeyUtil {
|
|||||||
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法<br>
|
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法<br>
|
||||||
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
||||||
*
|
*
|
||||||
* @param algorithm 算法
|
* @param algorithm 算法,如RSA、EC、SM2等
|
||||||
* @param key 密钥,必须为DER编码存储
|
* @param key 密钥,PKCS#8格式
|
||||||
* @return 私钥 {@link PrivateKey}
|
* @return 私钥 {@link PrivateKey}
|
||||||
*/
|
*/
|
||||||
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) {
|
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) {
|
||||||
@@ -271,7 +271,7 @@ public class KeyUtil {
|
|||||||
* 生成私钥,仅用于非对称加密<br>
|
* 生成私钥,仅用于非对称加密<br>
|
||||||
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
||||||
*
|
*
|
||||||
* @param algorithm 算法
|
* @param algorithm 算法,如RSA、EC、SM2等
|
||||||
* @param keySpec {@link KeySpec}
|
* @param keySpec {@link KeySpec}
|
||||||
* @return 私钥 {@link PrivateKey}
|
* @return 私钥 {@link PrivateKey}
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
|
@@ -3,7 +3,6 @@ package cn.hutool.crypto;
|
|||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import org.bouncycastle.asn1.sec.ECPrivateKey;
|
|
||||||
import org.bouncycastle.util.io.pem.PemObject;
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
||||||
import org.bouncycastle.util.io.pem.PemReader;
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
@@ -134,14 +133,17 @@ public class PemUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取OpenSSL生成的ANS1格式的Pem私钥文件
|
* 读取OpenSSL生成的ANS1格式的Pem私钥文件,必须为PKCS#1格式
|
||||||
*
|
*
|
||||||
* @param keyStream 私钥pem流
|
* @param keyStream 私钥pem流
|
||||||
* @return {@link PrivateKey}
|
* @return {@link PrivateKey}
|
||||||
*/
|
*/
|
||||||
public static PrivateKey readSm2PemPrivateKey(InputStream keyStream) {
|
public static PrivateKey readSm2PemPrivateKey(InputStream keyStream) {
|
||||||
final ECPrivateKey ecPrivateKey = ECPrivateKey.getInstance(readPem(keyStream));
|
try{
|
||||||
return ECKeyUtil.toSm2PrivateKey(ecPrivateKey);
|
return KeyUtil.generatePrivateKey("sm2", ECKeyUtil.createOpenSSHPrivateKeySpec(readPem(keyStream)));
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(keyStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -14,6 +14,8 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
|||||||
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
||||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
|
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
|
||||||
import org.bouncycastle.util.Arrays;
|
import org.bouncycastle.util.Arrays;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
@@ -22,11 +24,20 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM国密算法工具类<br>
|
* SM国密算法工具类<br>
|
||||||
* 此工具类依赖org.bouncycastle:bcprov-jdk15to18
|
* 此工具类依赖org.bouncycastle:bcprov-jdk15to18
|
||||||
*
|
*
|
||||||
|
* <p>封装包括:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>SM2 椭圆曲线非对称加密和签名</li>
|
||||||
|
* <li>SM3 杂凑算法</li>
|
||||||
|
* <li>SM4 对称加密</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 4.3.2
|
* @since 4.3.2
|
||||||
*/
|
*/
|
||||||
@@ -74,14 +85,42 @@ public class SmUtil {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKey 私钥
|
* @param privateKey 私钥,必须使用PKCS#8规范
|
||||||
* @param publicKey 公钥
|
* @param publicKey 公钥,必须使用X509规范
|
||||||
* @return {@link SM2}
|
* @return {@link SM2}
|
||||||
*/
|
*/
|
||||||
public static SM2 sm2(byte[] privateKey, byte[] publicKey) {
|
public static SM2 sm2(byte[] privateKey, byte[] publicKey) {
|
||||||
return new SM2(privateKey, publicKey);
|
return new SM2(privateKey, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SM2算法对象<br>
|
||||||
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
|
*
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return {@link SM2}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static SM2 sm2(PrivateKey privateKey, PublicKey publicKey) {
|
||||||
|
return new SM2(privateKey, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SM2算法对象<br>
|
||||||
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
|
*
|
||||||
|
* @param privateKeyParams 私钥参数
|
||||||
|
* @param publicKeyParams 公钥参数
|
||||||
|
* @return {@link SM2}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static SM2 sm2(ECPrivateKeyParameters privateKeyParams, ECPublicKeyParameters publicKeyParams) {
|
||||||
|
return new SM2(privateKeyParams, publicKeyParams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM3加密<br>
|
* SM3加密<br>
|
||||||
* 例:<br>
|
* 例:<br>
|
||||||
@@ -192,8 +231,7 @@ public class SmUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s<br>
|
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
|
||||||
* 来自:https://blog.csdn.net/pridas/article/details/86118774
|
|
||||||
*
|
*
|
||||||
* @param rsDer rs in asn1 format
|
* @param rsDer rs in asn1 format
|
||||||
* @return sign result in plain byte array
|
* @return sign result in plain byte array
|
||||||
@@ -214,8 +252,7 @@ public class SmUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式<br>
|
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
|
||||||
* 来自:https://blog.csdn.net/pridas/article/details/86118774
|
|
||||||
*
|
*
|
||||||
* @param sign in plain byte array
|
* @param sign in plain byte array
|
||||||
* @return rs result in asn1 format
|
* @return rs result in asn1 format
|
||||||
|
@@ -4,7 +4,7 @@ import cn.hutool.core.lang.Assert;
|
|||||||
import cn.hutool.core.util.HexUtil;
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.crypto.BCUtil;
|
import cn.hutool.crypto.BCUtil;
|
||||||
import cn.hutool.crypto.CryptoException;
|
import cn.hutool.crypto.CryptoException;
|
||||||
import cn.hutool.crypto.KeyUtil;
|
import cn.hutool.crypto.ECKeyUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
import org.bouncycastle.crypto.CipherParameters;
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
@@ -74,13 +74,13 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKey 私钥,必须使用PKCS#8规范
|
* @param privateKey 私钥,可以使用PKCS#8、D值或PKCS#1规范
|
||||||
* @param publicKey 公钥,必须使用X509规范
|
* @param publicKey 公钥,可以使用X509、Q值或PKCS#1规范
|
||||||
*/
|
*/
|
||||||
public SM2(byte[] privateKey, byte[] publicKey) {
|
public SM2(byte[] privateKey, byte[] publicKey) {
|
||||||
this(//
|
this(
|
||||||
KeyUtil.generatePrivateKey(ALGORITHM_SM2, privateKey), //
|
ECKeyUtil.decodePrivateKeyParams(privateKey),
|
||||||
KeyUtil.generatePublicKey(ALGORITHM_SM2, publicKey)//
|
ECKeyUtil.decodePublicKeyParams(publicKey)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.crypto.digest;
|
package cn.hutool.crypto.digest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM3算法
|
* SM3杂凑算法
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 4.6.8
|
* @since 4.6.8
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
package cn.hutool.crypto.test;
|
package cn.hutool.crypto.test;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.resource.ResourceUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.crypto.PemUtil;
|
import cn.hutool.crypto.PemUtil;
|
||||||
import cn.hutool.crypto.asymmetric.KeyType;
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
import cn.hutool.crypto.asymmetric.RSA;
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
import cn.hutool.crypto.asymmetric.SM2;
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -58,4 +61,20 @@ public class PemUtilTest {
|
|||||||
// 64位签名
|
// 64位签名
|
||||||
Assert.assertEquals(64, sign.length);
|
Assert.assertEquals(64, sign.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void readECPrivateKeyTest2() {
|
||||||
|
// https://gitee.com/loolly/hutool/issues/I37Z75
|
||||||
|
byte[] d = PemUtil.readPem(FileUtil.getInputStream("d:/test/keys/priv.key"));
|
||||||
|
byte[] publicKey = PemUtil.readPem(FileUtil.getInputStream("d:/test/keys/pub.key"));
|
||||||
|
|
||||||
|
SM2 sm2 = new SM2(d, publicKey);
|
||||||
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
|
String content = "我是Hanley.";
|
||||||
|
byte[] sign = sm2.sign(StrUtil.utf8Bytes(content));
|
||||||
|
boolean verify = sm2.verify(StrUtil.utf8Bytes(content), sign);
|
||||||
|
Assert.assertTrue(verify);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@ import cn.hutool.crypto.asymmetric.KeyType;
|
|||||||
import cn.hutool.crypto.asymmetric.SM2;
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ public class SM2Test {
|
|||||||
//签名值
|
//签名值
|
||||||
String signHex = "2881346e038d2ed706ccdd025f2b1dafa7377d5cf090134b98756fafe084dddbcdba0ab00b5348ed48025195af3f1dda29e819bb66aa9d4d088050ff148482a1";
|
String signHex = "2881346e038d2ed706ccdd025f2b1dafa7377d5cf090134b98756fafe084dddbcdba0ab00b5348ed48025195af3f1dda29e819bb66aa9d4d088050ff148482a1";
|
||||||
|
|
||||||
final SM2 sm2 = new SM2(null, ECKeyUtil.toSm2PublicParams(publicKeyHex));
|
final SM2 sm2 = new SM2(null, publicKeyHex);
|
||||||
sm2.usePlainEncoding();
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
boolean verify = sm2.verify(dataBytes, HexUtil.decodeHex(signHex));
|
boolean verify = sm2.verify(dataBytes, HexUtil.decodeHex(signHex));
|
||||||
@@ -251,10 +251,7 @@ public class SM2Test {
|
|||||||
|
|
||||||
String data = "123456";
|
String data = "123456";
|
||||||
|
|
||||||
final ECPublicKeyParameters ecPublicKeyParameters = ECKeyUtil.toSm2PublicParams(q);
|
final SM2 sm2 = new SM2(d, q);
|
||||||
final ECPrivateKeyParameters ecPrivateKeyParameters = ECKeyUtil.toSm2PrivateParams(d);
|
|
||||||
|
|
||||||
final SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters);
|
|
||||||
sm2.setMode(SM2Engine.Mode.C1C2C3);
|
sm2.setMode(SM2Engine.Mode.C1C2C3);
|
||||||
final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
|
final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
|
||||||
final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey);
|
final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey);
|
||||||
@@ -277,8 +274,41 @@ public class SM2Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test(){
|
public void getPublicKeyByPrivateKeyTest(){
|
||||||
String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9yQNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
// issue#I38SDP,openSSL生成的PKCS#1格式私钥
|
||||||
|
String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9y" +
|
||||||
|
"QNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
|
|
||||||
|
PrivateKey privateKey = KeyUtil.generatePrivateKey("sm2", new OpenSSHPrivateKeySpec(SecureUtil.decode(priKey)));
|
||||||
|
final ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toPrivateParams(privateKey);
|
||||||
|
|
||||||
|
final SM2 sm2 = new SM2(privateKeyParameters, ECKeyUtil.getPublicParams(privateKeyParameters));
|
||||||
|
|
||||||
|
String src = "Sm2Test";
|
||||||
|
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
||||||
|
byte[] sign = sm2.sign(src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
Assert.assertTrue(sm2.verify( src.getBytes(StandardCharsets.UTF_8), sign));
|
||||||
|
|
||||||
|
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
||||||
|
Assert.assertArrayEquals(dec, src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readPublicKeyTest(){
|
||||||
|
String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9y" +
|
||||||
|
"QNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
String pubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAESkOzNigIsH5ehFvr9yQNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
String pubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAESkOzNigIsH5ehFvr9yQNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
|
|
||||||
|
SM2 sm2 = SmUtil.sm2(priKey, pubKey);
|
||||||
|
|
||||||
|
String src = "Sm2Test中文";
|
||||||
|
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
||||||
|
byte[] sign = sm2.sign(src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
Assert.assertTrue(sm2.verify( src.getBytes(StandardCharsets.UTF_8), sign));
|
||||||
|
|
||||||
|
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
||||||
|
Assert.assertArrayEquals(dec, src.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user