Looly 5 年之前
父节点
当前提交
8fba51f62b

+ 129 - 3
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)<br>
 	 * 见: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;
+	}
 }

+ 1 - 1
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)
  *
  * <p>
  * PEM一般为文本格式,以 -----BEGIN... 开头,以 -----END... 结尾,中间的内容是 BASE64 编码。

+ 14 - 0
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;
 
 	/**

+ 2 - 1
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java

@@ -35,8 +35,8 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	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<SM2> {
 	public SM2(PrivateKey privateKey, PublicKey publicKey) {
 		super(ALGORITHM_SM2, privateKey, publicKey);
 	}
+
 	// ------------------------------------------------------------------ Constructor end
 
 	/**

+ 28 - 0
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);
+	}
+}