Looly 5 years ago
parent
commit
82f982f809

+ 3 - 1
CHANGELOG.md

@@ -3,7 +3,7 @@
 
 -------------------------------------------------------------------------------------------------------------
 
-## 5.1.6
+## 5.2.0
 
 ### 新特性
 * 【core  】     NumberUtil.decimalFormat增加Object对象参数支持
@@ -18,6 +18,8 @@
 * 【all   】     log、template、tokenizer使用SPI机制代替硬编码
 * 【poi   】     Word07Writer增加addPicture
 * 【crypto】     RSA算法中,BlockSize长度策略调整(issue#721@Github)
+* 【crypto】     删除SM2Engine,使用BC库中的对象替代
+* 【crypto】     增加PemUtil工具类
 
 ### Bug修复
 

+ 5 - 5
README.md

@@ -40,7 +40,7 @@
 	-- 主页:<a href="https://hutool.cn">https://hutool.cn/</a> | <a href="https://www.hutool.club/">https://www.hutool.club/</a> --
 </p>
 <p align="center">
-	-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.1.6b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
+	-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.2.0b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
 	-- QQ群④:<a href="https://shang.qq.com/wpa/qunwpa?idkey=309056e409a304a454c7ba250a10d38dd82b9b49cd0e1f180fedbde78b02ae0d">718802356</a> --
 </p>
 
@@ -116,21 +116,21 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
 <dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
-    <version>5.1.6</version>
+    <version>5.2.0</version>
 </dependency>
 ```
 
 ### Gradle
 ```
-compile 'cn.hutool:hutool-all:5.1.6'
+compile 'cn.hutool:hutool-all:5.2.0'
 ```
 
 ### 非Maven项目
 
 点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
 
-- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.1.6/)
-- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.1.6/)
+- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.2.0/)
+- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.2.0/)
 
 > 注意
 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类获工具方法可用。

+ 1 - 1
bin/version.txt

@@ -1 +1 @@
-5.1.6
+5.2.0

+ 1 - 1
docs/js/version.js

@@ -1 +1 @@
-var version = '5.1.6'
+var version = '5.2.0'

+ 1 - 1
hutool-all/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-all</artifactId>

+ 1 - 1
hutool-aop/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-aop</artifactId>

+ 1 - 1
hutool-bloomFilter/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-bloomFilter</artifactId>

+ 1 - 1
hutool-bom/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-bom</artifactId>

+ 1 - 1
hutool-cache/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-cache</artifactId>

+ 1 - 1
hutool-captcha/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-captcha</artifactId>

+ 1 - 1
hutool-core/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-core</artifactId>

+ 1 - 1
hutool-cron/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-cron</artifactId>

+ 1 - 1
hutool-crypto/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-crypto</artifactId>

+ 28 - 148
hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java

@@ -1,30 +1,25 @@
 package cn.hutool.crypto;
 
-import cn.hutool.core.io.IORuntimeException;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.StrUtil;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
 import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
 import org.bouncycastle.jce.ECNamedCurveTable;
 import org.bouncycastle.jce.ECPointUtil;
+import org.bouncycastle.jce.interfaces.ECKey;
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.jce.spec.ECParameterSpec;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemObjectGenerator;
-import org.bouncycastle.util.io.pem.PemReader;
-import org.bouncycastle.util.io.pem.PemWriter;
 
-import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
 import java.security.GeneralSecurityException;
-import java.security.Key;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.ECFieldFp;
-import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
@@ -81,7 +76,7 @@ public class BCUtil {
 		final ECPoint point = ECPointUtil.decodePoint(ecCurve, encodeByte);
 
 		// 根据曲线恢复公钥格式
-		ECParameterSpec ecSpec = new ECNamedCurveSpec(curveName, curve, namedSpec.getG(), namedSpec.getN());
+		ECNamedCurveSpec ecSpec = new ECNamedCurveSpec(curveName, curve, namedSpec.getG(), namedSpec.getN());
 
 		final KeyFactory PubKeyGen = KeyUtil.getKeyFactory("EC");
 		try {
@@ -92,152 +87,37 @@ public class BCUtil {
 	}
 
 	/**
-	 * 读取PEM格式的私钥
+	 * ECKey转换为ECKeyParameters
 	 *
-	 * @param pemStream pem流
-	 * @return {@link PrivateKey}
-	 * @since 4.5.2
-	 */
-	public static PrivateKey readPrivateKey(InputStream pemStream) {
-		return (PrivateKey) readPemKey(pemStream);
-	}
-
-	/**
-	 * 读取PEM格式的公钥
-	 *
-	 * @param pemStream pem流
-	 * @return {@link PublicKey}
-	 * @since 4.5.2
-	 */
-	public static PublicKey readPublicKey(InputStream pemStream) {
-		return (PublicKey) readPemKey(pemStream);
-	}
-
-	/**
-	 * 从pem文件中读取公钥或私钥<br>
-	 * 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
-	 *
-	 * @param pemKeyStream pem流
-	 * @return {@link Key}
-	 * @since 4.5.2
-	 * @deprecated 请使用{@link #readPemKey(InputStream)}
-	 */
-	@Deprecated
-	public static Key readKey(InputStream pemKeyStream) {
-		return readPemKey(pemKeyStream);
-	}
-
-	/**
-	 * 从pem文件中读取公钥或私钥<br>
-	 * 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
-	 *
-	 * @param keyStream pem流
-	 * @return {@link Key},null表示无法识别的密钥类型
+	 * @param ecKey BCECPrivateKey或者BCECPublicKey
+	 * @return ECPrivateKeyParameters或者ECPublicKeyParameters
 	 * @since 5.1.6
 	 */
-	public static Key readPemKey(InputStream keyStream) {
-		final PemObject object = readPemObject(keyStream);
-		final String type = object.getType();
-		if (StrUtil.isNotBlank(type)) {
-			if (type.endsWith("PRIVATE KEY")) {
-				return KeyUtil.generateRSAPrivateKey(object.getContent());
-			} else if (type.endsWith("PUBLIC KEY")) {
-				return KeyUtil.generateRSAPublicKey(object.getContent());
-			} else if (type.endsWith("CERTIFICATE")) {
-				return KeyUtil.readPublicKeyFromCert(IoUtil.toStream(object.getContent()));
-			}
+	public static ECKeyParameters toParams(ECKey ecKey) {
+		final ECParameterSpec parameterSpec = ecKey.getParameters();
+		final ECDomainParameters ecDomainParameters = buildECDomainParameters(parameterSpec);
+
+		if (ecKey instanceof BCECPrivateKey) {
+			return new ECPrivateKeyParameters(((BCECPrivateKey) ecKey).getD(), ecDomainParameters);
+		} else if (ecKey instanceof BCECPublicKey) {
+			return new ECPublicKeyParameters(((BCECPublicKey) ecKey).getQ(), ecDomainParameters);
 		}
 
-		//表示无法识别的密钥类型
 		return null;
 	}
 
 	/**
-	 * 从pem文件中读取公钥或私钥
-	 *
-	 * @param keyStream pem流
-	 * @return 密钥bytes
-	 * @since 4.5.2
-	 * @deprecated 使用{@link #readPem(InputStream)}
-	 */
-	@Deprecated
-	public static byte[] readKeyBytes(InputStream keyStream) {
-		return readPem(keyStream);
-	}
-
-	/**
-	 * 从pem流中读取公钥或私钥
+	 * 构建ECDomainParameters对象
 	 *
-	 * @param keyStream pem流
-	 * @return 密钥bytes
+	 * @param parameterSpec ECParameterSpec
+	 * @return ECDomainParameters
 	 * @since 5.1.6
 	 */
-	public static byte[] readPem(InputStream keyStream) {
-		PemObject pemObject = readPemObject(keyStream);
-		if (null != pemObject) {
-			return pemObject.getContent();
-		}
-		return null;
-	}
-
-	/**
-	 * 读取pem文件中的信息,包括类型、头信息和密钥内容
-	 *
-	 * @param keyStream pem流
-	 * @return {@link PemObject}
-	 * @since 4.5.2
-	 */
-	public static PemObject readPemObject(InputStream keyStream) {
-		return readPemObject(IoUtil.getUtf8Reader(keyStream));
-	}
-
-	/**
-	 * 读取pem文件中的信息,包括类型、头信息和密钥内容
-	 *
-	 * @param reader pem Reader
-	 * @return {@link PemObject}
-	 * @since 5.1.6
-	 */
-	public static PemObject readPemObject(Reader reader) {
-		PemReader pemReader = null;
-		try {
-			pemReader = new PemReader(reader);
-			return pemReader.readPemObject();
-		} catch (IOException e) {
-			throw new IORuntimeException(e);
-		} finally {
-			IoUtil.close(pemReader);
-		}
-	}
-
-	/**
-	 * 写出pem密钥(私钥、公钥、证书)
-	 *
-	 * @param type      密钥类型(私钥、公钥、证书)
-	 * @param content   密钥内容
-	 * @param keyStream pem流
-	 * @since 5.1.6
-	 */
-	public static void writePemObject(String type, byte[] content, OutputStream keyStream) {
-		writePemObject(new PemObject(type, content), keyStream);
-	}
-
-	/**
-	 * 写出pem密钥(私钥、公钥、证书)
-	 *
-	 * @param pemObject pem对象,包括密钥和密钥类型等信息
-	 * @param keyStream pem流
-	 * @since 5.1.6
-	 */
-	public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
-		PemWriter writer = null;
-		try {
-			writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
-			writer.writeObject(pemObject);
-		} catch (IOException e) {
-			throw new IORuntimeException(e);
-		} finally {
-			IoUtil.close(writer);
-		}
+	public static ECDomainParameters buildECDomainParameters(ECParameterSpec parameterSpec) {
+		return new ECDomainParameters(
+				parameterSpec.getCurve(),
+				parameterSpec.getG(),
+				parameterSpec.getN(),
+				parameterSpec.getH());
 	}
 }

+ 154 - 0
hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java

@@ -0,0 +1,154 @@
+package cn.hutool.crypto;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemObjectGenerator;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.bouncycastle.util.io.pem.PemWriter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+/**
+ * PEM(Privacy Enhanced Mail)格式相关工具类。
+ *
+ * <p>
+ * PEM一般为文本格式,以 -----BEGIN... 开头,以 -----END... 结尾,中间的内容是 BASE64 编码。
+ * <p>
+ * 这种格式可以保存证书和私钥,有时我们也把PEM格式的私钥的后缀改为 .key 以区别证书与私钥。
+ *
+ * @author looly
+ * @since 5.1.6
+ */
+public class PemUtil {
+
+	/**
+	 * 读取PEM格式的私钥
+	 *
+	 * @param pemStream pem流
+	 * @return {@link PrivateKey}
+	 * @since 4.5.2
+	 */
+	public static PrivateKey readPemPrivateKey(InputStream pemStream) {
+		return (PrivateKey) PemUtil.readPemKey(pemStream);
+	}
+
+	/**
+	 * 读取PEM格式的公钥
+	 *
+	 * @param pemStream pem流
+	 * @return {@link PublicKey}
+	 * @since 4.5.2
+	 */
+	public static PublicKey readPemPublicKey(InputStream pemStream) {
+		return (PublicKey) PemUtil.readPemKey(pemStream);
+	}
+
+	/**
+	 * 从pem文件中读取公钥或私钥<br>
+	 * 根据类型返回 {@link PublicKey} 或者 {@link PrivateKey}
+	 *
+	 * @param keyStream pem流
+	 * @return {@link Key},null表示无法识别的密钥类型
+	 * @since 5.1.6
+	 */
+	public static Key readPemKey(InputStream keyStream) {
+		final PemObject object = readPemObject(keyStream);
+		final String type = object.getType();
+		if (StrUtil.isNotBlank(type)) {
+			if (type.endsWith("PRIVATE KEY")) {
+				return KeyUtil.generateRSAPrivateKey(object.getContent());
+			} else if (type.endsWith("PUBLIC KEY")) {
+				return KeyUtil.generateRSAPublicKey(object.getContent());
+			} else if (type.endsWith("CERTIFICATE")) {
+				return KeyUtil.readPublicKeyFromCert(IoUtil.toStream(object.getContent()));
+			}
+		}
+
+		//表示无法识别的密钥类型
+		return null;
+	}
+
+	/**
+	 * 从pem流中读取公钥或私钥
+	 *
+	 * @param keyStream pem流
+	 * @return 密钥bytes
+	 * @since 5.1.6
+	 */
+	public static byte[] readPem(InputStream keyStream) {
+		PemObject pemObject = readPemObject(keyStream);
+		if (null != pemObject) {
+			return pemObject.getContent();
+		}
+		return null;
+	}
+
+	/**
+	 * 读取pem文件中的信息,包括类型、头信息和密钥内容
+	 *
+	 * @param keyStream pem流
+	 * @return {@link PemObject}
+	 * @since 4.5.2
+	 */
+	public static PemObject readPemObject(InputStream keyStream) {
+		return readPemObject(IoUtil.getUtf8Reader(keyStream));
+	}
+
+	/**
+	 * 读取pem文件中的信息,包括类型、头信息和密钥内容
+	 *
+	 * @param reader pem Reader
+	 * @return {@link PemObject}
+	 * @since 5.1.6
+	 */
+	public static PemObject readPemObject(Reader reader) {
+		PemReader pemReader = null;
+		try {
+			pemReader = new PemReader(reader);
+			return pemReader.readPemObject();
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		} finally {
+			IoUtil.close(pemReader);
+		}
+	}
+
+	/**
+	 * 写出pem密钥(私钥、公钥、证书)
+	 *
+	 * @param type      密钥类型(私钥、公钥、证书)
+	 * @param content   密钥内容
+	 * @param keyStream pem流
+	 * @since 5.1.6
+	 */
+	public static void writePemObject(String type, byte[] content, OutputStream keyStream) {
+		writePemObject(new PemObject(type, content), keyStream);
+	}
+
+	/**
+	 * 写出pem密钥(私钥、公钥、证书)
+	 *
+	 * @param pemObject pem对象,包括密钥和密钥类型等信息
+	 * @param keyStream pem流
+	 * @since 5.1.6
+	 */
+	public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
+		PemWriter writer = null;
+		try {
+			writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
+			writer.writeObject(pemObject);
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		} finally {
+			IoUtil.close(writer);
+		}
+	}
+}

+ 93 - 52
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java

@@ -2,8 +2,9 @@ package cn.hutool.crypto.asymmetric;
 
 import cn.hutool.crypto.CryptoException;
 import cn.hutool.crypto.SecureUtil;
-import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.SM2Engine;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithID;
@@ -19,23 +20,26 @@ import java.security.PublicKey;
  * 国密SM2算法实现,基于BC库<br>
  * SM2算法只支持公钥加密,私钥解密<br>
  * 参考:https://blog.csdn.net/pridas/article/details/86118774
- * 
+ *
  * @author looly
  * @since 4.3.2
  */
 public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
-	/** 算法EC */
+	/**
+	 * 算法EC
+	 */
 	private static final String ALGORITHM_SM2 = "SM2";
 
 	protected SM2Engine engine;
 	protected SM2Signer signer;
 
-	private SM2Mode mode;
+	private SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
 	private ECPublicKeyParameters publicKeyParams;
 	private ECPrivateKeyParameters privateKeyParams;
 
 	// ------------------------------------------------------------------ Constructor start
+
 	/**
 	 * 构造,生成新的私钥公钥对
 	 */
@@ -47,9 +51,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	 * 构造<br>
 	 * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
 	 * 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
-	 * 
+	 *
 	 * @param privateKeyStr 私钥Hex或Base64表示
-	 * @param publicKeyStr 公钥Hex或Base64表示
+	 * @param publicKeyStr  公钥Hex或Base64表示
 	 */
 	public SM2(String privateKeyStr, String publicKeyStr) {
 		this(SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr));
@@ -59,9 +63,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	 * 构造 <br>
 	 * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
 	 * 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
-	 * 
+	 *
 	 * @param privateKey 私钥
-	 * @param publicKey 公钥
+	 * @param publicKey  公钥
 	 */
 	public SM2(byte[] privateKey, byte[] publicKey) {
 		this(//
@@ -74,9 +78,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	 * 构造 <br>
 	 * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
 	 * 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
-	 * 
+	 *
 	 * @param privateKey 私钥
-	 * @param publicKey 公钥
+	 * @param publicKey  公钥
 	 */
 	public SM2(PrivateKey privateKey, PublicKey publicKey) {
 		super(ALGORITHM_SM2, privateKey, publicKey);
@@ -87,9 +91,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	 * 初始化<br>
 	 * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
 	 * 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密(签名)或者解密(校验)
-	 * 
+	 *
 	 * @param privateKey 私钥
-	 * @param publicKey 公钥
+	 * @param publicKey  公钥
 	 * @return this
 	 */
 	public SM2 init(PrivateKey privateKey, PublicKey publicKey) {
@@ -103,16 +107,17 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	}
 
 	// --------------------------------------------------------------------------------- Encrypt
+
 	/**
 	 * 加密,SM2非对称加密的结果由C1,C2,C3三部分组成,其中:
-	 * 
+	 *
 	 * <pre>
 	 * C1 生成随机数的计算出的椭圆曲线点
 	 * C2 密文数据
 	 * C3 SM3的摘要值
 	 * </pre>
-	 * 
-	 * @param data 被加密的bytes
+	 *
+	 * @param data    被加密的bytes
 	 * @param keyType 私钥或公钥 {@link KeyType}
 	 * @return 加密后的bytes
 	 * @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
@@ -123,22 +128,43 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 			throw new IllegalArgumentException("Encrypt is only support by public key");
 		}
 		checkKey(keyType);
+		return encrypt(data, new ParametersWithRandom(getCipherParameters(keyType)));
+	}
 
+	/**
+	 * 加密,SM2非对称加密的结果由C1,C2,C3三部分组成,其中:
+	 *
+	 * <pre>
+	 * C1 生成随机数的计算出的椭圆曲线点
+	 * C2 密文数据
+	 * C3 SM3的摘要值
+	 * </pre>
+	 *
+	 * @param data             被加密的bytes
+	 * @param pubKeyParameters 公钥参数
+	 * @return 加密后的bytes
+	 * @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
+	 * @since 5.1.6
+	 */
+	public byte[] encrypt(byte[] data, CipherParameters pubKeyParameters) throws CryptoException {
 		lock.lock();
 		final SM2Engine engine = getEngine();
 		try {
-			engine.init(true, new ParametersWithRandom(getCipherParameters(keyType)));
+			engine.init(true, pubKeyParameters);
 			return engine.processBlock(data, 0, data.length);
+		} catch (InvalidCipherTextException e) {
+			throw new CryptoException(e);
 		} finally {
 			lock.unlock();
 		}
 	}
 
 	// --------------------------------------------------------------------------------- Decrypt
+
 	/**
 	 * 解密
-	 * 
-	 * @param data SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
+	 *
+	 * @param data    SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
 	 * @param keyType 私钥或公钥 {@link KeyType}
 	 * @return 加密后的bytes
 	 * @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
@@ -149,21 +175,35 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 			throw new IllegalArgumentException("Decrypt is only support by private key");
 		}
 		checkKey(keyType);
+		return decrypt(data, getCipherParameters(keyType));
+	}
 
+	/**
+	 * 解密
+	 *
+	 * @param data                 SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
+	 * @param privateKeyParameters 私钥参数
+	 * @return 加密后的bytes
+	 * @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
+	 * @since 5.1.6
+	 */
+	public byte[] decrypt(byte[] data, CipherParameters privateKeyParameters) throws CryptoException {
 		lock.lock();
 		final SM2Engine engine = getEngine();
 		try {
-			engine.init(false, getCipherParameters(keyType));
+			engine.init(false, privateKeyParameters);
 			return engine.processBlock(data, 0, data.length);
+		} catch (InvalidCipherTextException e) {
+			throw new CryptoException(e);
 		} finally {
 			lock.unlock();
 		}
 	}
-
 	// --------------------------------------------------------------------------------- Sign and Verify
+
 	/**
 	 * 用私钥对信息生成数字签名
-	 * 
+	 *
 	 * @param data 加密数据
 	 * @return 签名
 	 */
@@ -173,9 +213,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 用私钥对信息生成数字签名
-	 * 
+	 *
 	 * @param data 加密数据
-	 * @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
+	 * @param id   可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
 	 * @return 签名
 	 */
 	public byte[] sign(byte[] data, byte[] id) {
@@ -198,7 +238,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 用公钥检验数字签名的合法性
-	 * 
+	 *
 	 * @param data 数据
 	 * @param sign 签名
 	 * @return 是否验证通过
@@ -209,10 +249,10 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 用公钥检验数字签名的合法性
-	 * 
+	 *
 	 * @param data 数据
 	 * @param sign 签名
-	 * @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
+	 * @param id   可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
 	 * @return 是否验证通过
 	 */
 	public boolean verify(byte[] data, byte[] sign, byte[] id) {
@@ -257,22 +297,23 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 设置加密类型
-	 * 
-	 * @param mode {@link SM2Mode}
+	 *
+	 * @param mode {@link SM2Engine.Mode}
 	 * @return this
 	 */
-	public SM2 setMode(SM2Mode mode) {
+	public SM2 setMode(SM2Engine.Mode mode) {
 		this.mode = mode;
 		if (null != this.engine) {
-			this.engine.setMode(mode);
+			this.engine = null;
 		}
 		return this;
 	}
 
 	// ------------------------------------------------------------------------------------------------------------------------- Private method start
+
 	/**
 	 * 初始化加密解密参数(包括私钥和公钥参数)
-	 * 
+	 *
 	 * @return this
 	 */
 	private SM2 initCipherParams() {
@@ -292,16 +333,16 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 获取密钥类型对应的加密参数对象{@link CipherParameters}
-	 * 
+	 *
 	 * @param keyType Key类型枚举,包括私钥或公钥
 	 * @return {@link CipherParameters}
 	 */
 	private CipherParameters getCipherParameters(KeyType keyType) {
 		switch (keyType) {
-		case PublicKey:
-			return this.publicKeyParams;
-		case PrivateKey:
-			return this.privateKeyParams;
+			case PublicKey:
+				return this.publicKeyParams;
+			case PrivateKey:
+				return this.privateKeyParams;
 		}
 
 		return null;
@@ -309,27 +350,27 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 
 	/**
 	 * 检查对应类型的Key是否存在
-	 * 
+	 *
 	 * @param keyType key类型
 	 */
 	private void checkKey(KeyType keyType) {
 		switch (keyType) {
-		case PublicKey:
-			if (null == this.publicKey) {
-				throw new NullPointerException("No public key provided");
-			}
-			break;
-		case PrivateKey:
-			if (null == this.privateKey) {
-				throw new NullPointerException("No private key provided");
-			}
-			break;
+			case PublicKey:
+				if (null == this.publicKey) {
+					throw new NullPointerException("No public key provided");
+				}
+				break;
+			case PrivateKey:
+				if (null == this.privateKey) {
+					throw new NullPointerException("No private key provided");
+				}
+				break;
 		}
 	}
 
 	/**
-	 * 获取{@link SM2Engine}
-	 * 
+	 * 获取{@link SM2Engine},此对象为懒加载模式
+	 *
 	 * @return {@link SM2Engine}
 	 */
 	private SM2Engine getEngine() {
@@ -338,10 +379,10 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 		}
 		return this.engine;
 	}
-	
+
 	/**
-	 * 获取{@link SM2Signer}
-	 * 
+	 * 获取{@link SM2Signer},此对象为懒加载模式
+	 *
 	 * @return {@link SM2Signer}
 	 */
 	private SM2Signer getSigner() {

+ 0 - 356
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2Engine.java

@@ -1,356 +0,0 @@
-package cn.hutool.crypto.asymmetric;
-
-import java.math.BigInteger;
-import java.util.Random;
-
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.CryptoServicesRegistrar;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.digests.SM3Digest;
-import org.bouncycastle.crypto.params.ECDomainParameters;
-import org.bouncycastle.crypto.params.ECKeyParameters;
-import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
-import org.bouncycastle.crypto.params.ECPublicKeyParameters;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.math.ec.ECConstants;
-import org.bouncycastle.math.ec.ECFieldElement;
-import org.bouncycastle.math.ec.ECMultiplier;
-import org.bouncycastle.math.ec.ECPoint;
-import org.bouncycastle.math.ec.FixedPointCombMultiplier;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.BigIntegers;
-import org.bouncycastle.util.Memoable;
-import org.bouncycastle.util.Pack;
-
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.crypto.CryptoException;
-
-/**
- * SM2加密解密引擎,来自Bouncy Castle库的SM2Engine类改造<br>
- * SM2加密后的数据格式为(两种模式):
- * 
- * <pre>
- * curve(C1) | data(C2) | digest(C3)
- * curve(C1) | digest(C3) | data(C2)
- * </pre>
- * 
- * @author looly, bouncycastle
- * @since 4.5.0
- */
-public class SM2Engine {
-
-	private final Digest digest;
-
-	private boolean forEncryption;
-	private ECKeyParameters ecKey;
-	private ECDomainParameters ecParams;
-	private int curveLength;
-	private Random random;
-	/** 加密解密模式 */
-	private SM2Mode mode;
-
-	/**
-	 * 构造
-	 */
-	public SM2Engine() {
-		this(new SM3Digest());
-	}
-
-	/**
-	 * 构造
-	 * 
-	 * @param mode SM2密钥生成模式,可选C1C2C3和C1C3C2
-	 */
-	public SM2Engine(SM2Mode mode) {
-		this(new SM3Digest(), mode);
-	}
-
-	/**
-	 * 构造
-	 * 
-	 * @param digest 摘要算法啊
-	 */
-	public SM2Engine(Digest digest) {
-		this(digest, null);
-	}
-
-	/**
-	 * 构造
-	 * 
-	 * @param digest 摘要算法啊
-	 * @param mode SM2密钥生成模式,可选C1C2C3和C1C3C2
-	 */
-	public SM2Engine(Digest digest, SM2Mode mode) {
-		this.digest = digest;
-		this.mode = ObjectUtil.defaultIfNull(mode, SM2Mode.C1C3C2);
-	}
-
-	/**
-	 * 初始化引擎
-	 * 
-	 * @param forEncryption 是否为加密模式
-	 * @param param {@link CipherParameters},此处应为{@link ParametersWithRandom}(加密时)或{@link ECKeyParameters}(解密时)
-	 */
-	public void init(boolean forEncryption, CipherParameters param) {
-		this.forEncryption = forEncryption;
-
-		if (param instanceof ParametersWithRandom) {
-			final ParametersWithRandom rParam = (ParametersWithRandom) param;
-			this.ecKey = (ECKeyParameters) rParam.getParameters();
-			this.random = rParam.getRandom();
-		} else {
-			this.ecKey = (ECKeyParameters) param;
-		}
-		this.ecParams = this.ecKey.getParameters();
-
-		if (forEncryption) {
-			// 检查曲线点
-			final ECPoint ecPoint = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());
-			if (ecPoint.isInfinity()) {
-				throw new IllegalArgumentException("invalid key: [h]Q at infinity");
-			}
-
-			// 检查随机参数
-			if (null == this.random) {
-				this.random = CryptoServicesRegistrar.getSecureRandom();
-			}
-		}
-
-		// 曲线位长度
-		this.curveLength = (this.ecParams.getCurve().getFieldSize() + 7) / 8;
-	}
-
-	/**
-	 * 处理块,包括加密和解密
-	 * 
-	 * @param in 数据
-	 * @param inOff 数据开始位置
-	 * @param inLen 数据长度
-	 * @return 结果
-	 */
-	public byte[] processBlock(byte[] in, int inOff, int inLen) {
-		if (forEncryption) {
-			return encrypt(in, inOff, inLen);
-		} else {
-			return decrypt(in, inOff, inLen);
-		}
-	}
-
-	/**
-	 * 设置加密类型
-	 * 
-	 * @param mode {@link SM2Mode}
-	 * @return this
-	 */
-	public SM2Engine setMode(SM2Mode mode) {
-		this.mode = mode;
-		return this;
-	}
-
-	/**
-	 * SM2算法模式<br>
-	 * 在SM2算法中,C1C2C3为旧标准模式,C1C3C2为新标准模式
-	 * 
-	 * @author looly
-	 *
-	 */
-	public enum SM2Mode {
-		C1C2C3, C1C3C2
-	}
-
-	protected ECMultiplier createBasePointMultiplier() {
-		return new FixedPointCombMultiplier();
-	}
-
-	// --------------------------------------------------------------------------------------------------- Private method start
-	/**
-	 * 加密
-	 * 
-	 * @param in 数据
-	 * @param inOff 位置
-	 * @param inLen 长度
-	 * @return 密文
-	 */
-	private byte[] encrypt(byte[] in, int inOff, int inLen) {
-		// 加密数据
-		byte[] c2 = new byte[inLen];
-		System.arraycopy(in, inOff, c2, 0, c2.length);
-
-		final ECMultiplier multiplier = createBasePointMultiplier();
-
-		byte[] c1;
-		ECPoint kPB;
-		BigInteger k;
-		do {
-			k = nextK();
-			// 产生随机数计算出曲线点C1
-			c1 = multiplier.multiply(ecParams.getG(), k).normalize().getEncoded(false);
-			kPB = ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();
-			kdf(kPB, c2);
-		} while (notEncrypted(c2, in, inOff));
-
-		// 杂凑值,效验数据
-		byte[] c3 = new byte[digest.getDigestSize()];
-
-		addFieldElement(kPB.getAffineXCoord());
-		this.digest.update(in, inOff, inLen);
-		addFieldElement(kPB.getAffineYCoord());
-
-		this.digest.doFinal(c3, 0);
-
-		// 按照对应模式输出结果
-		if (mode == SM2Mode.C1C3C2) {
-			return Arrays.concatenate(c1, c3, c2);
-		}
-		return Arrays.concatenate(c1, c2, c3);
-	}
-
-	/**
-	 * 解密,只支持私钥解密
-	 * 
-	 * @param in 密文
-	 * @param inOff 位置
-	 * @param inLen 长度
-	 * @return 解密后的内容
-	 */
-	private byte[] decrypt(byte[] in, int inOff, int inLen) {
-		// 获取曲线点
-		final byte[] c1 = new byte[this.curveLength * 2 + 1];
-		System.arraycopy(in, inOff, c1, 0, c1.length);
-
-		ECPoint c1P = this.ecParams.getCurve().decodePoint(c1);
-		if (c1P.multiply(this.ecParams.getH()).isInfinity()) {
-			throw new CryptoException("[h]C1 at infinity");
-		}
-		c1P = c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();
-
-		final int digestSize = this.digest.getDigestSize();
-
-		// 解密C2数据
-		final byte[] c2 = new byte[inLen - c1.length - digestSize];
-
-		if (SM2Mode.C1C3C2 == this.mode) {
-			// C2位于第三部分
-			System.arraycopy(in, inOff + c1.length + digestSize, c2, 0, c2.length);
-		} else {
-			// C2位于第二部分
-			System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);
-		}
-		kdf(c1P, c2);
-
-		// 使用摘要验证C2数据
-		final byte[] c3 = new byte[digestSize];
-
-		addFieldElement(c1P.getAffineXCoord());
-		this.digest.update(c2, 0, c2.length);
-		addFieldElement(c1P.getAffineYCoord());
-		this.digest.doFinal(c3, 0);
-
-		int check = 0;
-		for (int i = 0; i != c3.length; i++) {
-			check |= c3[i] ^ in[inOff + c1.length + ((SM2Mode.C1C3C2 == this.mode) ? 0 : c2.length) + i];
-		}
-
-		Arrays.fill(c1, (byte) 0);
-		Arrays.fill(c3, (byte) 0);
-
-		if (check != 0) {
-			Arrays.fill(c2, (byte) 0);
-			throw new CryptoException("invalid cipher text");
-		}
-
-		return c2;
-	}
-
-	private boolean notEncrypted(byte[] encData, byte[] in, int inOff) {
-		for (int i = 0; i != encData.length; i++) {
-			if (encData[i] != in[inOff + i]) {
-				return false;
-			}
-		}
-		return true;
-	}
-
-	/**
-	 * 解密数据
-	 * 
-	 * @param c1 c1点
-	 * @param encData 密文
-	 */
-	private void kdf(ECPoint c1, byte[] encData) {
-		final Digest digest = this.digest;
-		int digestSize = digest.getDigestSize();
-		byte[] buf = new byte[Math.max(4, digestSize)];
-		int off = 0;
-
-		Memoable memo = null;
-		Memoable copy = null;
-
-		if (digest instanceof Memoable) {
-			addFieldElement(c1.getAffineXCoord());
-			addFieldElement(c1.getAffineYCoord());
-			memo = (Memoable) digest;
-			copy = memo.copy();
-		}
-
-		int ct = 0;
-
-		while (off < encData.length) {
-			if (memo != null) {
-				memo.reset(copy);
-			} else {
-				addFieldElement(c1.getAffineXCoord());
-				addFieldElement(c1.getAffineYCoord());
-			}
-
-			Pack.intToBigEndian(++ct, buf, 0);
-			digest.update(buf, 0, 4);
-			digest.doFinal(buf, 0);
-
-			int xorLen = Math.min(digestSize, encData.length - off);
-			xor(encData, buf, off, xorLen);
-			off += xorLen;
-		}
-	}
-
-	/**
-	 * 异或
-	 * 
-	 * @param data 数据
-	 * @param kdfOut kdf输出值
-	 * @param dOff d偏移
-	 * @param dRemaining d剩余
-	 */
-	private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {
-		for (int i = 0; i != dRemaining; i++) {
-			data[dOff + i] ^= kdfOut[i];
-		}
-	}
-
-	/**
-	 * 下一个K值
-	 * 
-	 * @return K值
-	 */
-	private BigInteger nextK() {
-		final int qBitLength = this.ecParams.getN().bitLength();
-
-		BigInteger k;
-		do {
-			k = new BigInteger(qBitLength, this.random);
-		} while (k.equals(ECConstants.ZERO) || k.compareTo(this.ecParams.getN()) >= 0);
-
-		return k;
-	}
-
-	/**
-	 * 增加字段节点
-	 * 
-	 * @param v 节点
-	 */
-	private void addFieldElement(ECFieldElement v) {
-		final byte[] p = BigIntegers.asUnsignedByteArray(this.curveLength, v.toBigInteger());
-		this.digest.update(p, 0, p.length);
-	}
-	// --------------------------------------------------------------------------------------------------- Private method start
-}

+ 13 - 14
hutool-crypto/src/test/java/cn/hutool/crypto/test/BCUtilTest.java

@@ -1,40 +1,39 @@
 package cn.hutool.crypto.test;
 
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.junit.Assert;
-import org.junit.Test;
-
 import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.crypto.BCUtil;
+import cn.hutool.crypto.PemUtil;
 import cn.hutool.crypto.asymmetric.KeyType;
 import cn.hutool.crypto.asymmetric.RSA;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+public class PemUtilTest {
 
-public class BCUtilTest {
-	
 	@Test
 	public void readPrivateKeyTest() {
-		PrivateKey privateKey = BCUtil.readPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
+		PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
 		Assert.assertNotNull(privateKey);
 	}
 	
 	@Test
 	public void readPublicKeyTest() {
-		PublicKey publicKey = BCUtil.readPublicKey(ResourceUtil.getStream("test_public_key.csr"));
+		PublicKey publicKey = PemUtil.readPemPublicKey(ResourceUtil.getStream("test_public_key.csr"));
 		Assert.assertNotNull(publicKey);
 	}
 
 	@Test
 	public void readPemKeyTest() {
-		PublicKey publicKey = (PublicKey) BCUtil.readPemKey(ResourceUtil.getStream("test_public_key.csr"));
+		PublicKey publicKey = (PublicKey) PemUtil.readPemKey(ResourceUtil.getStream("test_public_key.csr"));
 		Assert.assertNotNull(publicKey);
 	}
 
 	@Test
 	public void validateKey() {
-		PrivateKey privateKey = BCUtil.readPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
-		PublicKey publicKey = BCUtil.readPublicKey(ResourceUtil.getStream("test_public_key.csr"));
+		PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
+		PublicKey publicKey = PemUtil.readPemPublicKey(ResourceUtil.getStream("test_public_key.csr"));
 		
 		RSA rsa = new RSA(privateKey, publicKey);
 		String str = "你好,Hutool";//测试字符串

+ 8 - 11
hutool-crypto/src/test/java/cn/hutool/crypto/test/SM2Test.java

@@ -1,13 +1,5 @@
 package cn.hutool.crypto.test;
 
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import cn.hutool.core.lang.Console;
-import org.junit.Assert;
-import org.junit.Test;
-
 import cn.hutool.core.codec.Base64;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.HexUtil;
@@ -17,7 +9,13 @@ import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.SmUtil;
 import cn.hutool.crypto.asymmetric.KeyType;
 import cn.hutool.crypto.asymmetric.SM2;
-import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 
 /**
  * SM2算法单元测试
@@ -48,10 +46,9 @@ public class SM2Test {
 		KeyPair pair = SecureUtil.generateKeyPair("SM2");
 		byte[] privateKey = pair.getPrivate().getEncoded();
 		byte[] publicKey = pair.getPublic().getEncoded();
-		Console.log(HexUtil.encodeHexStr(publicKey));
 
 		SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
-		sm2.setMode(SM2Mode.C1C3C2);
+		sm2.setMode(SM2Engine.Mode.C1C2C3);
 
 		// 公钥加密,私钥解密
 		byte[] encrypt = sm2.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);

+ 1 - 1
hutool-db/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-db</artifactId>

+ 1 - 1
hutool-dfa/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-dfa</artifactId>

+ 1 - 1
hutool-extra/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-extra</artifactId>

+ 1 - 1
hutool-http/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-http</artifactId>

+ 1 - 1
hutool-json/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-json</artifactId>

+ 1 - 1
hutool-log/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-log</artifactId>

+ 1 - 1
hutool-poi/pom.xml

@@ -8,7 +8,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-poi</artifactId>

+ 1 - 1
hutool-script/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-script</artifactId>

+ 1 - 1
hutool-setting/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-setting</artifactId>

+ 1 - 1
hutool-socket/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-socket</artifactId>

+ 1 - 1
hutool-system/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.1.6-SNAPSHOT</version>
+		<version>5.2.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-system</artifactId>

+ 1 - 1
pom.xml

@@ -8,7 +8,7 @@
 
 	<groupId>cn.hutool</groupId>
 	<artifactId>hutool-parent</artifactId>
-	<version>5.1.6-SNAPSHOT</version>
+	<version>5.2.0-SNAPSHOT</version>
 	<name>hutool</name>
 	<description>提供丰富的Java工具方法</description>
 	<url>https://github.com/looly/hutool</url>