add ZeroPadding support

This commit is contained in:
Looly 2019-09-21 22:24:59 +08:00
parent 4292d3fd55
commit c9f77e1746
9 changed files with 362 additions and 161 deletions

View File

@ -16,6 +16,8 @@
* 【cron】 添加获取任务表的方法issue#I12E5H@Gitee
* 【http】 SoapClient增加reset方法用于此对象的复用issue#I12CCC@Gitee
* 【db】 StatementUtil增加setParam方法
* 【db】 Entity.fieldList改为有序实现
* 【crypto】 AES、DES增加对ZeroPadding的支持issue#551@Github
### Bug修复
* 【core】 修复DateUtil.offset导致的时区错误问题issue#I1294O@Gitee

View File

@ -1,15 +1,15 @@
package cn.hutool.core.util;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.*;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Editor;
import cn.hutool.core.lang.Filter;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.*;
/**
* 数组工具类
*
@ -494,15 +494,63 @@ public class ArrayUtil {
* 调整大小后拷贝原数组到新数组下扩大则占位前N个位置缩小则截断
*
* @param <T> 数组元素类型
* @param buffer 原数组
* @param data 原数组
* @param newSize 新的数组大小
* @param componentType 数组元素类型
* @return 调整后的新数组
*/
public static <T> T[] resize(T[] buffer, int newSize, Class<?> componentType) {
T[] newArray = newArray(componentType, newSize);
if (isNotEmpty(buffer)) {
System.arraycopy(buffer, 0, newArray, 0, Math.min(buffer.length, newSize));
public static <T> T[] resize(T[] data, int newSize, Class<?> componentType) {
if(newSize < 0){
return data;
}
final T[] newArray = newArray(componentType, newSize);
if (newSize > 0 && isNotEmpty(data)) {
System.arraycopy(data, 0, newArray, 0, Math.min(data.length, newSize));
}
return newArray;
}
/**
* 生成一个新的重新设置大小的数组<br>
* 调整大小后拷贝原数组到新数组下扩大则占位前N个位置其它位置补充0缩小则截断
*
* @param array 原数组
* @param newSize 新的数组大小
* @return 调整后的新数组
* @since 4.6.7
*/
public static Object resize(Object array, int newSize) {
if(newSize < 0){
return array;
}
if (null == array) {
return null;
}
final int length = length(array);
final Object newArray = Array.newInstance(array.getClass().getComponentType(), newSize);
if (newSize > 0 && isNotEmpty(array)) {
System.arraycopy(array, 0, newArray, 0, Math.min(length, newSize));
}
return newArray;
}
/**
* 生成一个新的重新设置大小的数组<br>
* 调整大小后拷贝原数组到新数组下扩大则占位前N个位置其它位置补充0缩小则截断
*
* @param bytes 原数组
* @param newSize 新的数组大小
* @return 调整后的新数组
* @since 4.6.7
*/
public static byte[] resize(byte[] bytes, int newSize) {
if(newSize < 0){
return bytes;
}
final byte[] newArray = new byte[newSize];
if (newSize > 0 && isNotEmpty(bytes)) {
System.arraycopy(bytes, 0, newArray, 0, Math.min(bytes.length, newSize));
}
return newArray;
}

View File

@ -337,7 +337,7 @@ public class RandomUtil {
* @return 随机元素
*/
public static <T> List<T> randomEles(List<T> list, int count) {
final List<T> result = new ArrayList<T>(count);
final List<T> result = new ArrayList<>(count);
int limit = list.size();
while (result.size() < count) {
result.add(randomEle(list, limit));
@ -361,7 +361,7 @@ public class RandomUtil {
throw new IllegalArgumentException("Count is larger than collection distinct size !");
}
final HashSet<T> result = new HashSet<T>(count);
final HashSet<T> result = new HashSet<>(count);
int limit = source.size();
while (result.size() < count) {
result.add(randomEle(source, limit));
@ -409,14 +409,14 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomString(String baseString, int length) {
final StringBuilder sb = new StringBuilder();
final StringBuilder sb = new StringBuilder(length);
if (length < 1) {
length = 1;
}
int baseLength = baseString.length();
for (int i = 0; i < length; i++) {
int number = getRandom().nextInt(baseLength);
int number = randomInt(baseLength);
sb.append(baseString.charAt(number));
}
return sb.toString();
@ -450,7 +450,7 @@ public class RandomUtil {
* @since 3.1.2
*/
public static char randomChar(String baseString) {
return baseString.charAt(getRandom().nextInt(baseString.length()));
return baseString.charAt(randomInt(baseString.length()));
}
/**

View File

@ -2,25 +2,45 @@ package cn.hutool.crypto;
/**
* 模式
*
* <p>
* 加密算法模式是用来描述加密算法此处特指分组密码不包括流密码在加密时对明文分组的模式它代表了不同的分组方式
*
* @author Looly
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
* @since 3.0.8
*/
public enum Mode{
/** 无模式 */
NONE,
/** 密码分组连接模式Cipher Block Chaining */
CBC,
/** 密文反馈模式Cipher Feedback */
CFB,
/** 计数器模式A simplification of OFB */
public enum Mode {
/**
* 无模式
*/
NONE,
/**
* 密码分组连接模式Cipher Block Chaining
*/
CBC,
/**
* 密文反馈模式Cipher Feedback
*/
CFB,
/**
* 计数器模式A simplification of OFB
*/
CTR,
/** Cipher Text Stealing */
/**
* Cipher Text Stealing
*/
CTS,
/** 电子密码本模式Electronic CodeBook */
ECB,
/** 输出反馈模式Output Feedback */
OFB,
/** Propagating Cipher Block */
/**
* 电子密码本模式Electronic CodeBook
*/
ECB,
/**
* 输出反馈模式Output Feedback
*/
OFB,
/**
* Propagating Cipher Block
*/
PCBC;
}

View File

@ -2,17 +2,26 @@ package cn.hutool.crypto;
/**
* 补码方式
*
*
* <p>
* 补码方式是在分组密码中当明文长度不是分组长度的整数倍时需要在最后一个分组中填充一些数据使其凑满一个分组的长度
*
* @author Looly
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
* @since 3.0.8
*/
public enum Padding {
/** 无补码 */
NoPadding,
/**
* 无补码
*/
NoPadding,
/**
* 0补码既不满block长度时使用0填充
*/
ZeroPadding,
ISO10126Padding,
OAEPPadding,
PKCS1Padding,
PKCS5Padding,
PKCS5Padding,
SSL3Padding
}

View File

@ -3,6 +3,7 @@ package cn.hutool.crypto.symmetric;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
@ -12,13 +13,25 @@ import cn.hutool.crypto.SecureUtil;
* AES加密算法实现<br>
* 高级加密标准英语Advanced Encryption Standard缩写AES在密码学中又称Rijndael加密法<br>
* 对于Java中AES的默认模式是AES/ECB/PKCS5Padding如果使用CryptoJS请调整为padding: CryptoJS.pad.Pkcs7
*
*
* <p>
* 相关概念说明
* <pre>
* mode: 加密算法模式是用来描述加密算法此处特指分组密码不包括流密码在加密时对明文分组的模式它代表了不同的分组方式
* padding: 补码方式是在分组密码中当明文长度不是分组长度的整数倍时需要在最后一个分组中填充一些数据使其凑满一个分组的长度
* iv: 在对明文分组加密时会将明文分组与前一个密文分组进行XOR运算即异或运算但是加密第一个明文分组时不存在前一个密文分组
* 因此需要事先准备一个与分组长度相等的比特序列来代替这个比特序列就是偏移量
* </pre>
* <p>
* 相关概念见https://blog.csdn.net/OrangeJack/article/details/82913804
*
* @author Looly
* @since 3.0.8
*/
public class AES extends SymmetricCrypto {
//------------------------------------------------------------------------- Constrctor start
/**
* 构造默认AES/ECB/PKCS5Padding使用随机密钥
*/
@ -28,7 +41,7 @@ public class AES extends SymmetricCrypto {
/**
* 构造使用默认的AES/ECB/PKCS5Padding
*
*
* @param key 密钥
*/
public AES(byte[] key) {
@ -37,20 +50,20 @@ public class AES extends SymmetricCrypto {
/**
* 构造使用随机密钥
*
* @param mode 模式{@link Mode}
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
*/
public AES(Mode mode, Padding padding) {
this(mode.name(), padding.name());
}
/**
* 构造
*
* @param mode 模式{@link Mode}
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param key 密钥支持三种密钥长度128192256位
*/
public AES(Mode mode, Padding padding, byte[] key) {
this(mode, padding, key, null);
@ -58,36 +71,49 @@ public class AES extends SymmetricCrypto {
/**
* 构造
*
* @param mode 模式{@link Mode}
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param iv 偏移向量加盐
* @param key 密钥支持三种密钥长度128192256位
* @param iv 偏移向量加盐
* @since 3.3.0
*/
public AES(Mode mode, Padding padding, byte[] key, byte[] iv) {
this(mode.name(), padding.name(), key, iv);
}
/**
* 构造
*
* @param mode 模式{@link Mode}
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param key 密钥支持三种密钥长度128192256位
* @since 3.3.0
*/
public AES(Mode mode, Padding padding, SecretKey key) {
this(mode, padding, key, null);
this(mode, padding, key, (IvParameterSpec) null);
}
/**
* 构造
*
* @param mode 模式{@link Mode}
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param iv 偏移向量加盐
* @param key 密钥支持三种密钥长度128192256位
* @param iv 偏移向量加盐
* @since 4.6.7
*/
public AES(Mode mode, Padding padding, SecretKey key, byte[] iv) {
this(mode, padding, key, ArrayUtil.isEmpty(iv) ? ((IvParameterSpec) null) : new IvParameterSpec(iv));
}
/**
* 构造
*
* @param mode 模式{@link Mode}
* @param padding {@link Padding}补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param iv 偏移向量加盐
* @since 3.3.0
*/
public AES(Mode mode, Padding padding, SecretKey key, IvParameterSpec iv) {
@ -96,8 +122,8 @@ public class AES extends SymmetricCrypto {
/**
* 构造
*
* @param mode 模式
*
* @param mode 模式
* @param padding 补码方式
*/
public AES(String mode, String padding) {
@ -106,10 +132,10 @@ public class AES extends SymmetricCrypto {
/**
* 构造
*
* @param mode 模式
*
* @param mode 模式
* @param padding 补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param key 密钥支持三种密钥长度128192256位
*/
public AES(String mode, String padding, byte[] key) {
this(mode, padding, key, null);
@ -117,22 +143,24 @@ public class AES extends SymmetricCrypto {
/**
* 构造
*
* @param mode 模式
*
* @param mode 模式
* @param padding 补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param iv 加盐
* @param key 密钥支持三种密钥长度128192256位
* @param iv 加盐
*/
public AES(String mode, String padding, byte[] key, byte[] iv) {
this(mode, padding, SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), key), null == iv ? null : new IvParameterSpec(iv));
this(mode, padding,//
SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), key),//
ArrayUtil.isEmpty(iv) ? ((IvParameterSpec) null) : new IvParameterSpec(iv));
}
/**
* 构造
*
* @param mode 模式
*
* @param mode 模式
* @param padding 补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param key 密钥支持三种密钥长度128192256位
*/
public AES(String mode, String padding, SecretKey key) {
this(mode, padding, key, null);
@ -140,11 +168,11 @@ public class AES extends SymmetricCrypto {
/**
* 构造
*
* @param mode 模式
*
* @param mode 模式
* @param padding 补码方式
* @param key 密钥支持三种密钥长度128192256位
* @param iv 加盐
* @param key 密钥支持三种密钥长度128192256位
* @param iv 加盐
*/
public AES(String mode, String padding, SecretKey key, IvParameterSpec iv) {
super(StrUtil.format("AES/{}/{}", mode, padding), key, iv);
@ -153,7 +181,7 @@ public class AES extends SymmetricCrypto {
/**
* 设置偏移向量
*
*
* @param iv {@link IvParameterSpec}偏移向量
* @return 自身
*/
@ -161,10 +189,10 @@ public class AES extends SymmetricCrypto {
super.setParams(iv);
return this;
}
/**
* 设置偏移向量
*
*
* @param iv 偏移向量加盐
* @return 自身
* @since 3.3.0

View File

@ -1,49 +1,57 @@
package cn.hutool.crypto.symmetric;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.*;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEParameterSpec;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.spec.AlgorithmParameterSpec;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEParameterSpec;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
/**
* 对称加密算法<br>
* 在对称加密算法中数据发信方将明文原始数据和加密密钥一起经过特殊加密算法处理后使其变成复杂的加密密文发送出去<br>
* 收信方收到密文后若想解读原文则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密才能使其恢复成可读明文<br>
* 在对称加密算法中使用的密钥只有一个发收信双方都使用这个密钥对数据进行加密和解密这就要求解密方事先必须知道加密密钥<br>
*
* @author Looly
*
* @author Looly
*/
public class SymmetricCrypto {
/** SecretKey 负责保存对称密钥 */
/**
* SecretKey 负责保存对称密钥
*/
private SecretKey secretKey;
/** Cipher负责完成加密或解密工作 */
/**
* Cipher负责完成加密或解密工作
*/
private Cipher cipher;
/** 加密解密参数 */
/**
* 加密解密参数
*/
private AlgorithmParameterSpec params;
/**
* 是否0填充
*/
private boolean isZeroPadding;
private Lock lock = new ReentrantLock();
// ------------------------------------------------------------------ Constructor start
/**
* 构造使用随机密钥
*
*
* @param algorithm {@link SymmetricAlgorithm}
*/
public SymmetricCrypto(SymmetricAlgorithm algorithm) {
@ -52,7 +60,7 @@ public class SymmetricCrypto {
/**
* 构造使用随机密钥
*
*
* @param algorithm 算法可以是"algorithm/mode/padding"或者"algorithm"
*/
public SymmetricCrypto(String algorithm) {
@ -61,9 +69,9 @@ public class SymmetricCrypto {
/**
* 构造
*
*
* @param algorithm 算法 {@link SymmetricAlgorithm}
* @param key 自定义KEY
* @param key 自定义KEY
*/
public SymmetricCrypto(SymmetricAlgorithm algorithm, byte[] key) {
this(algorithm.getValue(), key);
@ -71,9 +79,9 @@ public class SymmetricCrypto {
/**
* 构造
*
*
* @param algorithm 算法 {@link SymmetricAlgorithm}
* @param key 自定义KEY
* @param key 自定义KEY
* @since 3.1.2
*/
public SymmetricCrypto(SymmetricAlgorithm algorithm, SecretKey key) {
@ -82,9 +90,9 @@ public class SymmetricCrypto {
/**
* 构造
*
*
* @param algorithm 算法
* @param key 密钥
* @param key 密钥
*/
public SymmetricCrypto(String algorithm, byte[] key) {
this(algorithm, KeyUtil.generateKey(algorithm, key));
@ -92,9 +100,9 @@ public class SymmetricCrypto {
/**
* 构造
*
*
* @param algorithm 算法
* @param key 密钥
* @param key 密钥
* @since 3.1.2
*/
public SymmetricCrypto(String algorithm, SecretKey key) {
@ -103,9 +111,9 @@ public class SymmetricCrypto {
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
*
* @param algorithm 算法
* @param key 密钥
* @param paramsSpec 算法参数例如加盐等
* @since 3.3.0
*/
@ -117,26 +125,36 @@ public class SymmetricCrypto {
}
// ------------------------------------------------------------------ Constructor end
/**
* 初始化
*
*
* @param algorithm 算法
* @param key 密钥如果为<code>null</code>自动生成一个key
* @param key 密钥如果为<code>null</code>自动生成一个key
* @return {@link SymmetricCrypto}
*/
public SymmetricCrypto init(String algorithm, SecretKey key) {
Assert.notBlank(algorithm, "'algorithm' must be not blank !");
this.secretKey = key;
// 对于PBE算法使用随机数加盐
if (algorithm.startsWith("PBE")) {
// 对于PBE算法使用随机数加盐
this.params = new PBEParameterSpec(RandomUtil.randomBytes(8), 100);
}
// 检查是否为ZeroPadding是则替换为NoPadding并标记以便单独处理
if (algorithm.contains(Padding.ZeroPadding.name())) {
algorithm = StrUtil.replace(algorithm, Padding.ZeroPadding.name(), Padding.NoPadding.name());
this.isZeroPadding = true;
}
this.cipher = SecureUtil.createCipher(algorithm);
return this;
}
/**
* 设置 {@link AlgorithmParameterSpec}通常用于加盐或偏移向量
*
*
* @param params {@link AlgorithmParameterSpec}
* @return 自身
*/
@ -146,9 +164,10 @@ public class SymmetricCrypto {
}
// --------------------------------------------------------------------------------- Encrypt
/**
* 加密
*
*
* @param data 被加密的bytes
* @return 加密后的bytes
*/
@ -160,7 +179,7 @@ public class SymmetricCrypto {
} else {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);
}
return cipher.doFinal(data);
return cipher.doFinal(paddingDataWithZero(data, cipher.getBlockSize()));
} catch (Exception e) {
throw new CryptoException(e);
} finally {
@ -170,7 +189,7 @@ public class SymmetricCrypto {
/**
* 加密
*
*
* @param data 数据
* @return 加密后的Hex
*/
@ -180,7 +199,7 @@ public class SymmetricCrypto {
/**
* 加密
*
*
* @param data 数据
* @return 加密后的Base64
* @since 4.0.1
@ -191,19 +210,19 @@ public class SymmetricCrypto {
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
public byte[] encrypt(String data, String charset) {
return encrypt(StrUtil.bytes(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
@ -213,8 +232,8 @@ public class SymmetricCrypto {
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
* @since 4.5.12
@ -222,11 +241,11 @@ public class SymmetricCrypto {
public String encryptHex(String data, String charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
* @since 4.5.12
@ -237,19 +256,19 @@ public class SymmetricCrypto {
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
*/
public String encryptBase64(String data, String charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
* @since 4.5.12
@ -260,7 +279,7 @@ public class SymmetricCrypto {
/**
* 加密使用UTF-8编码
*
*
* @param data 被加密的字符串
* @return 加密后的bytes
*/
@ -270,7 +289,7 @@ public class SymmetricCrypto {
/**
* 加密使用UTF-8编码
*
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
@ -280,7 +299,7 @@ public class SymmetricCrypto {
/**
* 加密使用UTF-8编码
*
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
@ -290,7 +309,7 @@ public class SymmetricCrypto {
/**
* 加密
*
*
* @param data 被加密的字符串
* @return 加密后的bytes
* @throws IORuntimeException IO异常
@ -301,7 +320,7 @@ public class SymmetricCrypto {
/**
* 加密
*
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
@ -311,7 +330,7 @@ public class SymmetricCrypto {
/**
* 加密
*
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
@ -320,13 +339,17 @@ public class SymmetricCrypto {
}
// --------------------------------------------------------------------------------- Decrypt
/**
* 解密
*
*
* @param bytes 被解密的bytes
* @return 解密后的bytes
*/
public byte[] decrypt(byte[] bytes) {
final int blockSize;
final byte[] decryptData;
lock.lock();
try {
if (null == this.params) {
@ -334,18 +357,21 @@ public class SymmetricCrypto {
} else {
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
}
return cipher.doFinal(bytes);
blockSize = cipher.getBlockSize();
decryptData = cipher.doFinal(bytes);
} catch (Exception e) {
throw new CryptoException(e);
} finally {
lock.unlock();
}
return removePadding(decryptData, blockSize);
}
/**
* 解密为字符串
*
* @param bytes 被解密的bytes
*
* @param bytes 被解密的bytes
* @param charset 解密后的charset
* @return 解密后的String
*/
@ -355,7 +381,7 @@ public class SymmetricCrypto {
/**
* 解密为字符串默认UTF-8编码
*
*
* @param bytes 被解密的bytes
* @return 解密后的String
*/
@ -365,7 +391,7 @@ public class SymmetricCrypto {
/**
* 解密Hex16进制或Base64表示的字符串
*
*
* @param data 被解密的String必须为16进制字符串或Base64表示形式
* @return 解密后的bytes
*/
@ -375,8 +401,8 @@ public class SymmetricCrypto {
/**
* 解密Hex16进制或Base64表示的字符串
*
* @param data 被解密的String
*
* @param data 被解密的String
* @param charset 解密后的charset
* @return 解密后的String
*/
@ -386,7 +412,7 @@ public class SymmetricCrypto {
/**
* 解密Hex表示的字符串默认UTF-8编码
*
*
* @param data 被解密的String
* @return 解密后的String
*/
@ -396,7 +422,7 @@ public class SymmetricCrypto {
/**
* 解密不会关闭流
*
*
* @param data 被解密的bytes
* @return 解密后的bytes
* @throws IORuntimeException IO异常
@ -407,8 +433,8 @@ public class SymmetricCrypto {
/**
* 解密不会关闭流
*
* @param data 被解密的InputStream
*
* @param data 被解密的InputStream
* @param charset 解密后的charset
* @return 解密后的String
*/
@ -418,7 +444,7 @@ public class SymmetricCrypto {
/**
* 解密
*
*
* @param data 被解密的InputStream
* @return 解密后的String
*/
@ -427,9 +453,10 @@ public class SymmetricCrypto {
}
// --------------------------------------------------------------------------------- Getters
/**
* 获得对称密钥
*
*
* @return 获得对称密钥
*/
public SecretKey getSecretKey() {
@ -438,10 +465,67 @@ public class SymmetricCrypto {
/**
* 获得加密或解密器
*
*
* @return 加密或解密
*/
public Cipher getClipher() {
public Cipher getCipher() {
return cipher;
}
// --------------------------------------------------------------------------------- Private method start
/**
* 数据按照blockSize的整数倍长度填充填充0
*
* <p>
* {@link Padding#ZeroPadding} 模式下且数据长度不是blockSize的整数倍才有效否则返回原数据
*
* <p>
* https://blog.csdn.net/OrangeJack/article/details/82913804
*
* @param data 数据
* @param blockSize 块大小
* @return 填充后的数据如果isZeroPadding为false或长度刚好返回原数据
* @since 4.6.7
*/
private byte[] paddingDataWithZero(byte[] data, int blockSize) {
if (this.isZeroPadding) {
final int length = data.length;
// 按照块拆分后的数据中多余的数据
final int remainLength = length % blockSize;
if (remainLength > 0) {
// 新长度为blockSize的整数倍多余部分填充0
return ArrayUtil.resize(data, length + blockSize - remainLength);
}
}
return data;
}
/**
* 数据按照blockSize去除填充部分用于解密
*
* <p>
* {@link Padding#ZeroPadding} 模式下且数据长度不是blockSize的整数倍才有效否则返回原数据
*
* @param data 数据
* @param blockSize 块大小
* @return 去除填充后的数据如果isZeroPadding为false或长度刚好返回原数据
* @since 4.6.7
*/
private byte[] removePadding(byte[] data, int blockSize) {
if (this.isZeroPadding) {
final int length = data.length;
final int remainLength = length % blockSize;
if (remainLength == 0) {
// 解码后的数据正好是块大小的整数倍说明可能存在补0的情况去掉末尾所有的0
int i = length - 1;
while (i >= 0 && 0 == data[i]) {
i--;
}
return ArrayUtil.resize(data, i + 1);
}
}
return data;
}
// --------------------------------------------------------------------------------- Private method end
}

View File

@ -1,5 +1,8 @@
package cn.hutool.crypto.test;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import org.junit.Assert;
import org.junit.Test;
@ -106,6 +109,18 @@ public class SymmetricTest {
Assert.assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", encryptHex);
}
@Test
public void aesZeroPaddingTest() {
String content = RandomUtil.randomString(RandomUtil.randomInt(200));
AES aes = new AES(Mode.CBC, Padding.ZeroPadding, "0123456789ABHAEQ".getBytes(), "DYgjCEIMVrj2W9xN".getBytes());
// 加密为16进制表示
String encryptHex = aes.encryptHex(content);
// 解密
String decryptStr = aes.decryptStr(encryptHex);
Assert.assertEquals(content, decryptStr);
}
@Test
public void desTest() {
String content = "test中文";

View File

@ -6,10 +6,7 @@ import java.sql.Clob;
import java.sql.RowId;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Dict;
@ -156,7 +153,7 @@ public class Entity extends Dict {
*/
public Entity setFieldNames(Collection<String> fieldNames) {
if (CollectionUtil.isNotEmpty(fieldNames)) {
this.fieldNames = new HashSet<String>(fieldNames);
this.fieldNames = CollectionUtil.newHashSet(true, fieldNames);
}
return this;
}
@ -169,7 +166,7 @@ public class Entity extends Dict {
*/
public Entity setFieldNames(String... fieldNames) {
if (ArrayUtil.isNotEmpty(fieldNames)) {
this.fieldNames = CollectionUtil.newHashSet(fieldNames);
this.fieldNames = CollectionUtil.newLinkedHashSet(fieldNames);
}
return this;
}
@ -185,9 +182,7 @@ public class Entity extends Dict {
if (null == this.fieldNames) {
return setFieldNames(fieldNames);
} else {
for (String fieldName : fieldNames) {
this.fieldNames.add(fieldName);
}
Collections.addAll(this.fieldNames, fieldNames);
}
}
return this;