diff --git a/CHANGELOG.md b/CHANGELOG.md index e25e441b7..0321de887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * 【crypto 】 Sm2增加getD和getQ方法(issue#I37Z4C@Gitee) * 【cache 】 AbstractCache增加keySet方法(issue#I37Z4C@Gitee) * 【core 】 NumberWordFormatter增加formatSimple方法(pr#1436@Github) +* 【crypto 】 增加读取openSSL生成的sm2私钥 ### Bug修复 * 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题 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 73b12a8c1..055d0bf57 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java @@ -1,5 +1,11 @@ package cn.hutool.crypto; +import cn.hutool.core.io.IORuntimeException; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.sec.ECPrivateKey; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; @@ -8,6 +14,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.util.BigIntegers; +import java.io.IOException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.Key; @@ -262,4 +269,19 @@ public class ECKeyUtil { throw new CryptoException(e); } } + + /** + * 将SM2算法的{@link ECPrivateKey} 转换为 {@link PrivateKey} + * @param privateKey {@link ECPrivateKey} + * @return {@link PrivateKey} + */ + public static PrivateKey toSm2PrivateKey(ECPrivateKey privateKey){ + try { + final PrivateKeyInfo info = new PrivateKeyInfo( + new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SmUtil.ID_SM2_PUBLIC_KEY_PARAM), privateKey); + return KeyUtil.generatePrivateKey("SM2", info.getEncoded(ASN1Encoding.DER)); + } catch (IOException e) { + throw new IORuntimeException(e); + } + } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java index e9271929b..5d9cb4b4f 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java @@ -3,6 +3,7 @@ package cn.hutool.crypto; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; 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.PemObjectGenerator; import org.bouncycastle.util.io.pem.PemReader; @@ -159,4 +160,15 @@ public class PemUtil { IoUtil.close(writer); } } + + /** + * 读取OpenSSL生成的ANS1格式的Pem私钥文件 + * + * @param keyStream 私钥pem流 + * @return {@link PrivateKey} + */ + public static PrivateKey readSm2PemPrivateKey(InputStream keyStream){ + final ECPrivateKey ecPrivateKey = ECPrivateKey.getInstance(readPem(keyStream)); + return ECKeyUtil.toSm2PrivateKey(ecPrivateKey); + } } 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 49526f6ed..2f8d61b7f 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java @@ -10,6 +10,7 @@ import cn.hutool.crypto.digest.mac.BCHMacEngine; import cn.hutool.crypto.digest.mac.MacEngine; import cn.hutool.crypto.symmetric.SM4; import cn.hutool.crypto.symmetric.SymmetricCrypto; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.params.ECDomainParameters; @@ -31,6 +32,7 @@ import java.math.BigInteger; */ public class SmUtil { + private final static int RS_LEN = 32; /** * SM2默认曲线 */ @@ -38,13 +40,11 @@ public class SmUtil { /** * SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper) */ - public static final ECDomainParameters SM2_DOMAIN_PARAMS; - - private final static int RS_LEN = 32; - - static { - SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName(SM2_CURVE_NAME)); - } + public static final ECDomainParameters SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName(SM2_CURVE_NAME)); + /** + * SM2国密算法公钥参数的Oid标识 + */ + public static final ASN1ObjectIdentifier ID_SM2_PUBLIC_KEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301"); /** * 创建SM2算法对象
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/PemUtilTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/PemUtilTest.java index 86f8a578c..637c09c6f 100644 --- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/PemUtilTest.java +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/PemUtilTest.java @@ -4,10 +4,11 @@ import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.crypto.PemUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.asymmetric.SM2; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.PublicKey; @@ -45,9 +46,16 @@ public class PemUtilTest { } @Test - @Ignore public void readECPrivateKeyTest() { - PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_ec_private_key.pem")); - Assert.assertNotNull(privateKey); + PrivateKey privateKey = PemUtil.readSm2PemPrivateKey(ResourceUtil.getStream("test_ec_private_key.pem")); + SM2 sm2 = new SM2(privateKey, null); + sm2.usePlainEncoding(); + + //需要签名的明文,得到明文对应的字节数组 + byte[] dataBytes = "我是一段测试aaaa".getBytes(StandardCharsets.UTF_8); + + byte[] sign = sm2.sign(dataBytes, null); + // 64位签名 + Assert.assertEquals(64, sign.length); } }