From 58b1cae32068626620203b7fe1ebd595d56aea75 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 13 Sep 2020 03:43:33 +0800 Subject: [PATCH] add ECKeyUtil --- CHANGELOG.md | 1 + .../main/java/cn/hutool/crypto/BCUtil.java | 94 +------ .../main/java/cn/hutool/crypto/ECKeyUtil.java | 244 ++++++++++++++++++ .../crypto/test/asymmetric/SM2Test.java | 20 ++ 4 files changed, 272 insertions(+), 87 deletions(-) create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a679089..e711cc8ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * 【setting】 Setting增加store无参方法(issue#1072@Github) * 【setting】 StatementUtil增加null缓存(pr#1076@Github) * 【core 】 扩充Console功能,支持可变参数(issue#1077@Github) +* 【crypto 】 增加ECKeyUtil(issue#I1UOF5@Gitee) ### Bug修复 * 【core 】 修复Dict.of错误(issue#I1UUO5@Gitee) 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 e6e88ffb0..6695a957f 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java @@ -172,7 +172,7 @@ public class BCUtil { * @return ECPrivateKeyParameters */ public static ECPrivateKeyParameters toParams(String dHex, ECDomainParameters domainParameters) { - return toParams(new BigInteger(dHex, 16), domainParameters); + return ECKeyUtil.toPrivateParams(dHex, domainParameters); } /** @@ -193,7 +193,7 @@ public class BCUtil { * @return ECPrivateKeyParameters */ public static ECPrivateKeyParameters toParams(byte[] d, ECDomainParameters domainParameters) { - return toParams(new BigInteger(d), domainParameters); + return ECKeyUtil.toPrivateParams(d, domainParameters); } /** @@ -214,10 +214,7 @@ public class BCUtil { * @return ECPrivateKeyParameters */ public static ECPrivateKeyParameters toParams(BigInteger d, ECDomainParameters domainParameters) { - if(null == d){ - return null; - } - return new ECPrivateKeyParameters(d, domainParameters); + return ECKeyUtil.toPrivateParams(d, domainParameters); } /** @@ -229,10 +226,7 @@ public class BCUtil { * @return ECPublicKeyParameters */ public static ECPublicKeyParameters toParams(BigInteger x, BigInteger y, ECDomainParameters domainParameters) { - if(null == x || null == y){ - return null; - } - return toParams(x.toByteArray(), y.toByteArray(), domainParameters); + return ECKeyUtil.toPublicParams(x, y, domainParameters); } /** @@ -278,13 +272,7 @@ public class BCUtil { * @return ECPublicKeyParameters */ public static ECPublicKeyParameters toParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) { - if(null == xBytes || null == yBytes){ - return null; - } - final ECCurve curve = domainParameters.getCurve(); - final int curveLength = getCurveLength(curve); - final byte[] encodedPubKey = encodePoint(xBytes, yBytes, curveLength); - return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters); + return ECKeyUtil.toPublicParams(xBytes, yBytes, domainParameters); } /** @@ -294,14 +282,7 @@ public class BCUtil { * @return {@link ECPublicKeyParameters}或null */ public static ECPublicKeyParameters toParams(PublicKey publicKey) { - if (null == publicKey) { - return null; - } - try { - return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey); - } catch (InvalidKeyException e) { - throw new CryptoException(e); - } + return ECKeyUtil.toPublicParams(publicKey); } /** @@ -311,14 +292,7 @@ public class BCUtil { * @return {@link ECPrivateKeyParameters}或null */ public static ECPrivateKeyParameters toParams(PrivateKey privateKey) { - if (null == privateKey) { - return null; - } - try { - return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey); - } catch (InvalidKeyException e) { - throw new CryptoException(e); - } + return ECKeyUtil.toPrivateParams(privateKey); } /** @@ -344,58 +318,4 @@ public class BCUtil { public static PublicKey readPemPublicKey(InputStream pemStream) { return PemUtil.readPemPublicKey(pemStream); } - - /** - * 将X,Y曲线点编码为bytes - * - * @param xBytes X坐标bytes - * @param yBytes Y坐标bytes - * @param curveLength 曲线编码后的长度 - * @return 编码bytes - */ - private static byte[] encodePoint(byte[] xBytes, byte[] yBytes, int curveLength) { - xBytes = fixLength(curveLength, xBytes); - yBytes = fixLength(curveLength, yBytes); - final byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length]; - - // 压缩类型:无压缩 - encodedPubKey[0] = 0x04; - System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length); - System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length); - - return encodedPubKey; - } - - /** - * 获取Curve长度 - * - * @param curve {@link ECCurve} - * @return Curve长度 - */ - private static int getCurveLength(ECCurve curve) { - return (curve.getFieldSize() + 7) / 8; - } - - /** - * 修正长度 - * - * @param curveLength 修正后的长度 - * @param src bytes - * @return 修正后的bytes - */ - private static byte[] fixLength(int curveLength, byte[] src) { - if (src.length == curveLength) { - return src; - } - - byte[] result = new byte[curveLength]; - if (src.length > curveLength) { - // 裁剪末尾的指定长度 - System.arraycopy(src, src.length - result.length, result, 0, result.length); - } else { - // 放置于末尾 - System.arraycopy(src, 0, result, result.length - src.length, src.length); - } - return result; - } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java new file mode 100644 index 000000000..cd9dbe8d4 --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/ECKeyUtil.java @@ -0,0 +1,244 @@ +package cn.hutool.crypto; + +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * EC密钥参数相关工具类封装 + * + * @author looly + * @since 5.4.3 + */ +public class ECKeyUtil { + + //--------------------------------------------------------------------------- Public Key + /** + * 转换为 ECPublicKeyParameters + * + * @param q 公钥Q值 + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toSm2PublicParams(byte[] q) { + return toPublicParams(q, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为 ECPublicKeyParameters + * + * @param q 公钥Q值 + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toSm2PublicParams(String q) { + return toPublicParams(q, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为SM2的ECPublicKeyParameters + * + * @param x 公钥X + * @param y 公钥Y + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toSm2PublicParams(String x, String y) { + return toPublicParams(x, y, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为SM2的ECPublicKeyParameters + * + * @param xBytes 公钥X + * @param yBytes 公钥Y + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toSm2PublicParams(byte[] xBytes, byte[] yBytes) { + return toPublicParams(xBytes, yBytes, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param x 公钥X + * @param y 公钥Y + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toPublicParams(String x, String y, ECDomainParameters domainParameters) { + return toPublicParams(SecureUtil.decode(x), SecureUtil.decode(y), domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param xBytes 公钥X + * @param yBytes 公钥Y + * @param domainParameters ECDomainParameters曲线参数 + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) { + return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param x 公钥X + * @param y 公钥Y + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toPublicParams(BigInteger x, BigInteger y, ECDomainParameters domainParameters) { + if (null == x || null == y) { + return null; + } + final ECCurve curve = domainParameters.getCurve(); + return toPublicParams(curve.createPoint(x, y), domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param pointEncoded 被编码的曲线坐标点 + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + * @since 5.4.3 + */ + public static ECPublicKeyParameters toPublicParams(String pointEncoded, ECDomainParameters domainParameters) { + final ECCurve curve = domainParameters.getCurve(); + return toPublicParams(curve.decodePoint(SecureUtil.decode(pointEncoded)), domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param pointEncoded 被编码的曲线坐标点 + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + * @since 5.4.3 + */ + public static ECPublicKeyParameters toPublicParams(byte[] pointEncoded, ECDomainParameters domainParameters) { + final ECCurve curve = domainParameters.getCurve(); + return toPublicParams(curve.decodePoint(pointEncoded), domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param point 曲线坐标点 + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + * @since 5.4.3 + */ + public static ECPublicKeyParameters toPublicParams(org.bouncycastle.math.ec.ECPoint point, ECDomainParameters domainParameters) { + return new ECPublicKeyParameters(point, domainParameters); + } + + /** + * 公钥转换为 {@link ECPublicKeyParameters} + * + * @param publicKey 公钥,传入null返回null + * @return {@link ECPublicKeyParameters}或null + */ + public static ECPublicKeyParameters toPublicParams(PublicKey publicKey) { + if (null == publicKey) { + return null; + } + try { + return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey); + } catch (InvalidKeyException e) { + throw new CryptoException(e); + } + } + + //--------------------------------------------------------------------------- Private Key + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值16进制字符串 + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toSm2PrivateParams(String d) { + return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值 + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toSm2PrivateParams(byte[] d) { + return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值 + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toSm2PrivateParams(BigInteger d) { + return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值16进制字符串 + * @param domainParameters ECDomainParameters + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toPrivateParams(String d, ECDomainParameters domainParameters) { + return toPrivateParams(BigIntegers.fromUnsignedByteArray(SecureUtil.decode(d)), domainParameters); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值 + * @param domainParameters ECDomainParameters + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toPrivateParams(byte[] d, ECDomainParameters domainParameters) { + return toPrivateParams(BigIntegers.fromUnsignedByteArray(d), domainParameters); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值 + * @param domainParameters ECDomainParameters + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toPrivateParams(BigInteger d, ECDomainParameters domainParameters) { + if (null == d) { + return null; + } + return new ECPrivateKeyParameters(d, domainParameters); + } + + /** + * 私钥转换为 {@link ECPrivateKeyParameters} + * + * @param privateKey 私钥,传入null返回null + * @return {@link ECPrivateKeyParameters}或null + */ + public static ECPrivateKeyParameters toPrivateParams(PrivateKey privateKey) { + if (null == privateKey) { + return null; + } + try { + return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey); + } catch (InvalidKeyException e) { + throw new CryptoException(e); + } + } +} 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 56abb282d..79bc98879 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 @@ -4,12 +4,15 @@ import cn.hutool.core.codec.Base64; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.ECKeyUtil; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.SM2; import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.junit.Assert; import org.junit.Test; @@ -205,4 +208,21 @@ public class SM2Test { String sign = "DCA0E80A7F46C93714B51C3EFC55A922BCEF7ECF0FE9E62B53BA6A7438B543A76C145A452CA9036F3CB70D7E6C67D4D9D7FE114E5367A2F6F5A4D39F2B10F3D6"; Assert.assertTrue(sm2.verifyHex(data, sign)); } + + @Test + public void sm2PlainWithPointTest2() { + String d = "4BD9A450D7E68A5D7E08EB7A0BFA468FD3EB32B71126246E66249A73A9E4D44A"; + String q = "04970AB36C3B870FBC04041087DB1BC36FB4C6E125B5EA406DB0EC3E2F80F0A55D8AFF28357A0BB215ADC2928BE76F1AFF869BF4C0A3852A78F3B827812C650AD3"; + + String data = "123456"; + + final ECPublicKeyParameters ecPublicKeyParameters = ECKeyUtil.toSm2PublicParams(q); + final ECPrivateKeyParameters ecPrivateKeyParameters = ECKeyUtil.toSm2PrivateParams(d); + + final SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters); + final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey); + final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey); + + Assert.assertEquals(data, decryptStr); + } }