mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add ZeroPadding support
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
* 【cron】 添加获取任务表的方法(issue#I12E5H@Gitee)
|
* 【cron】 添加获取任务表的方法(issue#I12E5H@Gitee)
|
||||||
* 【http】 SoapClient增加reset方法用于此对象的复用(issue#I12CCC@Gitee)
|
* 【http】 SoapClient增加reset方法用于此对象的复用(issue#I12CCC@Gitee)
|
||||||
* 【db】 StatementUtil增加setParam方法
|
* 【db】 StatementUtil增加setParam方法
|
||||||
|
* 【db】 Entity.fieldList改为有序实现
|
||||||
|
* 【crypto】 AES、DES增加对ZeroPadding的支持(issue#551@Github)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【core】 修复DateUtil.offset导致的时区错误问题(issue#I1294O@Gitee)
|
* 【core】 修复DateUtil.offset导致的时区错误问题(issue#I1294O@Gitee)
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
package cn.hutool.core.util;
|
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.CollectionUtil;
|
||||||
import cn.hutool.core.collection.IterUtil;
|
import cn.hutool.core.collection.IterUtil;
|
||||||
import cn.hutool.core.exceptions.UtilException;
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
import cn.hutool.core.lang.Editor;
|
import cn.hutool.core.lang.Editor;
|
||||||
import cn.hutool.core.lang.Filter;
|
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个位置,缩小则截断
|
* 调整大小后拷贝原数组到新数组下。扩大则占位前N个位置,缩小则截断
|
||||||
*
|
*
|
||||||
* @param <T> 数组元素类型
|
* @param <T> 数组元素类型
|
||||||
* @param buffer 原数组
|
* @param data 原数组
|
||||||
* @param newSize 新的数组大小
|
* @param newSize 新的数组大小
|
||||||
* @param componentType 数组元素类型
|
* @param componentType 数组元素类型
|
||||||
* @return 调整后的新数组
|
* @return 调整后的新数组
|
||||||
*/
|
*/
|
||||||
public static <T> T[] resize(T[] buffer, int newSize, Class<?> componentType) {
|
public static <T> T[] resize(T[] data, int newSize, Class<?> componentType) {
|
||||||
T[] newArray = newArray(componentType, newSize);
|
if(newSize < 0){
|
||||||
if (isNotEmpty(buffer)) {
|
return data;
|
||||||
System.arraycopy(buffer, 0, newArray, 0, Math.min(buffer.length, newSize));
|
}
|
||||||
|
|
||||||
|
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;
|
return newArray;
|
||||||
}
|
}
|
||||||
|
@@ -337,7 +337,7 @@ public class RandomUtil {
|
|||||||
* @return 随机元素
|
* @return 随机元素
|
||||||
*/
|
*/
|
||||||
public static <T> List<T> randomEles(List<T> list, int count) {
|
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();
|
int limit = list.size();
|
||||||
while (result.size() < count) {
|
while (result.size() < count) {
|
||||||
result.add(randomEle(list, limit));
|
result.add(randomEle(list, limit));
|
||||||
@@ -361,7 +361,7 @@ public class RandomUtil {
|
|||||||
throw new IllegalArgumentException("Count is larger than collection distinct size !");
|
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();
|
int limit = source.size();
|
||||||
while (result.size() < count) {
|
while (result.size() < count) {
|
||||||
result.add(randomEle(source, limit));
|
result.add(randomEle(source, limit));
|
||||||
@@ -409,14 +409,14 @@ public class RandomUtil {
|
|||||||
* @return 随机字符串
|
* @return 随机字符串
|
||||||
*/
|
*/
|
||||||
public static String randomString(String baseString, int length) {
|
public static String randomString(String baseString, int length) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder(length);
|
||||||
|
|
||||||
if (length < 1) {
|
if (length < 1) {
|
||||||
length = 1;
|
length = 1;
|
||||||
}
|
}
|
||||||
int baseLength = baseString.length();
|
int baseLength = baseString.length();
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
int number = getRandom().nextInt(baseLength);
|
int number = randomInt(baseLength);
|
||||||
sb.append(baseString.charAt(number));
|
sb.append(baseString.charAt(number));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
@@ -450,7 +450,7 @@ public class RandomUtil {
|
|||||||
* @since 3.1.2
|
* @since 3.1.2
|
||||||
*/
|
*/
|
||||||
public static char randomChar(String baseString) {
|
public static char randomChar(String baseString) {
|
||||||
return baseString.charAt(getRandom().nextInt(baseString.length()));
|
return baseString.charAt(randomInt(baseString.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,25 +2,45 @@ package cn.hutool.crypto;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 模式
|
* 模式
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 加密算法模式,是用来描述加密算法(此处特指分组密码,不包括流密码,)在加密时对明文分组的模式,它代表了不同的分组方式
|
||||||
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
|
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
|
||||||
* @since 3.0.8
|
* @since 3.0.8
|
||||||
*/
|
*/
|
||||||
public enum Mode{
|
public enum Mode {
|
||||||
/** 无模式 */
|
/**
|
||||||
|
* 无模式
|
||||||
|
*/
|
||||||
NONE,
|
NONE,
|
||||||
/** 密码分组连接模式(Cipher Block Chaining) */
|
/**
|
||||||
|
* 密码分组连接模式(Cipher Block Chaining)
|
||||||
|
*/
|
||||||
CBC,
|
CBC,
|
||||||
/** 密文反馈模式(Cipher Feedback) */
|
/**
|
||||||
|
* 密文反馈模式(Cipher Feedback)
|
||||||
|
*/
|
||||||
CFB,
|
CFB,
|
||||||
/** 计数器模式(A simplification of OFB) */
|
/**
|
||||||
|
* 计数器模式(A simplification of OFB)
|
||||||
|
*/
|
||||||
CTR,
|
CTR,
|
||||||
/** Cipher Text Stealing */
|
/**
|
||||||
|
* Cipher Text Stealing
|
||||||
|
*/
|
||||||
CTS,
|
CTS,
|
||||||
/** 电子密码本模式(Electronic CodeBook) */
|
/**
|
||||||
|
* 电子密码本模式(Electronic CodeBook)
|
||||||
|
*/
|
||||||
ECB,
|
ECB,
|
||||||
/** 输出反馈模式(Output Feedback) */
|
/**
|
||||||
|
* 输出反馈模式(Output Feedback)
|
||||||
|
*/
|
||||||
OFB,
|
OFB,
|
||||||
/** Propagating Cipher Block */
|
/**
|
||||||
|
* Propagating Cipher Block
|
||||||
|
*/
|
||||||
PCBC;
|
PCBC;
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,22 @@ package cn.hutool.crypto;
|
|||||||
/**
|
/**
|
||||||
* 补码方式
|
* 补码方式
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* 补码方式是在分组密码中,当明文长度不是分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组的长度。
|
||||||
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
|
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher"> Cipher章节</a>
|
||||||
* @since 3.0.8
|
* @since 3.0.8
|
||||||
*/
|
*/
|
||||||
public enum Padding {
|
public enum Padding {
|
||||||
/** 无补码 */
|
/**
|
||||||
|
* 无补码
|
||||||
|
*/
|
||||||
NoPadding,
|
NoPadding,
|
||||||
|
/**
|
||||||
|
* 0补码,既不满block长度时使用0填充
|
||||||
|
*/
|
||||||
|
ZeroPadding,
|
||||||
ISO10126Padding,
|
ISO10126Padding,
|
||||||
OAEPPadding,
|
OAEPPadding,
|
||||||
PKCS1Padding,
|
PKCS1Padding,
|
||||||
|
@@ -3,6 +3,7 @@ package cn.hutool.crypto.symmetric;
|
|||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.crypto.Mode;
|
import cn.hutool.crypto.Mode;
|
||||||
import cn.hutool.crypto.Padding;
|
import cn.hutool.crypto.Padding;
|
||||||
@@ -13,12 +14,24 @@ import cn.hutool.crypto.SecureUtil;
|
|||||||
* 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法<br>
|
* 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法<br>
|
||||||
* 对于Java中AES的默认模式是:AES/ECB/PKCS5Padding,如果使用CryptoJS,请调整为:padding: CryptoJS.pad.Pkcs7
|
* 对于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
|
* @author Looly
|
||||||
* @since 3.0.8
|
* @since 3.0.8
|
||||||
*/
|
*/
|
||||||
public class AES extends SymmetricCrypto {
|
public class AES extends SymmetricCrypto {
|
||||||
|
|
||||||
//------------------------------------------------------------------------- Constrctor start
|
//------------------------------------------------------------------------- Constrctor start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造,默认AES/ECB/PKCS5Padding,使用随机密钥
|
* 构造,默认AES/ECB/PKCS5Padding,使用随机密钥
|
||||||
*/
|
*/
|
||||||
@@ -78,7 +91,20 @@ public class AES extends SymmetricCrypto {
|
|||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
*/
|
*/
|
||||||
public AES(Mode mode, Padding padding, SecretKey key) {
|
public AES(Mode mode, Padding padding, SecretKey key) {
|
||||||
this(mode, padding, key, null);
|
this(mode, padding, key, (IvParameterSpec) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param mode 模式{@link Mode}
|
||||||
|
* @param padding {@link Padding}补码方式
|
||||||
|
* @param key 密钥,支持三种密钥长度:128、192、256位
|
||||||
|
* @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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,7 +150,9 @@ public class AES extends SymmetricCrypto {
|
|||||||
* @param iv 加盐
|
* @param iv 加盐
|
||||||
*/
|
*/
|
||||||
public AES(String mode, String padding, byte[] key, byte[] 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,26 +1,24 @@
|
|||||||
package cn.hutool.crypto.symmetric;
|
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.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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>
|
||||||
@@ -28,19 +26,29 @@ import cn.hutool.crypto.SecureUtil;
|
|||||||
* 在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。<br>
|
* 在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。<br>
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class SymmetricCrypto {
|
public class SymmetricCrypto {
|
||||||
|
|
||||||
/** SecretKey 负责保存对称密钥 */
|
/**
|
||||||
|
* SecretKey 负责保存对称密钥
|
||||||
|
*/
|
||||||
private SecretKey secretKey;
|
private SecretKey secretKey;
|
||||||
/** Cipher负责完成加密或解密工作 */
|
/**
|
||||||
|
* Cipher负责完成加密或解密工作
|
||||||
|
*/
|
||||||
private Cipher cipher;
|
private Cipher cipher;
|
||||||
/** 加密解密参数 */
|
/**
|
||||||
|
* 加密解密参数
|
||||||
|
*/
|
||||||
private AlgorithmParameterSpec params;
|
private AlgorithmParameterSpec params;
|
||||||
|
/**
|
||||||
|
* 是否0填充
|
||||||
|
*/
|
||||||
|
private boolean isZeroPadding;
|
||||||
private Lock lock = new ReentrantLock();
|
private Lock lock = new ReentrantLock();
|
||||||
|
|
||||||
// ------------------------------------------------------------------ Constructor start
|
// ------------------------------------------------------------------ Constructor start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造,使用随机密钥
|
* 构造,使用随机密钥
|
||||||
*
|
*
|
||||||
@@ -117,6 +125,7 @@ public class SymmetricCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ Constructor end
|
// ------------------------------------------------------------------ Constructor end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
*
|
*
|
||||||
@@ -125,11 +134,20 @@ public class SymmetricCrypto {
|
|||||||
* @return {@link SymmetricCrypto}
|
* @return {@link SymmetricCrypto}
|
||||||
*/
|
*/
|
||||||
public SymmetricCrypto init(String algorithm, SecretKey key) {
|
public SymmetricCrypto init(String algorithm, SecretKey key) {
|
||||||
|
Assert.notBlank(algorithm, "'algorithm' must be not blank !");
|
||||||
this.secretKey = key;
|
this.secretKey = key;
|
||||||
if (algorithm.startsWith("PBE")) {
|
|
||||||
// 对于PBE算法使用随机数加盐
|
// 对于PBE算法使用随机数加盐
|
||||||
|
if (algorithm.startsWith("PBE")) {
|
||||||
this.params = new PBEParameterSpec(RandomUtil.randomBytes(8), 100);
|
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);
|
this.cipher = SecureUtil.createCipher(algorithm);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -146,6 +164,7 @@ public class SymmetricCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------- Encrypt
|
// --------------------------------------------------------------------------------- Encrypt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密
|
* 加密
|
||||||
*
|
*
|
||||||
@@ -160,7 +179,7 @@ public class SymmetricCrypto {
|
|||||||
} else {
|
} else {
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);
|
||||||
}
|
}
|
||||||
return cipher.doFinal(data);
|
return cipher.doFinal(paddingDataWithZero(data, cipher.getBlockSize()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CryptoException(e);
|
throw new CryptoException(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -320,6 +339,7 @@ public class SymmetricCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------- Decrypt
|
// --------------------------------------------------------------------------------- Decrypt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解密
|
* 解密
|
||||||
*
|
*
|
||||||
@@ -327,6 +347,9 @@ public class SymmetricCrypto {
|
|||||||
* @return 解密后的bytes
|
* @return 解密后的bytes
|
||||||
*/
|
*/
|
||||||
public byte[] decrypt(byte[] bytes) {
|
public byte[] decrypt(byte[] bytes) {
|
||||||
|
final int blockSize;
|
||||||
|
final byte[] decryptData;
|
||||||
|
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
if (null == this.params) {
|
if (null == this.params) {
|
||||||
@@ -334,12 +357,15 @@ public class SymmetricCrypto {
|
|||||||
} else {
|
} else {
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
|
||||||
}
|
}
|
||||||
return cipher.doFinal(bytes);
|
blockSize = cipher.getBlockSize();
|
||||||
|
decryptData = cipher.doFinal(bytes);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CryptoException(e);
|
throw new CryptoException(e);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return removePadding(decryptData, blockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -427,6 +453,7 @@ public class SymmetricCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------- Getters
|
// --------------------------------------------------------------------------------- Getters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得对称密钥
|
* 获得对称密钥
|
||||||
*
|
*
|
||||||
@@ -441,7 +468,64 @@ public class SymmetricCrypto {
|
|||||||
*
|
*
|
||||||
* @return 加密或解密
|
* @return 加密或解密
|
||||||
*/
|
*/
|
||||||
public Cipher getClipher() {
|
public Cipher getCipher() {
|
||||||
return cipher;
|
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
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package cn.hutool.crypto.test;
|
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.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -106,6 +109,18 @@ public class SymmetricTest {
|
|||||||
Assert.assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", encryptHex);
|
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
|
@Test
|
||||||
public void desTest() {
|
public void desTest() {
|
||||||
String content = "test中文";
|
String content = "test中文";
|
||||||
|
@@ -6,10 +6,7 @@ import java.sql.Clob;
|
|||||||
import java.sql.RowId;
|
import java.sql.RowId;
|
||||||
import java.sql.Time;
|
import java.sql.Time;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.lang.Dict;
|
import cn.hutool.core.lang.Dict;
|
||||||
@@ -156,7 +153,7 @@ public class Entity extends Dict {
|
|||||||
*/
|
*/
|
||||||
public Entity setFieldNames(Collection<String> fieldNames) {
|
public Entity setFieldNames(Collection<String> fieldNames) {
|
||||||
if (CollectionUtil.isNotEmpty(fieldNames)) {
|
if (CollectionUtil.isNotEmpty(fieldNames)) {
|
||||||
this.fieldNames = new HashSet<String>(fieldNames);
|
this.fieldNames = CollectionUtil.newHashSet(true, fieldNames);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -169,7 +166,7 @@ public class Entity extends Dict {
|
|||||||
*/
|
*/
|
||||||
public Entity setFieldNames(String... fieldNames) {
|
public Entity setFieldNames(String... fieldNames) {
|
||||||
if (ArrayUtil.isNotEmpty(fieldNames)) {
|
if (ArrayUtil.isNotEmpty(fieldNames)) {
|
||||||
this.fieldNames = CollectionUtil.newHashSet(fieldNames);
|
this.fieldNames = CollectionUtil.newLinkedHashSet(fieldNames);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -185,9 +182,7 @@ public class Entity extends Dict {
|
|||||||
if (null == this.fieldNames) {
|
if (null == this.fieldNames) {
|
||||||
return setFieldNames(fieldNames);
|
return setFieldNames(fieldNames);
|
||||||
} else {
|
} else {
|
||||||
for (String fieldName : fieldNames) {
|
Collections.addAll(this.fieldNames, fieldNames);
|
||||||
this.fieldNames.add(fieldName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
Reference in New Issue
Block a user