This commit is contained in:
Looly 2020-04-30 09:11:22 +08:00
parent 484d705638
commit ea96eecd4b
10 changed files with 241 additions and 35 deletions

View File

@ -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.writeObjissue#I1FZIE
### Bug修复
* 【core 】 修复URLBuilder中请求参数有`&`导致的问题issue#850@Github

View File

@ -13,35 +13,36 @@ import cn.hutool.core.util.NumberUtil;
* 2.散列hash映射到数组的bit位置 <br>
* 3.验证<br>
* 此实现方式可以指定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 是否存在
*/

View File

@ -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);
}
/**
* 将多部分内容写到流中
*

View File

@ -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摘要算法<br>
@ -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();
}
}

View File

@ -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} 实现摘要<br>
@ -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();
}
}

View File

@ -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} 实现摘要<br>
* 当引入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();
}
}

View File

@ -1,9 +1,9 @@
package cn.hutool.crypto.digest.mac;
import java.io.InputStream;
import cn.hutool.core.io.IoUtil;
import java.io.InputStream;
/**
* MACMessage 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();
}

View File

@ -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());

View File

@ -0,0 +1,124 @@
package cn.hutool.crypto.digest.opt;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
/**
* <p>HMAC-based one-time passwords (HOTP) 一次性密码生成器
* 规范见<a href="https://tools.ietf.org/html/rfc4226">RFC&nbsp;4226</a>.</p>
*
* <p>参考https://github.com/jchambers/java-otp</p>
*
* @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();
}
}

View File

@ -0,0 +1,14 @@
/**
* OTP One-Time Password的简写表示一次性密码
* <p>
* 计算OTP串的公式
* <pre>
* OTP(K,C) = Truncate(HMAC-SHA-1(K,C))
* K表示秘钥串
* C是一个数字表示随机数
* Truncate是一个函数就是怎么截取加密后的串并取加密后串的哪些字段组成一个数字
* </pre>
*
* @author looly
*/
package cn.hutool.crypto.digest.opt;