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} 是否为空,空的条件是: - * + * 获取{@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} 是否为空,空的条件是: + * + * + * @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()); + } }