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