This commit is contained in:
Looly 2020-03-03 00:37:01 +08:00
parent 82f982f809
commit 8fba51f62b
5 changed files with 174 additions and 5 deletions

View File

@ -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 {
/**
* 只获取私钥里的d32字节
*
* @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;
}
}

View File

@ -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 编码

View File

@ -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;
/**

View File

@ -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
/**

View File

@ -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);
}
}