diff --git a/CHANGELOG.md b/CHANGELOG.md
index 970001c6b..fd91bdd0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
* 【extra 】 增加PinyinUtil,封装TinyPinyin
* 【extra 】 Ftp和Sftp增加FtpConfig,提供超时等更多可选参数
* 【extra 】 SpringUtil增加getActiveProfiles、getBeansOfType、getBeanNamesForType方法(issue#I1FXF3@Gitee)
+* 【bloomFilter】 避免布隆过滤器数字溢出(pr#119@Gitee)
+* 【core 】 增加IoUtil.writeObj(issue#I1FZIE)
### Bug修复
* 【core 】 修复URLBuilder中请求参数有`&`导致的问题(issue#850@Github)
diff --git a/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BitMapBloomFilter.java b/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BitMapBloomFilter.java
index a4cc97d67..b1da492d6 100644
--- a/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BitMapBloomFilter.java
+++ b/hutool-bloomFilter/src/main/java/cn/hutool/bloomfilter/BitMapBloomFilter.java
@@ -13,35 +13,36 @@ import cn.hutool.core.util.NumberUtil;
* 2.散列hash映射到数组的bit位置
* 3.验证
* 此实现方式可以指定Hash算法
- *
+ *
* @author Ansj
*/
-public class BitMapBloomFilter implements BloomFilter{
+public class BitMapBloomFilter implements BloomFilter {
private static final long serialVersionUID = 1L;
private BloomFilter[] filters;
/**
* 构造,使用默认的5个过滤器
+ *
* @param m M值决定BitMap的大小
*/
public BitMapBloomFilter(int m) {
- long mNum =NumberUtil.div(String.valueOf(m), String.valueOf(5)).longValue();
+ long mNum = NumberUtil.div(String.valueOf(m), String.valueOf(5)).longValue();
long size = mNum * 1024 * 1024 * 8;
-
+
filters = new BloomFilter[]{
- new DefaultFilter(size),
- new ELFFilter(size),
- new JSFilter(size),
- new PJWFilter(size),
- new SDBMFilter(size)
+ new DefaultFilter(size),
+ new ELFFilter(size),
+ new JSFilter(size),
+ new PJWFilter(size),
+ new SDBMFilter(size)
};
}
/**
* 使用自定的多个过滤器建立BloomFilter
- *
- * @param m M值决定BitMap的大小
+ *
+ * @param m M值决定BitMap的大小
* @param filters Bloom过滤器列表
*/
public BitMapBloomFilter(int m, BloomFilter... filters) {
@@ -51,6 +52,7 @@ public class BitMapBloomFilter implements BloomFilter{
/**
* 增加字符串到Filter映射中
+ *
* @param str 字符串
*/
@Override
@@ -64,6 +66,7 @@ public class BitMapBloomFilter implements BloomFilter{
/**
* 是否可能包含此字符串,此处存在误判
+ *
* @param str 字符串
* @return 是否存在
*/
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
index 0966d544a..cda8b5efe 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
@@ -984,6 +984,19 @@ public class IoUtil {
}
}
+ /**
+ * 将多部分内容写到流中
+ *
+ * @param out 输出流
+ * @param isCloseOut 写入完毕是否关闭输出流
+ * @param obj 写入的对象内容
+ * @throws IORuntimeException IO异常
+ * @since 5.3.3
+ */
+ public static void writeObj(OutputStream out, boolean isCloseOut, Serializable obj) throws IORuntimeException {
+ writeObjects(out, isCloseOut, obj);
+ }
+
/**
* 将多部分内容写到流中
*
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
index 4daa0f7ab..363c5057a 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
@@ -9,12 +9,12 @@ import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.digest.mac.MacEngine;
import cn.hutool.crypto.digest.mac.MacEngineFactory;
-import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
+import java.security.Key;
/**
* HMAC摘要算法
@@ -37,7 +37,7 @@ public class HMac implements Serializable {
* @param algorithm 算法 {@link HmacAlgorithm}
*/
public HMac(HmacAlgorithm algorithm) {
- this(algorithm, (SecretKey)null);
+ this(algorithm, (Key)null);
}
/**
@@ -54,7 +54,7 @@ public class HMac implements Serializable {
* @param algorithm 算法 {@link HmacAlgorithm}
* @param key 密钥
*/
- public HMac(HmacAlgorithm algorithm, SecretKey key) {
+ public HMac(HmacAlgorithm algorithm, Key key) {
this(algorithm.getValue(), key);
}
@@ -74,7 +74,7 @@ public class HMac implements Serializable {
* @param key 密钥
* @since 4.5.13
*/
- public HMac(String algorithm, SecretKey key) {
+ public HMac(String algorithm, Key key) {
this(MacEngineFactory.createEngine(algorithm, key));
}
@@ -223,5 +223,23 @@ public class HMac implements Serializable {
public String digestHex(InputStream data, int bufferLength) {
return HexUtil.encodeHexStr(digest(data, bufferLength));
}
-
+
+ /**
+ * 获取MAC算法块长度
+ * @return MAC算法块长度
+ * @since 5.3.3
+ */
+ public int getMacLength(){
+ return this.engine.getMacLength();
+ }
+
+ /**
+ * 获取算法
+ *
+ * @return 算法
+ * @since 5.3.3
+ */
+ public String getAlgorithm() {
+ return this.engine.getAlgorithm();
+ }
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/BCHMacEngine.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/BCHMacEngine.java
index e0db7ee49..9f61e5c0a 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/BCHMacEngine.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/BCHMacEngine.java
@@ -1,16 +1,15 @@
package cn.hutool.crypto.digest.mac;
-import java.io.IOException;
-import java.io.InputStream;
-
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.crypto.CryptoException;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.crypto.CryptoException;
+import java.io.IOException;
+import java.io.InputStream;
/**
* BouncyCastle的HMAC算法实现引擎,使用{@link Mac} 实现摘要
@@ -94,4 +93,13 @@ public class BCHMacEngine implements MacEngine {
return mac;
}
+ @Override
+ public int getMacLength() {
+ return mac.getMacSize();
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return this.mac.getAlgorithmName();
+ }
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/DefaultHMacEngine.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/DefaultHMacEngine.java
index 6136347e4..2ea6aef98 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/DefaultHMacEngine.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/DefaultHMacEngine.java
@@ -1,16 +1,16 @@
package cn.hutool.crypto.digest.mac;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.SecureUtil;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Key;
+
/**
* 默认的HMAC算法实现引擎,使用{@link Mac} 实现摘要
* 当引入BouncyCastle库时自动使用其作为Provider
@@ -39,7 +39,7 @@ public class DefaultHMacEngine implements MacEngine {
* @param key 密钥
* @since 4.5.13
*/
- public DefaultHMacEngine(String algorithm, SecretKey key) {
+ public DefaultHMacEngine(String algorithm, Key key) {
init(algorithm, key);
}
// ------------------------------------------------------------------------------------------- Constructor end
@@ -61,7 +61,7 @@ public class DefaultHMacEngine implements MacEngine {
* @return this
* @throws CryptoException Cause by IOException
*/
- public DefaultHMacEngine init(String algorithm, SecretKey key){
+ public DefaultHMacEngine init(String algorithm, Key key){
try {
mac = SecureUtil.createMac(algorithm);
if(null == key){
@@ -106,4 +106,14 @@ public class DefaultHMacEngine implements MacEngine {
public Mac getMac() {
return mac;
}
+
+ @Override
+ public int getMacLength() {
+ return mac.getMacLength();
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return this.mac.getAlgorithm();
+ }
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngine.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngine.java
index 85fb8ec95..04d4d7f42 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngine.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngine.java
@@ -1,9 +1,9 @@
package cn.hutool.crypto.digest.mac;
-import java.io.InputStream;
-
import cn.hutool.core.io.IoUtil;
+import java.io.InputStream;
+
/**
* MAC(Message Authentication Code)算法引擎
*
@@ -20,4 +20,18 @@ public interface MacEngine {
* @return 摘要bytes
*/
byte[] digest(InputStream data, int bufferLength);
+
+ /**
+ * 获取MAC算法块大小
+ *
+ * @return MAC算法块大小
+ */
+ int getMacLength();
+
+ /**
+ * 获取当前算法
+ *
+ * @return 算法
+ */
+ String getAlgorithm();
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngineFactory.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngineFactory.java
index 9288b6e72..49d0516dd 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngineFactory.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/mac/MacEngineFactory.java
@@ -1,10 +1,10 @@
package cn.hutool.crypto.digest.mac;
-import javax.crypto.SecretKey;
-
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
+import java.security.Key;
+
/**
* {@link MacEngine} 实现工厂类
*
@@ -19,7 +19,7 @@ public class MacEngineFactory {
* @param key 密钥
* @return {@link MacEngine}
*/
- public static MacEngine createEngine(String algorithm, SecretKey key) {
+ public static MacEngine createEngine(String algorithm, Key key) {
if(algorithm.equalsIgnoreCase(HmacAlgorithm.HmacSM3.getValue())) {
// HmacSM3算法是BC库实现的
return SmUtil.createHmacSm3Engine(key.getEncoded());
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java
new file mode 100644
index 000000000..fe69e2006
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java
@@ -0,0 +1,124 @@
+package cn.hutool.crypto.digest.opt;
+
+import cn.hutool.crypto.digest.HMac;
+import cn.hutool.crypto.digest.HmacAlgorithm;
+
+/**
+ *
HMAC-based one-time passwords (HOTP) 一次性密码生成器, + * 规范见:RFC 4226.
+ * + *参考:https://github.com/jchambers/java-otp
+ * + * @author Looly + */ +public class HOTP { + + /** + * 数子量级 + */ + private static final int[] MOD_DIVISORS = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; + /** + * 默认密码长度. + */ + public static final int DEFAULT_PASSWORD_LENGTH = 6; + + /** + * 默认HMAC算法. + */ + public static final HmacAlgorithm HOTP_HMAC_ALGORITHM = HmacAlgorithm.HmacSHA1; + + private final HMac mac; + private final int passwordLength; + private final int modDivisor; + + private final byte[] buffer; + + /** + * 构造,使用默认密码长度和默认HMAC算法 + */ + public HOTP(byte[] key) { + this(DEFAULT_PASSWORD_LENGTH, key); + } + + /** + * 构造,使用默认HMAC算法 + * + * @param passwordLength 密码长度,可以是6,7,8 + * @param key 共享密码,RFC 4226要求最少128位 + */ + public HOTP(int passwordLength, byte[] key) { + this(passwordLength, HOTP_HMAC_ALGORITHM, key); + } + + /** + * 构造 + * + * @param passwordLength 密码长度,可以是6,7,8 + * @param algorithm HMAC算法枚举 + * @param key 共享密码,RFC 4226要求最少128位 + */ + public HOTP(int passwordLength, HmacAlgorithm algorithm, byte[] key) { + this.mac = new HMac(algorithm, key); + + this.modDivisor = MOD_DIVISORS[passwordLength]; + this.passwordLength = passwordLength; + this.buffer = new byte[this.mac.getMacLength()]; + } + + /** + * 生成一次性密码 + * + * @param counter 事件计数的值,8 字节的整数,称为移动因子(moving factor), + * 可以是基于计次的动移动因子,也可以是计时移动因子 + * @return 一次性密码的int值 + */ + public synchronized int generateOneTimePassword(final long counter) { + // C 的整数值需要用二进制的字符串表达,比如某个事件计数为 3, + // 则C是 "11"(此处省略了前面的二进制的数字0) + this.buffer[0] = (byte) ((counter & 0xff00000000000000L) >>> 56); + this.buffer[1] = (byte) ((counter & 0x00ff000000000000L) >>> 48); + this.buffer[2] = (byte) ((counter & 0x0000ff0000000000L) >>> 40); + this.buffer[3] = (byte) ((counter & 0x000000ff00000000L) >>> 32); + this.buffer[4] = (byte) ((counter & 0x00000000ff000000L) >>> 24); + this.buffer[5] = (byte) ((counter & 0x0000000000ff0000L) >>> 16); + this.buffer[6] = (byte) ((counter & 0x000000000000ff00L) >>> 8); + this.buffer[7] = (byte) (counter & 0x00000000000000ffL); + + final byte[] digest = this.mac.digest(this.buffer); + + return truncate(digest); + } + + /** + * 截断 + * + * @param digest HMAC的hash值 + * @return 截断值 + */ + private int truncate(byte[] digest) { + final int offset = digest[digest.length - 1] & 0x0f; + return ((digest[offset] & 0x7f) << 24 | + (digest[offset + 1] & 0xff) << 16 | + (digest[offset + 2] & 0xff) << 8 | + (digest[offset + 3] & 0xff)) % + this.modDivisor; + } + + /** + * 获取密码长度,可以是6,7,8 + * + * @return 密码长度,可以是6,7,8 + */ + public int getPasswordLength() { + return this.passwordLength; + } + + /** + * 获取HMAC算法 + * + * @return HMAC算法 + */ + public String getAlgorithm() { + return this.mac.getAlgorithm(); + } +} \ No newline at end of file diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/package-info.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/package-info.java new file mode 100644 index 000000000..e496dd688 --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/package-info.java @@ -0,0 +1,14 @@ +/** + * OTP 是 One-Time Password的简写,表示一次性密码。 + *+ * 计算OTP串的公式: + *
+ * OTP(K,C) = Truncate(HMAC-SHA-1(K,C)) + * K:表示秘钥串 + * C:是一个数字,表示随机数 + * Truncate:是一个函数,就是怎么截取加密后的串,并取加密后串的哪些字段组成一个数字。 + *+ * + * @author looly + */ +package cn.hutool.crypto.digest.opt; \ No newline at end of file