diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SecureUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SecureUtil.java index a558e8951..6b6963d41 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SecureUtil.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SecureUtil.java @@ -175,7 +175,7 @@ public class SecureUtil { * @return {@link Digester} */ public static MD5 md5() { - return new MD5(); + return MD5.of(); } /** @@ -185,7 +185,7 @@ public class SecureUtil { * @return MD5字符串 */ public static String md5(final String data) { - return new MD5().digestHex(data); + return MD5.of().digestHex(data); } /** @@ -195,7 +195,7 @@ public class SecureUtil { * @return MD5字符串 */ public static String md5(final InputStream data) { - return new MD5().digestHex(data); + return MD5.of().digestHex(data); } /** @@ -205,7 +205,7 @@ public class SecureUtil { * @return MD5字符串 */ public static String md5(final File dataFile) { - return new MD5().digestHex(dataFile); + return MD5.of().digestHex(dataFile); } /** @@ -523,7 +523,8 @@ public class SecureUtil { } /** - * 创建{@link Cipher} + * 创建{@link Cipher}
+ * 当provider为{@code null}时,使用{@link GlobalProviderFactory}查找提供方,找不到使用JDK默认提供方。 * * @param algorithm 算法 * @return {@link Cipher} @@ -543,18 +544,22 @@ public class SecureUtil { } /** - * 创建{@link MessageDigest} + * 创建{@link MessageDigest}
+ * 当provider为{@code null}时,使用{@link GlobalProviderFactory}查找提供方,找不到使用JDK默认提供方。 * * @param algorithm 算法 + * @param provider 算法提供方,{@code null}表示使用{@link GlobalProviderFactory}找到的提供方。 * @return {@link MessageDigest} - * @since 4.5.2 */ - public static MessageDigest createMessageDigest(final String algorithm) { - final Provider provider = GlobalProviderFactory.getProvider(); + public static MessageDigest createMessageDigest(final String algorithm, Provider provider) { + if (null == provider) { + provider = GlobalProviderFactory.getProvider(); + } final MessageDigest messageDigest; try { - messageDigest = (null == provider) ? MessageDigest.getInstance(algorithm) : MessageDigest.getInstance(algorithm, provider); + messageDigest = (null == provider) ? MessageDigest.getInstance(algorithm) : + MessageDigest.getInstance(algorithm, provider); } catch (final NoSuchAlgorithmException e) { throw new CryptoException(e); } @@ -562,6 +567,20 @@ public class SecureUtil { return messageDigest; } + /** + * 创建{@link MessageDigest},使用JDK默认的Provider
+ * + * @param algorithm 算法 + * @return {@link MessageDigest} + */ + public static MessageDigest createJdkMessageDigest(final String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + throw new CryptoException(e); + } + } + /** * 创建{@link Mac} * diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigestUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigestUtil.java index ad0535d1d..9bd86048e 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigestUtil.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/DigestUtil.java @@ -37,7 +37,7 @@ public class DigestUtil { * @return MD5摘要 */ public static byte[] md5(final byte[] data) { - return new MD5().digest(data); + return MD5.of().digest(data); } /** @@ -48,7 +48,7 @@ public class DigestUtil { * @return MD5摘要 */ public static byte[] md5(final String data, final Charset charset) { - return new MD5().digest(data, charset); + return MD5.of().digest(data, charset); } /** @@ -68,7 +68,7 @@ public class DigestUtil { * @return MD5摘要 */ public static byte[] md5(final InputStream data) { - return new MD5().digest(data); + return MD5.of().digest(data); } /** @@ -78,7 +78,7 @@ public class DigestUtil { * @return MD5摘要 */ public static byte[] md5(final File file) { - return new MD5().digest(file); + return MD5.of().digest(file); } /** @@ -88,7 +88,7 @@ public class DigestUtil { * @return MD5摘要的16进制表示 */ public static String md5Hex(final byte[] data) { - return new MD5().digestHex(data); + return MD5.of().digestHex(data); } /** @@ -100,7 +100,7 @@ public class DigestUtil { * @since 4.6.0 */ public static String md5Hex(final String data, final Charset charset) { - return new MD5().digestHex(data, charset); + return MD5.of().digestHex(data, charset); } /** @@ -120,7 +120,7 @@ public class DigestUtil { * @return MD5摘要的16进制表示 */ public static String md5Hex(final InputStream data) { - return new MD5().digestHex(data); + return MD5.of().digestHex(data); } /** @@ -130,7 +130,7 @@ public class DigestUtil { * @return MD5摘要的16进制表示 */ public static String md5Hex(final File file) { - return new MD5().digestHex(file); + return MD5.of().digestHex(file); } // endregion @@ -145,7 +145,7 @@ public class DigestUtil { * @since 4.6.0 */ public static String md5Hex16(final byte[] data) { - return new MD5().digestHex16(data); + return MD5.of().digestHex16(data); } /** @@ -157,7 +157,7 @@ public class DigestUtil { * @since 4.6.0 */ public static String md5Hex16(final String data, final Charset charset) { - return new MD5().digestHex16(data, charset); + return MD5.of().digestHex16(data, charset); } /** @@ -179,7 +179,7 @@ public class DigestUtil { * @since 4.6.0 */ public static String md5Hex16(final InputStream data) { - return new MD5().digestHex16(data); + return MD5.of().digestHex16(data); } /** @@ -190,7 +190,7 @@ public class DigestUtil { * @since 4.6.0 */ public static String md5Hex16(final File file) { - return new MD5().digestHex16(file); + return MD5.of().digestHex16(file); } /** diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/Digester.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/Digester.java index b0997a921..427caf6c3 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/Digester.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/Digester.java @@ -12,15 +12,17 @@ package org.dromara.hutool.crypto.digest; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.codec.HexUtil; +import org.dromara.hutool.core.func.SimpleWrapper; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; -import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.util.ByteUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.crypto.CryptoException; import org.dromara.hutool.crypto.SecureUtil; +import org.dromara.hutool.crypto.provider.GlobalProviderFactory; import java.io.File; import java.io.IOException; @@ -28,7 +30,6 @@ import java.io.InputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.Provider; /** @@ -36,20 +37,25 @@ import java.security.Provider; * 注意:此对象实例化后为非线程安全! * * @author Looly - * */ -public class Digester implements Serializable { +public class Digester extends SimpleWrapper 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); }