增加BCUtil.decodeECPrivateKey方法(issue#3829@Github)

This commit is contained in:
Looly 2025-01-01 19:08:11 +08:00
parent fd45eecda1
commit 224bfee92e
4 changed files with 143 additions and 57 deletions

View File

@ -68,6 +68,17 @@ public class KeyUtil {
*/
public static final int DEFAULT_KEY_SIZE = 1024;
/**
* 将密钥编码为Base64格式
*
* @param key 密钥
* @return Base64格式密钥
* @since 5.7.22
*/
public static String toBase64(final Key key) {
return Base64.encode(key.getEncoded());
}
// region ----- generateKey
/**
@ -200,26 +211,29 @@ public class KeyUtil {
throw new CryptoException(e);
}
}
// endregion
/**
* 检查{@link KeyPair} 是否为空空的条件是
* <ul>
* <li>keyPair本身为{@code null}</li>
* <li>{@link KeyPair#getPrivate()}{@link KeyPair#getPublic()}都为{@code null}</li>
* </ul>
* 获取{@link KeyGenerator}
*
* @param keyPair 密钥对
* @return 是否为空
* @param algorithm 对称加密算法
* @return {@link KeyGenerator}
* @since 4.5.2
*/
// region ----- keyPair
public static boolean isEmpty(final KeyPair keyPair) {
if (null == keyPair) {
return false;
public static KeyGenerator getKeyGenerator(final String algorithm) {
final Provider provider = GlobalProviderFactory.getProvider();
final KeyGenerator generator;
try {
generator = (null == provider) //
? KeyGenerator.getInstance(getMainAlgorithm(algorithm)) //
: KeyGenerator.getInstance(getMainAlgorithm(algorithm), provider);
} catch (final NoSuchAlgorithmException e) {
throw new CryptoException(e);
}
return null != keyPair.getPrivate() || null != keyPair.getPublic();
return generator;
}
// endregion
// region ----- generatePrivateKey
/**
* 生成RSA私钥仅用于非对称加密<br>
* 采用PKCS#8规范此规范定义了私钥信息语法和加密私钥语法<br>
@ -285,7 +299,9 @@ public class KeyUtil {
throw new CryptoException(e);
}
}
// endregion
// region ----- generatePublicKey
/**
* 生成RSA公钥仅用于非对称加密<br>
* 采用X509证书规范<br>
@ -335,7 +351,9 @@ public class KeyUtil {
throw new CryptoException(e);
}
}
// endregion
// region ----- getRSAPublicKey
/**
* 通过RSA私钥生成RSA公钥
*
@ -380,6 +398,25 @@ public class KeyUtil {
throw new CryptoException(e);
}
}
// endregion
// region ----- keyPair
/**
* 检查{@link KeyPair} 是否为空空的条件是
* <ul>
* <li>keyPair本身为{@code null}</li>
* <li>{@link KeyPair#getPrivate()}{@link KeyPair#getPublic()}都为{@code null}</li>
* </ul>
*
* @param keyPair 密钥对
* @return 是否为空
*/
public static boolean isEmpty(final KeyPair keyPair) {
if (null == keyPair) {
return false;
}
return null != keyPair.getPrivate() || null != keyPair.getPublic();
}
/**
* 生成用于非对称加密的公钥和私钥仅用于非对称加密<br>
@ -616,6 +653,7 @@ public class KeyUtil {
}
// endregion
// region ----- keyFactory
/**
* 获取{@link KeyFactory}
*
@ -655,27 +693,9 @@ public class KeyUtil {
}
return keyFactory;
}
// endregion
/**
* 获取{@link KeyGenerator}
*
* @param algorithm 对称加密算法
* @return {@link KeyGenerator}
* @since 4.5.2
*/
public static KeyGenerator getKeyGenerator(final String algorithm) {
final Provider provider = GlobalProviderFactory.getProvider();
final KeyGenerator generator;
try {
generator = (null == provider) //
? KeyGenerator.getInstance(getMainAlgorithm(algorithm)) //
: KeyGenerator.getInstance(getMainAlgorithm(algorithm), provider);
} catch (final NoSuchAlgorithmException e) {
throw new CryptoException(e);
}
return generator;
}
// region ----- algorithm
/**
* 获取主体算法名例如RSA/ECB/PKCS1Padding的主体算法是RSA
*
@ -720,6 +740,7 @@ public class KeyUtil {
}
return algorithm;
}
// endregion
/**
* 读取X.509 Certification文件中的公钥<br>
@ -738,6 +759,39 @@ public class KeyUtil {
return null;
}
// region ----- EC Key
/**
* 编码压缩EC私钥基于BouncyCastle
*
* @param privateKey {@link PrivateKey}必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
* @return 压缩得到的D
*/
public static byte[] encodeECPrivateKey(final PrivateKey privateKey) {
return ECKeyUtil.encodeECPrivateKey(privateKey);
}
/**
* 解码恢复EC私钥,支持Base64和Hex编码,基于BouncyCastle
*
* @param encode 私钥
* @param curveName EC曲线名
* @return 私钥
*/
public static PrivateKey decodeECPrivateKey(final String encode, final String curveName) {
return ECKeyUtil.decodeECPrivateKey(encode, curveName);
}
/**
* 解码恢复EC私钥,支持Base64和Hex编码,基于BouncyCastle
*
* @param encodeByte 私钥
* @param curveName EC曲线名
* @return 私钥
*/
public static PrivateKey decodeECPrivateKey(final byte[] encodeByte, final String curveName) {
return ECKeyUtil.decodeECPrivateKey(encodeByte, curveName);
}
/**
* 编码压缩EC公钥基于BouncyCastle<br>
* <a href="https://www.cnblogs.com/xinzhao/p/8963724.html">...</a>
@ -759,8 +813,8 @@ public class KeyUtil {
* @return 公钥
* @since 4.4.4
*/
public static PublicKey decodeECPoint(final String encode, final String curveName) {
return ECKeyUtil.decodeECPoint(encode, curveName);
public static PublicKey decodeECPublicKey(final String encode, final String curveName) {
return ECKeyUtil.decodeECPublicKey(encode, curveName);
}
/**
@ -772,18 +826,8 @@ public class KeyUtil {
* @return 公钥
* @since 4.4.4
*/
public static PublicKey decodeECPoint(final byte[] encodeByte, final String curveName) {
return ECKeyUtil.decodeECPoint(encodeByte, curveName);
}
/**
* 将密钥编码为Base64格式
*
* @param key 密钥
* @return Base64格式密钥
* @since 5.7.22
*/
public static String toBase64(final Key key) {
return Base64.encode(key.getEncoded());
public static PublicKey decodeECPublicKey(final byte[] encodeByte, final String curveName) {
return ECKeyUtil.decodeECPublicKey(encodeByte, curveName);
}
// endregion
}

View File

@ -31,6 +31,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;
@ -49,7 +50,6 @@ import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Objects;
/**
* 椭圆曲线EC(Elliptic Curves)密钥参数相关工具类封装
@ -110,6 +110,38 @@ public class ECKeyUtil {
return ((ECPrivateKey) privateKey).getD().toByteArray();
}
/**
* 解码恢复EC私钥,支持Base64和Hex编码,基于BouncyCastle
*
* @param d 私钥d值Base64或Hex格式
* @param curveName EC曲线名例如{@link SM2Constant#SM2_DOMAIN_PARAMS}
* @return 私钥
*/
public static PrivateKey decodeECPrivateKey(final String d, final String curveName) {
return decodeECPrivateKey(SecureUtil.decode(d), curveName);
}
/**
* 解码恢复EC私钥,支持Base64和Hex编码,基于BouncyCastle
*
* @param d 私钥d值
* @param curveName EC曲线名例如{@link SM2Constant#SM2_DOMAIN_PARAMS}
* @return 私钥
* @since 5.8.36
*/
public static PrivateKey decodeECPrivateKey(final byte[] d, final String curveName) {
final X9ECParameters x9ECParameters = ECUtil.getNamedCurveByName(curveName);
final ECParameterSpec ecSpec = new ECParameterSpec(
x9ECParameters.getCurve(),
x9ECParameters.getG(),
x9ECParameters.getN(),
x9ECParameters.getH()
);
final ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(d), ecSpec);
return KeyUtil.generatePrivateKey("EC", privateKeySpec);
}
/**
* 编码压缩EC公钥基于BouncyCastle即Q值<br>
* https://www.cnblogs.com/xinzhao/p/8963724.html
@ -144,8 +176,8 @@ public class ECKeyUtil {
* @return 公钥
* @since 4.4.4
*/
public static PublicKey decodeECPoint(final String encode, final String curveName) {
return decodeECPoint(SecureUtil.decode(encode), curveName);
public static PublicKey decodeECPublicKey(final String encode, final String curveName) {
return decodeECPublicKey(SecureUtil.decode(encode), curveName);
}
/**
@ -156,7 +188,7 @@ public class ECKeyUtil {
* @return 公钥
* @since 4.4.4
*/
public static PublicKey decodeECPoint(final byte[] encodeByte, final String curveName) {
public static PublicKey decodeECPublicKey(final byte[] encodeByte, final String curveName) {
final X9ECParameters x9ECParameters = ECUtil.getNamedCurveByName(curveName);
final ECCurve curve = x9ECParameters.getCurve();
final java.security.spec.ECPoint point = EC5Util.convertPoint(curve.decodePoint(encodeByte));
@ -370,9 +402,7 @@ public class ECKeyUtil {
if (null == d) {
return null;
}
return toPrivateParams(
BigIntegers.fromUnsignedByteArray(Objects.requireNonNull(SecureUtil.decode(d))),
domainParameters);
return toPrivateParams(SecureUtil.decode(d), domainParameters);
}
/**

View File

@ -210,8 +210,8 @@ public class SM2Test {
final byte[] data = KeyUtil.encodeECPublicKey(publicKey);
final String encodeHex = HexUtil.encodeStr(data);
final String encodeB64 = Base64.encode(data);
final PublicKey Hexdecode = KeyUtil.decodeECPoint(encodeHex, SM2Constant.SM2_CURVE_NAME);
final PublicKey B64decode = KeyUtil.decodeECPoint(encodeB64, SM2Constant.SM2_CURVE_NAME);
final PublicKey Hexdecode = KeyUtil.decodeECPublicKey(encodeHex, SM2Constant.SM2_CURVE_NAME);
final PublicKey B64decode = KeyUtil.decodeECPublicKey(encodeB64, SM2Constant.SM2_CURVE_NAME);
Assertions.assertEquals(HexUtil.encodeStr(publicKey.getEncoded()), HexUtil.encodeStr(Hexdecode.getEncoded()));
Assertions.assertEquals(HexUtil.encodeStr(publicKey.getEncoded()), HexUtil.encodeStr(B64decode.getEncoded()));
}

View File

@ -54,4 +54,16 @@ public class ECKeyUtilTest {
final PublicKey ecPublicKey = ECKeyUtil.getECPublicKey((ECPrivateKey) sm2.getPrivate(), SM2Constant.SM2_EC_SPEC);
Assertions.assertEquals(sm2.getPublic(), ecPublicKey);
}
@Test
void encodeAndDecodeECPrivateKeyTest(){
// 生成一个EC密钥对
final KeyPair ecKeyPair = KeyUtil.generateKeyPair("EC");
final ECPrivateKey ecPrivateKey = (ECPrivateKey) ecKeyPair.getPrivate();
final byte[] bytes = ECKeyUtil.encodeECPrivateKey(ecPrivateKey);
final ECPrivateKey decodedPrivateKey = (ECPrivateKey) ECKeyUtil.decodeECPrivateKey(bytes, "secp256r1");
Assertions.assertEquals(ecPrivateKey.getD(), decodedPrivateKey.getD());
}
}