This commit is contained in:
Looly 2021-09-09 16:14:37 +08:00
parent 5be6c0103b
commit 02885bbcd3
17 changed files with 303 additions and 69 deletions

View File

@ -3,7 +3,7 @@
-------------------------------------------------------------------------------------------------------------
# 5.7.12 (2021-09-08)
# 5.7.12 (2021-09-09)
### 🐣新特性
* 【system 】 OshiUtil增加getCurrentProcess方法
@ -16,12 +16,13 @@
* 【core 】 ZipReader支持Filter
* 【all 】 Sftp、Ftp、HttpDownloader增加download重载支持避免传输文件损坏pr#407@Gitee
* 【crypto 】 AES修改构造的IvParameterSpec为AlgorithmParameterSpecissue#1814@Gitee
* 【crypto 】 增加FPEissue#1814@Gitee
* 【crypto 】 增加FPE、ZUCissue#1814@Gitee
### 🐞Bug修复
* 【core 】 修复ListUtil.split方法越界问题issue#I48Q0P@Gitee
* 【core 】 修复QrCode的isTryHarder、isPureBarcode设置无效问题issue#1815@Github
* 【core 】 修复DatePattern.CHINESE_DATE_FORMATTER错误问题issue#I48ZE3@Gitee
* 【core 】 修复ListUtil.split错误问题
-------------------------------------------------------------------------------------------------------------

View File

@ -40,6 +40,8 @@ public class AvgPartition<T> extends Partition<T> {
@Override
public List<T> get(int index) {
final int size = this.size;
final int remainder = this.remainder;
// 当limit个数超过list的size时size为0此时每个分区分1个元素直到remainder个分配完剩余分区为[]
int start = index * size + Math.min(index, remainder);
int end = start + size;

View File

@ -38,10 +38,11 @@ public class Partition<T> extends AbstractList<List<T>> {
@Override
public int size() {
// 此处采用动态计算以应对list变化
// 此处采用动态计算以应对list变
final int size = this.size;
final int total = list.size();
int length = total / size;
if(total % length > 0){
if(total % size > 0){
length += 1;
}
return length;

View File

@ -18,11 +18,19 @@ public class ListUtilTest {
@Test
public void splitTest(){
List<String> listAll = new ArrayList<>();
listAll.add("1");
listAll.add("2");
List<List<String>> lists = ListUtil.split(listAll, 10);
Assert.assertEquals(1, lists.size());
List<List<Object>> lists = ListUtil.split(null, 3);
Assert.assertEquals(ListUtil.empty(), lists);
lists = ListUtil.split(Arrays.asList(1, 2, 3, 4), 1);
Assert.assertEquals("[[1], [2], [3], [4]]", lists.toString());
lists = ListUtil.split(Arrays.asList(1, 2, 3, 4), 2);
Assert.assertEquals("[[1, 2], [3, 4]]", lists.toString());
lists = ListUtil.split(Arrays.asList(1, 2, 3, 4), 3);
Assert.assertEquals("[[1, 2, 3], [4]]", lists.toString());
lists = ListUtil.split(Arrays.asList(1, 2, 3, 4), 4);
Assert.assertEquals("[[1, 2, 3, 4]]", lists.toString());
lists = ListUtil.split(Arrays.asList(1, 2, 3, 4), 5);
Assert.assertEquals("[[1, 2, 3, 4]]", lists.toString());
}
@Test

View File

@ -4,15 +4,15 @@ import java.security.Provider;
/**
* 全局单例的 org.bouncycastle.jce.provider.BouncyCastleProvider 对象
* @author looly
*
* @author looly
*/
public enum GlobalBouncyCastleProvider {
INSTANCE;
private Provider provider;
private static boolean useBouncyCastle = true;
GlobalBouncyCastleProvider() {
try {
this.provider = ProviderFactory.createBouncyCastleProvider();
@ -20,19 +20,20 @@ public enum GlobalBouncyCastleProvider {
// ignore
}
}
/**
* 获取{@link Provider}
*
* @return {@link Provider}
*/
public Provider getProvider() {
return useBouncyCastle ? this.provider : null;
}
/**
* 设置是否使用Bouncy Castle库<br>
* 如果设置为false表示强制关闭Bouncy Castle而使用JDK
*
*
* @param isUseBouncyCastle 是否使用BouncyCastle库
* @since 4.5.2
*/

View File

@ -3,12 +3,12 @@ package cn.hutool.crypto;
import java.security.Provider;
/**
* Provider对象生产工厂类
*
* Provider对象生产工厂类
*
* <pre>
* 1. 调用{@link #createBouncyCastleProvider()} 用于新建一个org.bouncycastle.jce.provider.BouncyCastleProvider对象
* </pre>
*
*
* @author looly
* @since 4.2.1
*/
@ -17,7 +17,7 @@ public class ProviderFactory {
/**
* 创建Bouncy Castle 提供者<br>
* 如果用户未引入bouncycastle库则此方法抛出{@link NoClassDefFoundError} 异常
*
*
* @return {@link Provider}
*/
public static Provider createBouncyCastleProvider() {

View File

@ -21,6 +21,7 @@ import cn.hutool.crypto.symmetric.DESede;
import cn.hutool.crypto.symmetric.PBKDF2;
import cn.hutool.crypto.symmetric.RC4;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.crypto.symmetric.ZUC;
import cn.hutool.crypto.symmetric.fpe.FPE;
import org.bouncycastle.crypto.AlphabetMapper;
@ -1124,4 +1125,28 @@ public class SecureUtil {
public static FPE fpe(FPE.FPEMode mode, byte[] key, AlphabetMapper mapper, byte[] tweak) {
return new FPE(mode, key, mapper, tweak);
}
/**
* 祖冲之算法集ZUC-128算法实现基于BouncyCastle实现
*
* @param key 密钥
* @param iv 加盐长度16bytes{@code null}是随机加盐
* @return {@link ZUC}
* @since 5.7.12
*/
public static ZUC zuc128(byte[] key, byte[] iv) {
return new ZUC(ZUC.ZUCAlgorithm.ZUC_128, key, iv);
}
/**
* 祖冲之算法集ZUC-256算法实现基于BouncyCastle实现
*
* @param key 密钥
* @param iv 加盐长度25bytes{@code null}是随机加盐
* @return {@link ZUC}
* @since 5.7.12
*/
public static ZUC zuc256(byte[] key, byte[] iv) {
return new ZUC(ZUC.ZUCAlgorithm.ZUC_256, key, iv);
}
}

View File

@ -18,6 +18,7 @@ import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.MessageDigest;
import java.security.spec.AlgorithmParameterSpec;
/**
* HMAC摘要算法<br>
@ -84,7 +85,19 @@ public class HMac implements Serializable {
* @since 4.5.13
*/
public HMac(String algorithm, Key key) {
this(MacEngineFactory.createEngine(algorithm, key));
this(algorithm, key, null);
}
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
* @param spec {@link AlgorithmParameterSpec}
* @since 5.6.12
*/
public HMac(String algorithm, Key key, AlgorithmParameterSpec spec) {
this(MacEngineFactory.createEngine(algorithm, key, spec));
}
/**

View File

@ -5,6 +5,7 @@ import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* BouncyCastle的HMAC算法实现引擎使用{@link Mac} 实现摘要<br>
@ -18,11 +19,24 @@ public class BCHMacEngine implements MacEngine {
private Mac mac;
// ------------------------------------------------------------------------------------------- Constructor start
/**
* 构造
*
* @param digest 摘要算法{@link Digest} 的接口实现
* @param key 密钥
* @param key 密钥
* @param iv 加盐
* @since 5.7.12
*/
public BCHMacEngine(Digest digest, byte[] key, byte[] iv) {
this(digest, new ParametersWithIV(new KeyParameter(key), iv));
}
/**
* 构造
*
* @param digest 摘要算法{@link Digest} 的接口实现
* @param key 密钥
* @since 4.5.13
*/
public BCHMacEngine(Digest digest, byte[] key) {

View File

@ -7,64 +7,100 @@ import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
/**
* 默认的HMAC算法实现引擎使用{@link Mac} 实现摘要<br>
* 当引入BouncyCastle库时自动使用其作为Provider
*
* @author Looly
*@since 4.5.13
* @since 4.5.13
*/
public class DefaultHMacEngine implements MacEngine {
private Mac mac;
// ------------------------------------------------------------------------------------------- Constructor start
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
* @param key 密钥
* @since 4.5.13
*/
public DefaultHMacEngine(String algorithm, byte[] key) {
init(algorithm, key);
this(algorithm, (null == key) ? null : new SecretKeySpec(key, algorithm));
}
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
* @param key 密钥
* @since 4.5.13
*/
public DefaultHMacEngine(String algorithm, Key key) {
init(algorithm, key);
this(algorithm, key, null);
}
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
* @param spec {@link AlgorithmParameterSpec}
* @since 5.7.12
*/
public DefaultHMacEngine(String algorithm, Key key, AlgorithmParameterSpec spec) {
init(algorithm, key, spec);
}
// ------------------------------------------------------------------------------------------- Constructor end
/**
* 初始化
*
* @param algorithm 算法
* @param key 密钥
* @param key 密钥
* @return this
*/
public DefaultHMacEngine init(String algorithm, byte[] key){
public DefaultHMacEngine init(String algorithm, byte[] key) {
return init(algorithm, (null == key) ? null : new SecretKeySpec(key, algorithm));
}
/**
* 初始化
*
* @param algorithm 算法
* @param key 密钥 {@link SecretKey}
* @param key 密钥 {@link SecretKey}
* @return this
* @throws CryptoException Cause by IOException
*/
public DefaultHMacEngine init(String algorithm, Key key){
public DefaultHMacEngine init(String algorithm, Key key) {
return init(algorithm, key, null);
}
/**
* 初始化
*
* @param algorithm 算法
* @param key 密钥 {@link SecretKey}
* @param spec {@link AlgorithmParameterSpec}
* @return this
* @throws CryptoException Cause by IOException
* @since 5.7.12
*/
public DefaultHMacEngine init(String algorithm, Key key, AlgorithmParameterSpec spec) {
try {
mac = SecureUtil.createMac(algorithm);
if(null == key){
if (null == key) {
key = SecureUtil.generateKey(algorithm);
}
mac.init(key);
if (null != spec) {
mac.init(key, spec);
} else {
mac.init(key);
}
} catch (Exception e) {
throw new CryptoException(e);
}

View File

@ -4,26 +4,41 @@ import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
/**
* {@link MacEngine} 实现工厂类
*
*
* @author Looly
*@since 4.5.13
* @since 4.5.13
*/
public class MacEngineFactory {
/**
* 根据给定算法和密钥生成对应的{@link MacEngine}
*
* @param algorithm 算法{@link HmacAlgorithm}
* @param key 密钥
* @param key 密钥
* @return {@link MacEngine}
*/
public static MacEngine createEngine(String algorithm, Key key) {
if(algorithm.equalsIgnoreCase(HmacAlgorithm.HmacSM3.getValue())) {
// HmacSM3算法是BC库实现的
return createEngine(algorithm, key, null);
}
/**
* 根据给定算法和密钥生成对应的{@link MacEngine}
*
* @param algorithm 算法{@link HmacAlgorithm}
* @param key 密钥
* @param spec spec
* @return {@link MacEngine}
* @since 5.7.12
*/
public static MacEngine createEngine(String algorithm, Key key, AlgorithmParameterSpec spec) {
if (algorithm.equalsIgnoreCase(HmacAlgorithm.HmacSM3.getValue())) {
// HmacSM3算法是BC库实现的忽略加盐
return SmUtil.createHmacSm3Engine(key.getEncoded());
}
return new DefaultHMacEngine(algorithm, key);
return new DefaultHMacEngine(algorithm, key, spec);
}
}

View File

@ -0,0 +1,92 @@
package cn.hutool.crypto.symmetric;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.KeyUtil;
import javax.crypto.spec.IvParameterSpec;
/**
* 祖冲之算法集ZUC算法实现基于BouncyCastle实现
*
* @author looly
* @since 5.7.12
*/
public class ZUC extends SymmetricCrypto {
private static final long serialVersionUID = 1L;
/**
* 生成ZUC算法密钥
*
* @param algorithm ZUC算法
* @return 密钥
*
* @see KeyUtil#generateKey(String)
*/
public static byte[] generateKey(ZUCAlgorithm algorithm) {
return KeyUtil.generateKey(algorithm.value).getEncoded();
}
/**
* 构造
*
* @param algorithm ZUC算法枚举包括128位和256位两种
* @param key 密钥
* @param iv 加盐128位加盐是16bytes256位是25bytes{@code null}是随机加盐
*/
public ZUC(ZUCAlgorithm algorithm, byte[] key, byte[] iv) {
super(algorithm.value,
KeyUtil.generateKey(algorithm.value, key),
generateIvParam(algorithm, iv));
}
/**
* ZUC类型包括128位和256位
*
* @author looly
*/
public enum ZUCAlgorithm {
ZUC_128("ZUC-128"),
ZUC_256("ZUC-256");
private final String value;
/**
* 构造
*
* @param value 算法的字符串表示区分大小写
*/
ZUCAlgorithm(String value) {
this.value = value;
}
/**
* 获得算法的字符串表示形式
*
* @return 算法字符串
*/
public String getValue() {
return this.value;
}
}
/**
* 生成加盐参数
*
* @param algorithm ZUC算法
* @param iv 加盐128位加盐是16bytes256位是25bytes{@code null}是随机加盐
* @return {@link IvParameterSpec}
*/
private static IvParameterSpec generateIvParam(ZUCAlgorithm algorithm, byte[] iv){
if(null == iv){
switch (algorithm){
case ZUC_128:
iv = RandomUtil.randomBytes(16);
break;
case ZUC_256:
iv = RandomUtil.randomBytes(25);
break;
}
}
return new IvParameterSpec(iv);
}
}

View File

@ -25,6 +25,8 @@ import java.io.Serializable;
* <li>加密过程可逆加密后的数据可以通过密钥解密还原原始数据</li>
* </ul>
*
* 此类基于BouncyCastle实现
*
* @author looly
* @since 5.7.12
*/
@ -52,15 +54,15 @@ public class FPE implements Serializable {
* @param mode FPE模式枚举可选FF1或FF3-1
* @param key 密钥{@code null}表示随机密钥长度必须是16bit24bit或32bit
* @param mapper Alphabet字典映射被加密的字符范围和这个映射必须一致例如手机号银行卡号等字段可以采用数字字母字典表
* @param tweak Tweak是为了解决因局部加密而导致结果冲突问题通常情况下将数据的不可变部分作为Tweak
* @param tweak Tweak是为了解决因局部加密而导致结果冲突问题通常情况下将数据的不可变部分作为Tweak{@code null}使用默认长度全是0的bytes
*/
public FPE(FPEMode mode, byte[] key, AlphabetMapper mapper, byte[] tweak) {
if (null == mode) {
mode = FPEMode.FF1;
}
if(null == tweak){
switch (mode){
if (null == tweak) {
switch (mode) {
case FF1:
tweak = new byte[0];
break;

View File

@ -1,13 +1,16 @@
package cn.hutool.crypto.test.digest;
import org.junit.Assert;
import org.junit.Test;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.symmetric.ZUC;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.spec.IvParameterSpec;
/**
* Hmac单元测试
@ -15,44 +18,69 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
*
*/
public class HmacTest {
@Test
public void hmacTest(){
String testStr = "test中文";
byte[] key = "password".getBytes();
HMac mac = new HMac(HmacAlgorithm.HmacMD5, key);
String macHex1 = mac.digestHex(testStr);
Assert.assertEquals("b977f4b13f93f549e06140971bded384", macHex1);
String macHex2 = mac.digestHex(IoUtil.toStream(testStr, CharsetUtil.CHARSET_UTF_8));
Assert.assertEquals("b977f4b13f93f549e06140971bded384", macHex2);
}
@Test
public void hmacMd5Test(){
String testStr = "test中文";
HMac mac = SecureUtil.hmacMd5("password");
String macHex1 = mac.digestHex(testStr);
Assert.assertEquals("b977f4b13f93f549e06140971bded384", macHex1);
String macHex2 = mac.digestHex(IoUtil.toStream(testStr, CharsetUtil.CHARSET_UTF_8));
Assert.assertEquals("b977f4b13f93f549e06140971bded384", macHex2);
}
@Test
public void hmacSha1Test(){
String testStr = "test中文";
HMac mac = SecureUtil.hmacSha1("password");
String testStr = "test中文";
String macHex1 = mac.digestHex(testStr);
Assert.assertEquals("1dd68d2f119d5640f0d416e99d3f42408b88d511", macHex1);
String macHex2 = mac.digestHex(IoUtil.toStream(testStr, CharsetUtil.CHARSET_UTF_8));
Assert.assertEquals("1dd68d2f119d5640f0d416e99d3f42408b88d511", macHex2);
}
@Test
public void zuc128MacTest(){
byte[] iv = new byte[16];
final byte[] key = new byte[16];
HMac mac = new HMac("ZUC-128",
KeyUtil.generateKey(ZUC.ZUCAlgorithm.ZUC_128.getValue(), key),
new IvParameterSpec(iv));
String testStr = "test中文";
String macHex1 = mac.digestHex(testStr);
Assert.assertEquals("1e0b9455", macHex1);
}
@Test
public void zuc256MacTest(){
byte[] iv = new byte[25];
final byte[] key = new byte[32];
HMac mac = new HMac("ZUC-256",
KeyUtil.generateKey(ZUC.ZUCAlgorithm.ZUC_128.getValue(), key),
new IvParameterSpec(iv));
String testStr = "test中文";
String macHex1 = mac.digestHex(testStr);
Assert.assertEquals("d9ad618357c1bfb1d9d1200a763d5eaa", macHex1);
}
}

View File

@ -2,21 +2,17 @@ package cn.hutool.crypto.test.symmetric;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.crypto.symmetric.ZUC;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class ZucTest {
@Test
public void zuc128Test(){
final SecretKey secretKey = KeyUtil.generateKey("zuc-128");
final byte[] secretKey = ZUC.generateKey(ZUC.ZUCAlgorithm.ZUC_128);
byte[] iv = RandomUtil.randomBytes(16);
final SymmetricCrypto zuc = new SymmetricCrypto("zuc-128", secretKey, new IvParameterSpec(iv));
final ZUC zuc = new ZUC(ZUC.ZUCAlgorithm.ZUC_128, secretKey, iv);
String msg = RandomUtil.randomString(500);
byte[] crypt2 = zuc.encrypt(msg);
@ -26,9 +22,9 @@ public class ZucTest {
@Test
public void zuc256Test(){
final SecretKey secretKey = KeyUtil.generateKey("zuc-256");
final byte[] secretKey = ZUC.generateKey(ZUC.ZUCAlgorithm.ZUC_256);
byte[] iv = RandomUtil.randomBytes(25);
final SymmetricCrypto zuc = new SymmetricCrypto("zuc-256", secretKey, new IvParameterSpec(iv));
final ZUC zuc = new ZUC(ZUC.ZUCAlgorithm.ZUC_256, secretKey, iv);
String msg = RandomUtil.randomString(500);
byte[] crypt2 = zuc.encrypt(msg);

View File

@ -19,4 +19,4 @@ starttlsEnable = true
# 是否开启SSL
sslEnable = true
# 调试模式
debug = true
debug = true

View File

@ -45,7 +45,7 @@
<dependency>
<groupId>org.ofdrw</groupId>
<artifactId>ofdrw-full</artifactId>
<version>1.15.0</version>
<version>1.15.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>