@@ -39,296 +30,4 @@ public abstract class AbstractAsymmetricCrypto
+ * FPE是一种格式保持与明文相同的加密方式,通常用于数据脱敏中,因为它需要保持明密文的格式相同,
+ * 例如社保号经过加密之后并不是固定长度的杂文,而是相同格式、打乱的号码,依然是社保号的格式。
+ *
+ * FPE算法可以保证:
+ *
+ *
+ *
+ *
+ * @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 Hex(16进制)或Base64字符串
+ * @param keyType 私钥或公钥 {@link KeyType}
+ * @return 解密后的bytes
+ * @since 4.5.2
+ */
+ default byte[] decrypt(String data, KeyType keyType) {
+ return decrypt(SecureUtil.decode(data), keyType);
+ }
+
+ /**
+ * 解密为字符串,密文需为Hex(16进制)或Base64字符串
+ *
+ * @param data 数据,Hex(16进制)或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);
+ }
+
+ /**
+ * 解密为字符串,密文需为Hex(16进制)或Base64字符串
+ *
+ * @param data 数据,Hex(16进制)或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);
+ }
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricEncryptor.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricEncryptor.java
new file mode 100644
index 000000000..4e091e8b4
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricEncryptor.java
@@ -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;
+
+/**
+ * 非对称加密器接口,提供:
+ *
+ *
+ *
+ * @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));
+ }
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/BaseAsymmetric.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/BaseAsymmetric.java
index d841a4813..a9ac716cd 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/BaseAsymmetric.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/BaseAsymmetric.java
@@ -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
+ *
+ *
+ * @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);
+ }
+
+ /**
+ * 解密Hex(16进制)或Base64表示的字符串
+ *
+ * @param data 被解密的String,必须为16进制字符串或Base64表示形式
+ * @return 解密后的bytes
+ */
+ default byte[] decrypt(String data) {
+ return decrypt(SecureUtil.decode(data));
+ }
+
+ /**
+ * 解密Hex(16进制)或Base64表示的字符串
+ *
+ * @param data 被解密的String
+ * @param charset 解密后的charset
+ * @return 解密后的String
+ */
+ default String decryptStr(String data, Charset charset) {
+ return StrUtil.str(decrypt(data), charset);
+ }
+
+ /**
+ * 解密Hex(16进制)或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);
+ }
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricEncryptor.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricEncryptor.java
new file mode 100644
index 000000000..3d227718a
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricEncryptor.java
@@ -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;
+
+/**
+ * 对称加密器接口,提供:
+ *
+ *
+ *
+ * @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));
+ }
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/fpe/FPE.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/fpe/FPE.java
new file mode 100644
index 000000000..06c3d21ae
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/fpe/FPE.java
@@ -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模式。
+ * 相关介绍见:https://anquan.baidu.com/article/193
+ *
+ *
+ *
+ *
+ * @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}表示随机密钥,长度必须是16bit、24bit或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}表示随机密钥,长度必须是16bit、24bit或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模式
+ * FPE包括两种模式:FF1和FF3(FF2弃用),核心均为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;
+ }
+ }
+}
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/AESTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/AESTest.java
index 805f6e846..b860854f2 100644
--- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/AESTest.java
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/AESTest.java
@@ -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
*/
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/fpe/FPETest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/fpe/FPETest.java
new file mode 100644
index 000000000..dd41cbf69
--- /dev/null
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/fpe/FPETest.java
@@ -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);
+ }
+}