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 91150151e..d3d6cb9a9 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java @@ -1,5 +1,6 @@ package cn.hutool.crypto; +import cn.hutool.core.util.HexUtil; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; @@ -14,7 +15,7 @@ import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.math.ec.ECCurve; -import java.io.InputStream; +import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; @@ -31,6 +32,18 @@ import java.security.spec.EllipticCurve; * @since 4.5.0 */ public class BCUtil { + + /** + * 只获取私钥里的d,32字节 + * + * @param privateKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey + * @return 压缩得到的X + * @since 5.1.6 + */ + public static byte[] encodeECPrivateKey(PrivateKey privateKey) { + return ((BCECPrivateKey) privateKey).getD().toByteArray(); + } + /** * 编码压缩EC公钥(基于BouncyCastle)
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html @@ -95,7 +108,7 @@ public class BCUtil { */ public static ECKeyParameters toParams(ECKey ecKey) { final ECParameterSpec parameterSpec = ecKey.getParameters(); - final ECDomainParameters ecDomainParameters = buildECDomainParameters(parameterSpec); + final ECDomainParameters ecDomainParameters = toDomainParameters(parameterSpec); if (ecKey instanceof BCECPrivateKey) { return new ECPrivateKeyParameters(((BCECPrivateKey) ecKey).getD(), ecDomainParameters); @@ -113,11 +126,124 @@ public class BCUtil { * @return ECDomainParameters * @since 5.1.6 */ - public static ECDomainParameters buildECDomainParameters(ECParameterSpec parameterSpec) { + public static ECDomainParameters toDomainParameters(ECParameterSpec parameterSpec) { return new ECDomainParameters( parameterSpec.getCurve(), parameterSpec.getG(), parameterSpec.getN(), parameterSpec.getH()); } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param dHex 私钥d值16进制字符串 + * @param domainParameters ECDomainParameters + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toParams(String dHex, ECDomainParameters domainParameters) { + return new ECPrivateKeyParameters(new BigInteger(dHex, 16), domainParameters); + } + + /** + * 转换为 ECPrivateKeyParameters + * + * @param d 私钥d值 + * @param domainParameters ECDomainParameters + * @return ECPrivateKeyParameters + */ + public static ECPrivateKeyParameters toParams(BigInteger d, ECDomainParameters domainParameters) { + return new ECPrivateKeyParameters(d, domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param x 公钥X + * @param y 公钥Y + * @param curve ECCurve + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toParams(BigInteger x, BigInteger y, ECCurve curve, ECDomainParameters domainParameters) { + return toParams(x.toByteArray(), y.toByteArray(), curve, domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param xHex 公钥X + * @param yHex 公钥Y + * @param curve ECCurve + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toParams(String xHex, String yHex, ECCurve curve, ECDomainParameters domainParameters) { + return toParams(HexUtil.decodeHex(xHex), HexUtil.decodeHex(yHex), + curve, domainParameters); + } + + /** + * 转换为ECPublicKeyParameters + * + * @param xBytes 公钥X + * @param yBytes 公钥Y + * @param curve ECCurve + * @param domainParameters ECDomainParameters + * @return ECPublicKeyParameters + */ + public static ECPublicKeyParameters toParams(byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) { + final byte uncompressedFlag = 0x04; + int curveLength = getCurveLength(domainParameters); + xBytes = fixLength(curveLength, xBytes); + yBytes = fixLength(curveLength, yBytes); + byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length]; + encodedPubKey[0] = uncompressedFlag; + System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length); + System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length); + return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters); + } + + /** + * 获取Curve长度 + * + * @param ecKey Curve + * @return Curve长度 + */ + public static int getCurveLength(ECKeyParameters ecKey) { + return getCurveLength(ecKey.getParameters()); + } + + /** + * 获取Curve长度 + * + * @param domainParams ECDomainParameters + * @return Curve长度 + */ + public static int getCurveLength(ECDomainParameters domainParams) { + return (domainParams.getCurve().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/PemUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java index d2b8930ed..cbbd35ba2 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java @@ -17,7 +17,7 @@ import java.security.PrivateKey; import java.security.PublicKey; /** - * PEM(Privacy Enhanced Mail)格式相关工具类。 + * PEM(Privacy Enhanced Mail)格式相关工具类。(基于Bouncy Castle) * *

* PEM一般为文本格式,以 -----BEGIN... 开头,以 -----END... 结尾,中间的内容是 BASE64 编码。 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 f001ecf33..09a189d51 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/SmUtil.java @@ -15,6 +15,8 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -32,6 +34,18 @@ import java.math.BigInteger; */ public class SmUtil { + /* + * SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper) + */ + public static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); + public final static BigInteger SM2_ECC_GX = new BigInteger( + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + public final static BigInteger SM2_ECC_GY = new BigInteger( + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); + public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, + CURVE.getOrder(), CURVE.getCofactor()); + private final static int RS_LEN = 32; /** 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 da3a1b213..367f64b34 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 @@ -35,8 +35,8 @@ public class SM2 extends AbstractAsymmetricCrypto { protected SM2Signer signer; private SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2; - private ECPublicKeyParameters publicKeyParams; private ECPrivateKeyParameters privateKeyParams; + private ECPublicKeyParameters publicKeyParams; // ------------------------------------------------------------------ Constructor start @@ -85,6 +85,7 @@ public class SM2 extends AbstractAsymmetricCrypto { public SM2(PrivateKey privateKey, PublicKey publicKey) { super(ALGORITHM_SM2, privateKey, publicKey); } + // ------------------------------------------------------------------ Constructor end /** diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/BCUtilTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/BCUtilTest.java new file mode 100644 index 000000000..b60ee9641 --- /dev/null +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/BCUtilTest.java @@ -0,0 +1,28 @@ +package cn.hutool.crypto.test; + +import cn.hutool.core.lang.Assert; +import cn.hutool.crypto.BCUtil; +import cn.hutool.crypto.SmUtil; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.junit.Test; + +public class BCUtilTest { + + @Test + public void createECPublicKeyParametersTest() { + String x = "706AD9DAA3E5CEAC3DA59F583429E8043BAFC576BE10092C4EA4D8E19846CA62"; + String y = "F7E938B02EED7280277493B8556E5B01CB436E018A562DFDC53342BF41FDF728"; + + final ECPublicKeyParameters keyParameters = BCUtil.toParams(x, y, SmUtil.CURVE, SmUtil.DOMAIN_PARAMS); + Assert.notNull(keyParameters); + } + + @Test + public void createECPrivateKeyParametersTest() { + String privateKeyHex = "5F6CA5BB044C40ED2355F0372BF72A5B3AE6943712F9FDB7C1FFBAECC06F3829"; + + final ECPrivateKeyParameters keyParameters = BCUtil.toParams(privateKeyHex, SmUtil.DOMAIN_PARAMS); + Assert.notNull(keyParameters); + } +}