add interface

This commit is contained in:
Looly 2021-09-08 11:11:27 +08:00
parent 4c237ba95e
commit 8ddf18fb7a
18 changed files with 931 additions and 645 deletions

View File

@ -3,7 +3,7 @@
-------------------------------------------------------------------------------------------------------------
# 5.7.12 (2021-09-07)
# 5.7.12 (2021-09-08)
### 🐣新特性
* 【system 】 OshiUtil增加getCurrentProcess方法
@ -16,6 +16,7 @@
* 【core 】 ZipReader支持Filter
* 【all 】 Sftp、Ftp、HttpDownloader增加download重载支持避免传输文件损坏pr#407@Gitee
* 【crypto 】 AES修改构造的IvParameterSpec为AlgorithmParameterSpecissue#1814@Gitee
* 【crypto 】 增加FPEissue#1814@Gitee
### 🐞Bug修复
* 【core 】 修复ListUtil.split方法越界问题issue#I48Q0P@Gitee

View File

@ -99,7 +99,7 @@ public class FileUtil extends PathUtil {
}
/**
* 列出目录文件<br>
* 列出指定路径下的目录文件<br>
* 给定的绝对路径不能是压缩包中的路径
*
* @param path 目录绝对路径或者相对路径

View File

@ -21,6 +21,8 @@ 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.fpe.FPE;
import org.bouncycastle.crypto.AlphabetMapper;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@ -1105,7 +1107,21 @@ public class SecureUtil {
* @return 一般为16位
* @since 5.6.0
*/
public static String pbkdf2(char[] password, byte[] salt){
public static String pbkdf2(char[] password, byte[] salt) {
return new PBKDF2().encryptHex(password, salt);
}
/**
* FPE(Format Preserving Encryption)实现支持FF1和FF3-1模式
*
* @param mode FPE模式枚举可选FF1或FF3-1
* @param key 密钥{@code null}表示随机密钥长度必须是16bit24bit或32bit
* @param mapper Alphabet字典映射被加密的字符范围和这个映射必须一致例如手机号银行卡号等字段可以采用数字字母字典表
* @param tweak Tweak是为了解决因局部加密而导致结果冲突问题通常情况下将数据的不可变部分作为Tweak
* @return {@link FPE}
* @since 5.7.12
*/
public static FPE fpe(FPE.FPEMode mode, byte[] key, AlphabetMapper mapper, byte[] tweak) {
return new FPE(mode, key, mapper, tweak);
}
}

View File

@ -1,17 +1,5 @@
package cn.hutool.crypto.asymmetric;
import cn.hutool.core.codec.BCD;
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.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.PrivateKey;
import java.security.PublicKey;
@ -21,9 +9,12 @@ import java.security.PublicKey;
* @param <T> 返回自身类型
* @author Looly
*/
public abstract class AbstractAsymmetricCrypto<T extends AbstractAsymmetricCrypto<T>> extends BaseAsymmetric<T> {
// ------------------------------------------------------------------ Constructor start
public abstract class AbstractAsymmetricCrypto<T extends AbstractAsymmetricCrypto<T>>
extends BaseAsymmetric<T>
implements AsymmetricEncryptor, AsymmetricDecryptor{
private static final long serialVersionUID = 1L;
// ------------------------------------------------------------------ Constructor start
/**
* 构造
* <p>
@ -39,296 +30,4 @@ public abstract class AbstractAsymmetricCrypto<T extends AbstractAsymmetricCrypt
super(algorithm, privateKey, publicKey);
}
// ------------------------------------------------------------------ Constructor end
// --------------------------------------------------------------------------------- Encrypt
/**
* 加密
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
public abstract byte[] encrypt(byte[] data, KeyType keyType);
/**
* 编码为Hex字符串
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
*/
public String encryptHex(byte[] data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
public String encryptBase64(byte[] data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
public byte[] encrypt(String data, String charset, KeyType keyType) {
return encrypt(StrUtil.bytes(data, charset), keyType);
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
public byte[] encrypt(String data, Charset charset, KeyType keyType) {
return encrypt(StrUtil.bytes(data, charset), keyType);
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
public byte[] encrypt(String data, KeyType keyType) {
return encrypt(StrUtil.utf8Bytes(data), keyType);
}
/**
* 编码为Hex字符串
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
public String encryptHex(String data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Hex字符串
*
* @param data 被加密的bytes
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
public String encryptHex(String data, Charset charset, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, charset, keyType));
}
/**
* 编码为Base64字符串使用UTF-8编码
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
public String encryptBase64(String data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
public String encryptBase64(String data, Charset charset, KeyType keyType) {
return Base64.encode(encrypt(data, charset, keyType));
}
/**
* 加密
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
* @throws IORuntimeException IO异常
*/
public byte[] encrypt(InputStream data, KeyType keyType) throws IORuntimeException {
return encrypt(IoUtil.readBytes(data), keyType);
}
/**
* 编码为Hex字符串
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
public String encryptHex(InputStream data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
public String encryptBase64(InputStream data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 分组加密
*
* @param data 数据
* @param keyType 密钥类型
* @return 加密后的密文
* @since 4.1.0
*/
public String encryptBcd(String data, KeyType keyType) {
return encryptBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 分组加密
*
* @param data 数据
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 加密后的密文
* @since 4.1.0
*/
public String encryptBcd(String data, KeyType keyType, Charset charset) {
return BCD.bcdToStr(encrypt(data, charset, keyType));
}
// --------------------------------------------------------------------------------- Decrypt
/**
* 解密
*
* @param bytes 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
*/
public abstract byte[] decrypt(byte[] bytes, KeyType keyType);
/**
* 解密
*
* @param data 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
* @throws IORuntimeException IO异常
*/
public byte[] decrypt(InputStream data, KeyType keyType) throws IORuntimeException {
return decrypt(IoUtil.readBytes(data), keyType);
}
/**
* 从Hex或Base64字符串解密编码为UTF-8格式
*
* @param data Hex16进制或Base64字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
* @since 4.5.2
*/
public byte[] decrypt(String data, KeyType keyType) {
return decrypt(SecureUtil.decode(data), keyType);
}
/**
* 解密为字符串密文需为Hex16进制或Base64字符串
*
* @param data 数据Hex16进制或Base64字符串
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.5.2
*/
public String decryptStr(String data, KeyType keyType, Charset charset) {
return StrUtil.str(decrypt(data, keyType), charset);
}
/**
* 解密为字符串密文需为Hex16进制或Base64字符串
*
* @param data 数据Hex16进制或Base64字符串
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.5.2
*/
public String decryptStr(String data, KeyType keyType) {
return decryptStr(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密BCD
*
* @param data 数据
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.1.0
*/
public byte[] decryptFromBcd(String data, KeyType keyType) {
return decryptFromBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 分组解密
*
* @param data 数据
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.1.0
*/
public byte[] decryptFromBcd(String data, KeyType keyType, Charset charset) {
Assert.notNull(data, "Bcd string must be not null!");
final byte[] dataBytes = BCD.ascToBcd(StrUtil.bytes(data, charset));
return decrypt(dataBytes, keyType);
}
/**
* 解密为字符串密文需为BCD格式
*
* @param data 数据BCD格式
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.5.2
*/
public String decryptStrFromBcd(String data, KeyType keyType, Charset charset) {
return StrUtil.str(decryptFromBcd(data, keyType, charset), charset);
}
/**
* 解密为字符串密文需为BCD格式编码为UTF-8格式
*
* @param data 数据BCD格式
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.5.2
*/
public String decryptStrFromBcd(String data, KeyType keyType) {
return decryptStrFromBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
}

View File

@ -32,6 +32,7 @@ import java.security.spec.AlgorithmParameterSpec;
* @author Looly
*/
public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto> {
private static final long serialVersionUID = 1L;
/**
* Cipher负责完成加密或解密工作
@ -223,13 +224,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
// --------------------------------------------------------------------------------- Encrypt
/**
* 加密
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
@Override
public byte[] encrypt(byte[] data, KeyType keyType) {
final Key key = getKeyByType(keyType);
@ -255,13 +249,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
// --------------------------------------------------------------------------------- Decrypt
/**
* 解密
*
* @param data 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
*/
@Override
public byte[] decrypt(byte[] data, KeyType keyType) {
final Key key = getKeyByType(keyType);

View File

@ -0,0 +1,137 @@
package cn.hutool.crypto.asymmetric;
import cn.hutool.core.codec.BCD;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* 非对称解密器接口提供
* <ul>
* <li>从bytes解密</li>
* <li>从Hex(16进制)解密</li>
* <li>从Base64解密</li>
* <li>从BCD解密</li>
* </ul>
*
* @author looly
* @since 5.7.12
*/
public interface AsymmetricDecryptor {
/**
* 解密
*
* @param bytes 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
*/
byte[] decrypt(byte[] bytes, KeyType keyType);
/**
* 解密
*
* @param data 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
* @throws IORuntimeException IO异常
*/
default byte[] decrypt(InputStream data, KeyType keyType) throws IORuntimeException {
return decrypt(IoUtil.readBytes(data), keyType);
}
/**
* 从Hex或Base64字符串解密编码为UTF-8格式
*
* @param data Hex16进制或Base64字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes
* @since 4.5.2
*/
default byte[] decrypt(String data, KeyType keyType) {
return decrypt(SecureUtil.decode(data), keyType);
}
/**
* 解密为字符串密文需为Hex16进制或Base64字符串
*
* @param data 数据Hex16进制或Base64字符串
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.5.2
*/
default String decryptStr(String data, KeyType keyType, Charset charset) {
return StrUtil.str(decrypt(data, keyType), charset);
}
/**
* 解密为字符串密文需为Hex16进制或Base64字符串
*
* @param data 数据Hex16进制或Base64字符串
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.5.2
*/
default String decryptStr(String data, KeyType keyType) {
return decryptStr(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密BCD
*
* @param data 数据
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.1.0
*/
default byte[] decryptFromBcd(String data, KeyType keyType) {
return decryptFromBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 分组解密
*
* @param data 数据
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.1.0
*/
default byte[] decryptFromBcd(String data, KeyType keyType, Charset charset) {
Assert.notNull(data, "Bcd string must be not null!");
final byte[] dataBytes = BCD.ascToBcd(StrUtil.bytes(data, charset));
return decrypt(dataBytes, keyType);
}
/**
* 解密为字符串密文需为BCD格式
*
* @param data 数据BCD格式
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 解密后的密文
* @since 4.5.2
*/
default String decryptStrFromBcd(String data, KeyType keyType, Charset charset) {
return StrUtil.str(decryptFromBcd(data, keyType, charset), charset);
}
/**
* 解密为字符串密文需为BCD格式编码为UTF-8格式
*
* @param data 数据BCD格式
* @param keyType 密钥类型
* @return 解密后的密文
* @since 4.5.2
*/
default String decryptStrFromBcd(String data, KeyType keyType) {
return decryptStrFromBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
}

View File

@ -0,0 +1,205 @@
package cn.hutool.crypto.asymmetric;
import cn.hutool.core.codec.BCD;
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.StrUtil;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* 非对称加密器接口提供
* <ul>
* <li>加密为bytes</li>
* <li>加密为Hex(16进制)</li>
* <li>加密为Base64</li>
* <li>加密为BCD</li>
* </ul>
*
* @author looly
* @since 5.7.12
*/
public interface AsymmetricEncryptor {
/**
* 加密
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
byte[] encrypt(byte[] data, KeyType keyType);
/**
* 编码为Hex字符串
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
*/
default String encryptHex(byte[] data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
default String encryptBase64(byte[] data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
default byte[] encrypt(String data, String charset, KeyType keyType) {
return encrypt(StrUtil.bytes(data, charset), keyType);
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
default byte[] encrypt(String data, Charset charset, KeyType keyType) {
return encrypt(StrUtil.bytes(data, charset), keyType);
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
*/
default byte[] encrypt(String data, KeyType keyType) {
return encrypt(StrUtil.utf8Bytes(data), keyType);
}
/**
* 编码为Hex字符串
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
default String encryptHex(String data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Hex字符串
*
* @param data 被加密的bytes
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
default String encryptHex(String data, Charset charset, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, charset, keyType));
}
/**
* 编码为Base64字符串使用UTF-8编码
*
* @param data 被加密的字符串
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
default String encryptBase64(String data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的字符串
* @param charset 编码
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
default String encryptBase64(String data, Charset charset, KeyType keyType) {
return Base64.encode(encrypt(data, charset, keyType));
}
/**
* 加密
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes
* @throws IORuntimeException IO异常
*/
default byte[] encrypt(InputStream data, KeyType keyType) throws IORuntimeException {
return encrypt(IoUtil.readBytes(data), keyType);
}
/**
* 编码为Hex字符串
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return Hex字符串
* @since 4.0.1
*/
default String encryptHex(InputStream data, KeyType keyType) {
return HexUtil.encodeHexStr(encrypt(data, keyType));
}
/**
* 编码为Base64字符串
*
* @param data 被加密的数据流
* @param keyType 私钥或公钥 {@link KeyType}
* @return Base64字符串
* @since 4.0.1
*/
default String encryptBase64(InputStream data, KeyType keyType) {
return Base64.encode(encrypt(data, keyType));
}
/**
* 分组加密
*
* @param data 数据
* @param keyType 密钥类型
* @return 加密后的密文
* @since 4.1.0
*/
default String encryptBcd(String data, KeyType keyType) {
return encryptBcd(data, keyType, CharsetUtil.CHARSET_UTF_8);
}
/**
* 分组加密
*
* @param data 数据
* @param keyType 密钥类型
* @param charset 加密前编码
* @return 加密后的密文
* @since 4.1.0
*/
default String encryptBcd(String data, KeyType keyType, Charset charset) {
return BCD.bcdToStr(encrypt(data, charset, keyType));
}
}

View File

@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.KeyUtil;
import java.io.Serializable;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
@ -18,7 +19,8 @@ import java.util.concurrent.locks.ReentrantLock;
* @author Looly
* @since 3.3.0
*/
public class BaseAsymmetric<T extends BaseAsymmetric<T>> {
public class BaseAsymmetric<T extends BaseAsymmetric<T>> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 算法

View File

@ -14,6 +14,7 @@ import java.security.PublicKey;
* @since 5.3.10
*/
public class ECIES extends AsymmetricCrypto{
private static final long serialVersionUID = 1L;
/** 默认的ECIES算法 */
private static final String ALGORITHM_ECIES = "ECIES";

View File

@ -28,6 +28,7 @@ import java.security.spec.RSAPublicKeySpec;
*
*/
public class RSA extends AsymmetricCrypto {
private static final long serialVersionUID = 1L;
/** 默认的RSA算法 */
private static final AsymmetricAlgorithm ALGORITHM_RSA = AsymmetricAlgorithm.RSA_ECB_PKCS1;

View File

@ -40,6 +40,7 @@ import java.security.PublicKey;
* @since 4.3.2
*/
public class SM2 extends AbstractAsymmetricCrypto<SM2> {
private static final long serialVersionUID = 1L;
/**
* 算法EC

View File

@ -29,6 +29,7 @@ import java.util.Set;
* @since 3.3.0
*/
public class Sign extends BaseAsymmetric<Sign> {
private static final long serialVersionUID = 1L;
/** 签名,用于签名和验证 */
protected Signature signature;

View File

@ -1,11 +1,9 @@
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.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
@ -25,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
@ -40,7 +37,7 @@ import java.util.concurrent.locks.ReentrantLock;
*
* @author Looly
*/
public class SymmetricCrypto implements Serializable {
public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, Serializable {
private static final long serialVersionUID = 1L;
/**
@ -159,6 +156,24 @@ public class SymmetricCrypto implements Serializable {
return this;
}
/**
* 获得对称密钥
*
* @return 获得对称密钥
*/
public SecretKey getSecretKey() {
return secretKey;
}
/**
* 获得加密或解密器
*
* @return 加密或解密
*/
public Cipher getCipher() {
return cipher;
}
/**
* 设置 {@link AlgorithmParameterSpec}通常用于加盐或偏移向量
*
@ -246,12 +261,7 @@ public class SymmetricCrypto implements Serializable {
// --------------------------------------------------------------------------------- Encrypt
/**
* 加密
*
* @param data 被加密的bytes
* @return 加密后的bytes
*/
@Override
public byte[] encrypt(byte[] data) {
lock.lock();
try {
@ -264,15 +274,7 @@ public class SymmetricCrypto implements Serializable {
}
}
/**
* 加密针对大数据量可选结束后是否关闭流
*
* @param data 被加密的字符串
* @param out 输出流可以是文件或网络位置
* @param isClose 是否关闭流
* @throws IORuntimeException IO异常
* @since 5.6.3
*/
@Override
public void encrypt(InputStream data, OutputStream out, boolean isClose) throws IORuntimeException {
lock.lock();
CipherOutputStream cipherOutputStream = null;
@ -305,165 +307,9 @@ public class SymmetricCrypto implements Serializable {
}
}
/**
* 加密
*
* @param data 数据
* @return 加密后的Hex
*/
public String encryptHex(byte[] data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密
*
* @param data 数据
* @return 加密后的Base64
* @since 4.0.1
*/
public String encryptBase64(byte[] data) {
return Base64.encode(encrypt(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
public byte[] encrypt(String data, String charset) {
return encrypt(StrUtil.bytes(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
public byte[] encrypt(String data, Charset charset) {
return encrypt(StrUtil.bytes(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
* @since 4.5.12
*/
public String encryptHex(String data, String charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
* @since 4.5.12
*/
public String encryptHex(String data, Charset charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
*/
public String encryptBase64(String data, String charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
* @since 4.5.12
*/
public String encryptBase64(String data, Charset charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的bytes
*/
public byte[] encrypt(String data) {
return encrypt(StrUtil.bytes(data, CharsetUtil.CHARSET_UTF_8));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
public String encryptHex(String data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
public String encryptBase64(String data) {
return Base64.encode(encrypt(data));
}
/**
* 加密加密后关闭流
*
* @param data 被加密的字符串
* @return 加密后的bytes
* @throws IORuntimeException IO异常
*/
public byte[] encrypt(InputStream data) throws IORuntimeException {
return encrypt(IoUtil.readBytes(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
public String encryptHex(InputStream data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
public String encryptBase64(InputStream data) {
return Base64.encode(encrypt(data));
}
// --------------------------------------------------------------------------------- Decrypt
/**
* 解密
*
* @param bytes 被解密的bytes
* @return 解密后的bytes
*/
@Override
public byte[] decrypt(byte[] bytes) {
final int blockSize;
final byte[] decryptData;
@ -482,15 +328,7 @@ public class SymmetricCrypto implements Serializable {
return removePadding(decryptData, blockSize);
}
/**
* 解密针对大数据量结束后不关闭流
*
* @param data 加密的字符串
* @param out 输出流可以是文件或网络位置
* @param isClose 是否关闭流包括输入和输出流
* @throws IORuntimeException IO异常
* @since 5.6.3
*/
@Override
public void decrypt(InputStream data, OutputStream out, boolean isClose) throws IORuntimeException {
lock.lock();
CipherInputStream cipherInputStream = null;
@ -520,110 +358,8 @@ public class SymmetricCrypto implements Serializable {
}
}
/**
* 解密为字符串
*
* @param bytes 被解密的bytes
* @param charset 解密后的charset
* @return 解密后的String
*/
public String decryptStr(byte[] bytes, Charset charset) {
return StrUtil.str(decrypt(bytes), charset);
}
/**
* 解密为字符串默认UTF-8编码
*
* @param bytes 被解密的bytes
* @return 解密后的String
*/
public String decryptStr(byte[] bytes) {
return decryptStr(bytes, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密Hex16进制或Base64表示的字符串
*
* @param data 被解密的String必须为16进制字符串或Base64表示形式
* @return 解密后的bytes
*/
public byte[] decrypt(String data) {
return decrypt(SecureUtil.decode(data));
}
/**
* 解密Hex16进制或Base64表示的字符串
*
* @param data 被解密的String
* @param charset 解密后的charset
* @return 解密后的String
*/
public String decryptStr(String data, Charset charset) {
return StrUtil.str(decrypt(data), charset);
}
/**
* 解密Hex16进制或Base64表示的字符串默认UTF-8编码
*
* @param data 被解密的String
* @return 解密后的String
*/
public String decryptStr(String data) {
return decryptStr(data, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密会关闭流
*
* @param data 被解密的bytes
* @return 解密后的bytes
* @throws IORuntimeException IO异常
*/
public byte[] decrypt(InputStream data) throws IORuntimeException {
return decrypt(IoUtil.readBytes(data));
}
/**
* 解密不会关闭流
*
* @param data 被解密的InputStream
* @param charset 解密后的charset
* @return 解密后的String
*/
public String decryptStr(InputStream data, Charset charset) {
return StrUtil.str(decrypt(data), charset);
}
/**
* 解密
*
* @param data 被解密的InputStream
* @return 解密后的String
*/
public String decryptStr(InputStream data) {
return decryptStr(data, CharsetUtil.CHARSET_UTF_8);
}
// --------------------------------------------------------------------------------- Getters
/**
* 获得对称密钥
*
* @return 获得对称密钥
*/
public SecretKey getSecretKey() {
return secretKey;
}
/**
* 获得加密或解密器
*
* @return 加密或解密
*/
public Cipher getCipher() {
return cipher;
}
// --------------------------------------------------------------------------------- Private method start
/**
@ -740,7 +476,7 @@ public class SymmetricCrypto implements Serializable {
* @param blockSize 块大小
* @throws IOException IO异常
*/
private void copyForZeroPadding(CipherInputStream in, OutputStream out, int blockSize) throws IOException {
private static void copyForZeroPadding(CipherInputStream in, OutputStream out, int blockSize) throws IOException {
int n = 1;
if (IoUtil.DEFAULT_BUFFER_SIZE > blockSize) {
n = Math.max(n, IoUtil.DEFAULT_BUFFER_SIZE / blockSize);

View File

@ -0,0 +1,126 @@
package cn.hutool.crypto.symmetric;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* 对称解密器接口提供
* <ul>
* <li>从bytes解密</li>
* <li>从Hex(16进制)解密</li>
* <li>从Base64解密</li>
* </ul>
*
* @author looly
* @since 5.7.12
*/
public interface SymmetricDecryptor {
/**
* 解密
*
* @param bytes 被解密的bytes
* @return 解密后的bytes
*/
byte[] decrypt(byte[] bytes);
/**
* 解密针对大数据量结束后不关闭流
*
* @param data 加密的字符串
* @param out 输出流可以是文件或网络位置
* @param isClose 是否关闭流包括输入和输出流
* @throws IORuntimeException IO异常
*/
void decrypt(InputStream data, OutputStream out, boolean isClose);
/**
* 解密为字符串
*
* @param bytes 被解密的bytes
* @param charset 解密后的charset
* @return 解密后的String
*/
default String decryptStr(byte[] bytes, Charset charset) {
return StrUtil.str(decrypt(bytes), charset);
}
/**
* 解密为字符串默认UTF-8编码
*
* @param bytes 被解密的bytes
* @return 解密后的String
*/
default String decryptStr(byte[] bytes) {
return decryptStr(bytes, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密Hex16进制或Base64表示的字符串
*
* @param data 被解密的String必须为16进制字符串或Base64表示形式
* @return 解密后的bytes
*/
default byte[] decrypt(String data) {
return decrypt(SecureUtil.decode(data));
}
/**
* 解密Hex16进制或Base64表示的字符串
*
* @param data 被解密的String
* @param charset 解密后的charset
* @return 解密后的String
*/
default String decryptStr(String data, Charset charset) {
return StrUtil.str(decrypt(data), charset);
}
/**
* 解密Hex16进制或Base64表示的字符串默认UTF-8编码
*
* @param data 被解密的String
* @return 解密后的String
*/
default String decryptStr(String data) {
return decryptStr(data, CharsetUtil.CHARSET_UTF_8);
}
/**
* 解密会关闭流
*
* @param data 被解密的bytes
* @return 解密后的bytes
* @throws IORuntimeException IO异常
*/
default byte[] decrypt(InputStream data) throws IORuntimeException {
return decrypt(IoUtil.readBytes(data));
}
/**
* 解密不会关闭流
*
* @param data 被解密的InputStream
* @param charset 解密后的charset
* @return 解密后的String
*/
default String decryptStr(InputStream data, Charset charset) {
return StrUtil.str(decrypt(data), charset);
}
/**
* 解密
*
* @param data 被解密的InputStream
* @return 解密后的String
*/
default String decryptStr(InputStream data) {
return decryptStr(data, CharsetUtil.CHARSET_UTF_8);
}
}

View File

@ -0,0 +1,192 @@
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.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* 对称加密器接口提供
* <ul>
* <li>加密为bytes</li>
* <li>加密为Hex(16进制)</li>
* <li>加密为Base64</li>
* </ul>
*
* @author looly
* @since 5.7.12
*/
public interface SymmetricEncryptor {
/**
* 加密
*
* @param data 被加密的bytes
* @return 加密后的bytes
*/
byte[] encrypt(byte[] data);
/**
* 加密针对大数据量可选结束后是否关闭流
*
* @param data 被加密的字符串
* @param out 输出流可以是文件或网络位置
* @param isClose 是否关闭流
* @throws IORuntimeException IO异常
*/
void encrypt(InputStream data, OutputStream out, boolean isClose);
/**
* 加密
*
* @param data 数据
* @return 加密后的Hex
*/
default String encryptHex(byte[] data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密
*
* @param data 数据
* @return 加密后的Base64
*/
default String encryptBase64(byte[] data) {
return Base64.encode(encrypt(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
default byte[] encrypt(String data, String charset) {
return encrypt(StrUtil.bytes(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的bytes
*/
default byte[] encrypt(String data, Charset charset) {
return encrypt(StrUtil.bytes(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
*/
default String encryptHex(String data, String charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
*/
default String encryptHex(String data, Charset charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
*/
default String encryptBase64(String data, String charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
* @since 4.5.12
*/
default String encryptBase64(String data, Charset charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的bytes
*/
default byte[] encrypt(String data) {
return encrypt(StrUtil.bytes(data, CharsetUtil.CHARSET_UTF_8));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
default String encryptHex(String data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密使用UTF-8编码
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
default String encryptBase64(String data) {
return Base64.encode(encrypt(data));
}
/**
* 加密加密后关闭流
*
* @param data 被加密的字符串
* @return 加密后的bytes
* @throws IORuntimeException IO异常
*/
default byte[] encrypt(InputStream data) throws IORuntimeException {
return encrypt(IoUtil.readBytes(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @return 加密后的Hex
*/
default String encryptHex(InputStream data) {
return HexUtil.encodeHexStr(encrypt(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @return 加密后的Base64
*/
default String encryptBase64(InputStream data) {
return Base64.encode(encrypt(data));
}
}

View File

@ -0,0 +1,163 @@
package cn.hutool.crypto.symmetric.fpe;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import org.bouncycastle.crypto.AlphabetMapper;
import org.bouncycastle.jcajce.spec.FPEParameterSpec;
import java.io.Serializable;
/**
* FPE(Format Preserving Encryption)实现支持FF1和FF3-1模式<br>
* 相关介绍见https://anquan.baidu.com/article/193
*
* <p>
* FPE是一种格式保持与明文相同的加密方式通常用于数据脱敏中因为它需要保持明密文的格式相同
* 例如社保号经过加密之后并不是固定长度的杂文而是相同格式打乱的号码依然是社保号的格式
* </p>
* <p>
* FPE算法可以保证
*
* <ul>
* <li>数据长度不变加密前长度是N加密后长度仍然是N</li>
* <li>数据类型不变加密前是数字类型加密后仍然是数字类型</li>
* <li>加密过程可逆加密后的数据可以通过密钥解密还原原始数据</li>
* </ul>
*
* @author looly
* @since 5.7.12
*/
public class FPE implements Serializable {
private static final long serialVersionUID = 1L;
// 映射字符表规定了明文和密文的字符范围
private final AES aes;
private final AlphabetMapper mapper;
/**
* 构造使用空的Tweak
*
* @param mode FPE模式枚举可选FF1或FF3-1
* @param key 密钥{@code null}表示随机密钥长度必须是16bit24bit或32bit
* @param mapper Alphabet字典映射被加密的字符范围和这个映射必须一致例如手机号银行卡号等字段可以采用数字字母字典表
*/
public FPE(FPEMode mode, byte[] key, AlphabetMapper mapper) {
this(mode, key, mapper, null);
}
/**
* 构造
*
* @param mode FPE模式枚举可选FF1或FF3-1
* @param key 密钥{@code null}表示随机密钥长度必须是16bit24bit或32bit
* @param mapper Alphabet字典映射被加密的字符范围和这个映射必须一致例如手机号银行卡号等字段可以采用数字字母字典表
* @param tweak Tweak是为了解决因局部加密而导致结果冲突问题通常情况下将数据的不可变部分作为Tweak
*/
public FPE(FPEMode mode, byte[] key, AlphabetMapper mapper, byte[] tweak) {
if (null == mode) {
mode = FPEMode.FF1;
}
if(null == tweak){
switch (mode){
case FF1:
tweak = new byte[0];
break;
case FF3_1:
// FF3-1要求必须为56 bits
tweak = new byte[7];
}
}
this.aes = new AES(mode.value, Padding.NoPadding.name(),
KeyUtil.generateKey(mode.value, key),
new FPEParameterSpec(mapper.getRadix(), tweak));
this.mapper = mapper;
}
/**
* 加密
*
* @param data 数据数据必须在构造传入的{@link AlphabetMapper}中定义的范围
* @return 密文结果
*/
public String encrypt(String data) {
if (null == data) {
return null;
}
return new String(encrypt(data.toCharArray()));
}
/**
* 加密
*
* @param data 数据数据必须在构造传入的{@link AlphabetMapper}中定义的范围
* @return 密文结果
*/
public char[] encrypt(char[] data) {
if (null == data) {
return null;
}
// 通过 mapper 将密文输出处理为原始格式
return mapper.convertToChars(aes.encrypt(mapper.convertToIndexes(data)));
}
/**
* 解密
*
* @param data 密文数据数据必须在构造传入的{@link AlphabetMapper}中定义的范围
* @return 明文结果
*/
public String decrypt(String data) {
if (null == data) {
return null;
}
return new String(decrypt(data.toCharArray()));
}
/**
* 加密
*
* @param data 密文数据数据必须在构造传入的{@link AlphabetMapper}中定义的范围
* @return 明文结果
*/
public char[] decrypt(char[] data) {
if (null == data) {
return null;
}
// 通过 mapper 将密文输出处理为原始格式
return mapper.convertToChars(aes.decrypt(mapper.convertToIndexes(data)));
}
/**
* FPE模式<br>
* FPE包括两种模式FF1和FF3FF2弃用核心均为Feistel网络结构
*
* @author looly
*/
public enum FPEMode {
/**
* FF1模式
*/
FF1("FF1"),
/**
* FF3-1 模式
*/
FF3_1("FF3-1");
private final String value;
FPEMode(String name) {
this.value = name;
}
/**
* 获取模式名
*
* @return 模式名
*/
public String getValue() {
return value;
}
}
}

View File

@ -7,14 +7,11 @@ import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import org.bouncycastle.crypto.util.BasicAlphabetMapper;
import org.bouncycastle.jcajce.spec.FPEParameterSpec;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class AESTest {
@ -117,33 +114,6 @@ public class AESTest {
Assert.assertEquals(content, decryptStr);
}
/**
* https://github.com/dromara/hutool/issues/1814
*/
@Test
public void fpeTest() {
// 映射字符表规定了明文和密文的字符范围
BasicAlphabetMapper numberMapper = new BasicAlphabetMapper("0123456789");
// 初始化 aes 密钥
byte[] keyBytes = RandomUtil.randomBytes(16);
AES aes = new AES("FF1", "NoPadding",
new SecretKeySpec(keyBytes, "FF1"),
new FPEParameterSpec(numberMapper.getRadix(), new byte[]{}));
// 原始数据
String phone = "13534534567";
// 加密
byte[] inputDataByte = numberMapper.convertToIndexes(phone.toCharArray());
byte[] encrypt = aes.encrypt(inputDataByte);
// 通过 mapper 将密文输出处理为原始格式
char[] encryptChars = numberMapper.convertToChars(encrypt);
// 手机号码加密: 13534534567 -> 49725950626
Assert.assertEquals(phone.length(), encryptChars.length);
}
/**
* https://blog.csdn.net/weixin_42468911/article/details/114358682
*/

View File

@ -0,0 +1,48 @@
package cn.hutool.crypto.test.symmetric.fpe;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.symmetric.fpe.FPE;
import org.bouncycastle.crypto.util.BasicAlphabetMapper;
import org.junit.Assert;
import org.junit.Test;
public class FPETest {
@Test
public void ff1Test(){
// 映射字符表规定了明文和密文的字符范围
BasicAlphabetMapper numberMapper = new BasicAlphabetMapper("A0123456789");
// 初始化 aes 密钥
byte[] keyBytes = RandomUtil.randomBytes(16);
final FPE fpe = new FPE(FPE.FPEMode.FF1, keyBytes, numberMapper, null);
// 原始数据
String phone = RandomUtil.randomString("A0123456789", 13);
final String encrypt = fpe.encrypt(phone);
// 加密后与原密文长度一致
Assert.assertEquals(phone.length(), encrypt.length());
final String decrypt = fpe.decrypt(encrypt);
Assert.assertEquals(phone, decrypt);
}
@Test
public void ff3Test(){
// 映射字符表规定了明文和密文的字符范围
BasicAlphabetMapper numberMapper = new BasicAlphabetMapper("A0123456789");
// 初始化 aes 密钥
byte[] keyBytes = RandomUtil.randomBytes(16);
final FPE fpe = new FPE(FPE.FPEMode.FF3_1, keyBytes, numberMapper, null);
// 原始数据
String phone = RandomUtil.randomString("A0123456789", 13);
final String encrypt = fpe.encrypt(phone);
// 加密后与原密文长度一致
Assert.assertEquals(phone.length(), encrypt.length());
final String decrypt = fpe.decrypt(encrypt);
Assert.assertEquals(phone, decrypt);
}
}