implements Serializable {
private static final long serialVersionUID = 1L;
- private MessageDigest digest;
- /** 盐值 */
+ /**
+ * 盐值
+ */
protected byte[] salt;
- /** 加盐位置,即将盐值字符串放置在数据的index数,默认0 */
+ /**
+ * 加盐位置,即将盐值字符串放置在数据的index数,默认0
+ */
protected int saltPosition;
- /** 散列次数 */
+ /**
+ * 散列次数
+ */
protected int digestCount;
// ------------------------------------------------------------------------------------------- Constructor start
+
/**
* 构造
*
@@ -72,45 +78,33 @@ public class Digester implements Serializable {
* 构造
*
* @param algorithm 算法
- * @param provider 算法提供者,null表示JDK默认,可以引入Bouncy Castle等来提供更多算法支持
+ * @param provider 算法提供者,{@code null}表示使用{@link GlobalProviderFactory}找到的提供方。
* @since 4.5.1
*/
public Digester(final DigestAlgorithm algorithm, final Provider provider) {
- init(algorithm.getValue(), provider);
+ this(algorithm.getValue(), provider);
}
/**
* 构造
*
* @param algorithm 算法
- * @param provider 算法提供者,null表示JDK默认,可以引入Bouncy Castle等来提供更多算法支持
+ * @param provider 算法提供者,{@code null}表示使用{@link GlobalProviderFactory}找到的提供方。
* @since 4.5.1
*/
public Digester(final String algorithm, final Provider provider) {
- init(algorithm, provider);
+ this(SecureUtil.createMessageDigest(algorithm, provider));
}
- // ------------------------------------------------------------------------------------------- Constructor end
/**
- * 初始化
+ * 构造
*
- * @param algorithm 算法
- * @param provider 算法提供者,null表示JDK默认,可以引入Bouncy Castle等来提供更多算法支持
- * @return Digester
- * @throws CryptoException Cause by IOException
+ * @param messageDigest {@link MessageDigest}
*/
- public Digester init(final String algorithm, final Provider provider) {
- if(null == provider) {
- this.digest = SecureUtil.createMessageDigest(algorithm);
- }else {
- try {
- this.digest = MessageDigest.getInstance(algorithm, provider);
- } catch (final NoSuchAlgorithmException e) {
- throw new CryptoException(e);
- }
- }
- return this;
+ public Digester(final MessageDigest messageDigest) {
+ super(messageDigest);
}
+ // ------------------------------------------------------------------------------------------- Constructor end
/**
* 设置加盐内容
@@ -131,14 +125,13 @@ public class Digester implements Serializable {
*
* data: 0123456
*
- *
+ *
* 则当saltPosition = 2时,盐位于data的1和2中间,即第二个空隙,即:
*
*
* data: 01[salt]23456
*
*
- *
* @param saltPosition 盐的位置
* @return this
* @since 4.4.3
@@ -166,7 +159,7 @@ public class Digester implements Serializable {
* @since 4.5.1
*/
public Digester reset() {
- this.digest.reset();
+ this.raw.reset();
return this;
}
@@ -175,7 +168,7 @@ public class Digester implements Serializable {
/**
* 生成文件摘要
*
- * @param data 被摘要数据
+ * @param data 被摘要数据
* @param charset 编码
* @return 摘要
* @since 4.6.0
@@ -197,7 +190,7 @@ public class Digester implements Serializable {
/**
* 生成文件摘要,并转为16进制字符串
*
- * @param data 被摘要数据
+ * @param data 被摘要数据
* @param charset 编码
* @return 摘要
* @since 4.6.0
@@ -260,11 +253,12 @@ public class Digester implements Serializable {
// 加盐在末尾,自动忽略空盐值
result = doDigest(data, this.salt);
} else if (ArrayUtil.isNotEmpty(this.salt)) {
+ final MessageDigest digest = getRaw();
// 加盐在中间
- this.digest.update(data, 0, this.saltPosition);
- this.digest.update(this.salt);
- this.digest.update(data, this.saltPosition, data.length - this.saltPosition);
- result = this.digest.digest();
+ digest.update(data, 0, this.saltPosition);
+ digest.update(this.salt);
+ digest.update(data, this.saltPosition, data.length - this.saltPosition);
+ result = digest.digest();
} else {
// 无加盐
result = doDigest(data);
@@ -307,7 +301,7 @@ public class Digester implements Serializable {
/**
* 生成摘要
*
- * @param data {@link InputStream} 数据流
+ * @param data {@link InputStream} 数据流
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
* @return 摘要bytes
* @throws IORuntimeException IO异常
@@ -335,7 +329,7 @@ public class Digester implements Serializable {
* 生成摘要,并转为16进制字符串
* 使用默认缓存大小,见 {@link IoUtil#DEFAULT_BUFFER_SIZE}
*
- * @param data 被摘要数据
+ * @param data 被摘要数据
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
* @return 摘要
*/
@@ -343,15 +337,6 @@ public class Digester implements Serializable {
return HexUtil.encodeHexStr(digest(data, bufferLength));
}
- /**
- * 获得 {@link MessageDigest}
- *
- * @return {@link MessageDigest}
- */
- public MessageDigest getDigest() {
- return digest;
- }
-
/**
* 获取散列长度,0表示不支持此方法
*
@@ -359,39 +344,42 @@ public class Digester implements Serializable {
* @since 4.5.0
*/
public int getDigestLength() {
- return this.digest.getDigestLength();
+ return this.raw.getDigestLength();
}
// -------------------------------------------------------------------------------- Private method start
+
/**
* 生成摘要
*
- * @param data {@link InputStream} 数据流
+ * @param data {@link InputStream} 数据流
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
* @return 摘要bytes
* @throws IOException 从流中读取数据引发的IO异常
*/
private byte[] digestWithoutSalt(final InputStream data, final int bufferLength) throws IOException {
+ final MessageDigest digest = this.raw;
final byte[] buffer = new byte[bufferLength];
int read;
while ((read = data.read(buffer, 0, bufferLength)) > -1) {
- this.digest.update(buffer, 0, read);
+ digest.update(buffer, 0, read);
}
- return this.digest.digest();
+ return digest.digest();
}
/**
* 生成摘要
*
- * @param data {@link InputStream} 数据流
+ * @param data {@link InputStream} 数据流
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
* @return 摘要bytes
* @throws IOException 从流中读取数据引发的IO异常
*/
private byte[] digestWithSalt(final InputStream data, final int bufferLength) throws IOException {
+ final MessageDigest digest = this.raw;
if (this.saltPosition <= 0) {
// 加盐在开头
- this.digest.update(this.salt);
+ digest.update(this.salt);
}
final byte[] buffer = new byte[bufferLength];
@@ -404,19 +392,19 @@ public class Digester implements Serializable {
digest.update(buffer, 0, total - this.saltPosition);
}
// 加盐在中间
- this.digest.update(this.salt);
- this.digest.update(buffer, total - this.saltPosition, read);
+ digest.update(this.salt);
+ digest.update(buffer, total - this.saltPosition, read);
} else {
- this.digest.update(buffer, 0, read);
+ digest.update(buffer, 0, read);
}
}
if (total < this.saltPosition) {
// 加盐在末尾
- this.digest.update(this.salt);
+ digest.update(this.salt);
}
- return this.digest.digest();
+ return digest.digest();
}
/**
@@ -427,12 +415,13 @@ public class Digester implements Serializable {
* @since 4.4.3
*/
private byte[] doDigest(final byte[]... datas) {
+ final MessageDigest digest = this.raw;
for (final byte[] data : datas) {
if (null != data) {
- this.digest.update(data);
+ digest.update(data);
}
}
- return this.digest.digest();
+ return digest.digest();
}
/**
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigesterFactory.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigesterFactory.java
new file mode 100644
index 000000000..914adef7f
--- /dev/null
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigesterFactory.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.crypto.digest;
+
+import org.dromara.hutool.crypto.SecureUtil;
+
+import java.security.MessageDigest;
+
+/**
+ * {@link Digester}创建简单工厂,用于生产{@link Digester}对象
+ *
+ * @author looly
+ */
+public class DigesterFactory {
+
+ private final MessageDigest prototype;
+ private final boolean cloneSupport;
+
+ /**
+ * 创建工厂
+ *
+ * @param algorithm 算法
+ * @return DigesterFactory
+ */
+ public static DigesterFactory of(final String algorithm) {
+ return of(SecureUtil.createJdkMessageDigest(algorithm));
+ }
+
+ /**
+ * 创建工厂
+ *
+ * @param messageDigest {@link MessageDigest}
+ * @return DigesterFactory
+ */
+ public static DigesterFactory of(final MessageDigest messageDigest) {
+ return new DigesterFactory(messageDigest);
+ }
+
+ /**
+ * 构造
+ *
+ * @param messageDigest {@link MessageDigest}模板
+ */
+ private DigesterFactory(final MessageDigest messageDigest) {
+ this.prototype = messageDigest;
+ this.cloneSupport = checkCloneSupport(messageDigest);
+ }
+
+ /**
+ * 创建{@link Digester}
+ *
+ * @return {@link Digester}
+ */
+ public Digester createDigester() {
+ return new Digester(createMessageDigester());
+ }
+
+ /**
+ * 创建{@link MessageDigest}
+ *
+ * @return {@link MessageDigest}
+ */
+ public MessageDigest createMessageDigester() {
+ if (cloneSupport) {
+ try {
+ return (MessageDigest) prototype.clone();
+ } catch (final CloneNotSupportedException ignore) {
+ // ignore
+ }
+ }
+ return SecureUtil.createJdkMessageDigest(prototype.getAlgorithm());
+ }
+
+ /**
+ * 检查{@link MessageDigest}对象是否支持clone方法
+ *
+ * @param messageDigest {@link MessageDigest}
+ * @return 是否支持clone方法
+ */
+ private static boolean checkCloneSupport(final MessageDigest messageDigest) {
+ try {
+ messageDigest.clone();
+ return true;
+ } catch (final CloneNotSupportedException e) {
+ return false;
+ }
+ }
+}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/MD5.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/MD5.java
index 0ecf3b2b6..05544d07b 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/MD5.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/MD5.java
@@ -25,6 +25,8 @@ import java.nio.charset.Charset;
public class MD5 extends Digester {
private static final long serialVersionUID = 1L;
+ private static final DigesterFactory FACTORY = DigesterFactory.of(DigestAlgorithm.MD5.getValue());
+
/**
* 创建MD5实例
*
@@ -39,7 +41,7 @@ public class MD5 extends Digester {
* 构造
*/
public MD5() {
- super(DigestAlgorithm.MD5);
+ super(FACTORY.createMessageDigester());
}
/**
@@ -54,7 +56,7 @@ public class MD5 extends Digester {
/**
* 构造
*
- * @param salt 盐值
+ * @param salt 盐值
* @param digestCount 摘要次数,当此值小于等于1,默认为1。
*/
public MD5(final byte[] salt, final int digestCount) {
@@ -64,9 +66,9 @@ public class MD5 extends Digester {
/**
* 构造
*
- * @param salt 盐值
+ * @param salt 盐值
* @param saltPosition 加盐位置,即将盐值字符串放置在数据的index数,默认0
- * @param digestCount 摘要次数,当此值小于等于1,默认为1。
+ * @param digestCount 摘要次数,当此值小于等于1,默认为1。
*/
public MD5(final byte[] salt, final int saltPosition, final int digestCount) {
this();
@@ -78,7 +80,7 @@ public class MD5 extends Digester {
/**
* 生成16位MD5摘要
*
- * @param data 数据
+ * @param data 数据
* @param charset 编码
* @return 16位MD5摘要
* @since 4.6.0
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLSaltParser.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLSaltParser.java
index 85e1d720e..4688f5313 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLSaltParser.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLSaltParser.java
@@ -41,7 +41,7 @@ public class OpenSSLSaltParser {
* @return OpenSSLSaltParser
*/
public static OpenSSLSaltParser ofMd5(final int keyLength, final String algorithm) {
- return of(new MD5().getDigest(), keyLength, algorithm);
+ return of(MD5.of().getRaw(), keyLength, algorithm);
}
/**
diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/digest/Md5Test.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/digest/Md5Test.java
index 752d251be..4d8b5cbab 100644
--- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/digest/Md5Test.java
+++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/digest/Md5Test.java
@@ -13,7 +13,7 @@ public class Md5Test {
@Test
public void md5To16Test() {
- final String hex16 = new MD5().digestHex16("中国");
+ final String hex16 = MD5.of().digestHex16("中国");
Assertions.assertEquals(16, hex16.length());
Assertions.assertEquals("cb143acd6c929826", hex16);
}