diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
index 7c8812341..aacabd934 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
@@ -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} 是否为空,空的条件是:
- *
- * - keyPair本身为{@code null}
- * - {@link KeyPair#getPrivate()}和{@link KeyPair#getPublic()}都为{@code null}
- *
+ * 获取{@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私钥,仅用于非对称加密
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法
@@ -285,7 +299,9 @@ public class KeyUtil {
throw new CryptoException(e);
}
}
+ // endregion
+ // region ----- generatePublicKey
/**
* 生成RSA公钥,仅用于非对称加密
* 采用X509证书规范
@@ -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} 是否为空,空的条件是:
+ *
+ * - keyPair本身为{@code null}
+ * - {@link KeyPair#getPrivate()}和{@link KeyPair#getPublic()}都为{@code null}
+ *
+ *
+ * @param keyPair 密钥对
+ * @return 是否为空
+ */
+ public static boolean isEmpty(final KeyPair keyPair) {
+ if (null == keyPair) {
+ return false;
+ }
+ return null != keyPair.getPrivate() || null != keyPair.getPublic();
+ }
/**
* 生成用于非对称加密的公钥和私钥,仅用于非对称加密
@@ -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文件中的公钥
@@ -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)
* 见:...
@@ -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
}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/ECKeyUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/ECKeyUtil.java
index 42fa311d6..917c948b2 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/ECKeyUtil.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/ECKeyUtil.java
@@ -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值
* 见: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);
}
/**
diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java
index fcdc81de3..e4432c33a 100644
--- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java
+++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java
@@ -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()));
}
diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/ECKeyUtilTest.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/ECKeyUtilTest.java
index 08d02f510..5a4e4dc9f 100644
--- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/ECKeyUtilTest.java
+++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/ECKeyUtilTest.java
@@ -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());
+ }
}