From 8ddf18fb7a1cdf16fde993aaf48aaa802cc9f20e Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 8 Sep 2021 11:11:27 +0800 Subject: [PATCH] add interface --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/core/io/FileUtil.java | 2 +- .../java/cn/hutool/crypto/SecureUtil.java | 24 +- .../asymmetric/AbstractAsymmetricCrypto.java | 311 +---------------- .../crypto/asymmetric/AsymmetricCrypto.java | 15 +- .../asymmetric/AsymmetricDecryptor.java | 137 ++++++++ .../asymmetric/AsymmetricEncryptor.java | 205 ++++++++++++ .../crypto/asymmetric/BaseAsymmetric.java | 4 +- .../cn/hutool/crypto/asymmetric/ECIES.java | 1 + .../java/cn/hutool/crypto/asymmetric/RSA.java | 1 + .../java/cn/hutool/crypto/asymmetric/SM2.java | 1 + .../cn/hutool/crypto/asymmetric/Sign.java | 1 + .../crypto/symmetric/SymmetricCrypto.java | 312 ++---------------- .../crypto/symmetric/SymmetricDecryptor.java | 126 +++++++ .../crypto/symmetric/SymmetricEncryptor.java | 192 +++++++++++ .../cn/hutool/crypto/symmetric/fpe/FPE.java | 163 +++++++++ .../hutool/crypto/test/symmetric/AESTest.java | 30 -- .../crypto/test/symmetric/fpe/FPETest.java | 48 +++ 18 files changed, 931 insertions(+), 645 deletions(-) create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricDecryptor.java create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricEncryptor.java create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricDecryptor.java create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricEncryptor.java create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/fpe/FPE.java create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/fpe/FPETest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a67f6a052..cea88a96b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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为AlgorithmParameterSpec(issue#1814@Gitee) +* 【crypto 】 增加FPE(issue#1814@Gitee) ### 🐞Bug修复 * 【core 】 修复ListUtil.split方法越界问题(issue#I48Q0P@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index 56d5f4c26..837386b8a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -99,7 +99,7 @@ public class FileUtil extends PathUtil { } /** - * 列出目录文件
+ * 列出指定路径下的目录和文件
* 给定的绝对路径不能是压缩包中的路径 * * @param path 目录绝对路径或者相对路径 diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java b/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java index 79c94616c..c8bf7f778 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/SecureUtil.java @@ -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; @@ -889,7 +891,7 @@ public class SecureUtil { * @since 4.0.1 */ public static String signParams(SymmetricCrypto crypto, Map params, String separator, - String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { + String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { return crypto.encryptHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams)); } @@ -964,7 +966,7 @@ public class SecureUtil { * @since 4.0.1 */ public static String signParams(DigestAlgorithm digestAlgorithm, Map params, String separator, - String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { + String keyValueSeparator, boolean isIgnoreNull, String... otherParams) { return new Digester(digestAlgorithm).digestHex(MapUtil.sortJoin(params, separator, keyValueSeparator, isIgnoreNull, otherParams)); } @@ -1101,11 +1103,25 @@ public class SecureUtil { * PBKDF2加密密码 * * @param password 密码 - * @param salt 盐 + * @param salt 盐 * @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}表示随机密钥,长度必须是16bit、24bit或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); + } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AbstractAsymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AbstractAsymmetricCrypto.java index 82fe4ad9b..b31aa444a 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AbstractAsymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AbstractAsymmetricCrypto.java @@ -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 返回自身类型 * @author Looly */ -public abstract class AbstractAsymmetricCrypto> extends BaseAsymmetric { - // ------------------------------------------------------------------ Constructor start +public abstract class AbstractAsymmetricCrypto> + extends BaseAsymmetric + implements AsymmetricEncryptor, AsymmetricDecryptor{ + private static final long serialVersionUID = 1L; + // ------------------------------------------------------------------ Constructor start /** * 构造 *

@@ -39,296 +30,4 @@ public abstract class AbstractAsymmetricCrypto { + private static final long serialVersionUID = 1L; /** * Cipher负责完成加密或解密工作 @@ -223,13 +224,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // --------------------------------------------------------------------------------- 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 // --------------------------------------------------------------------------------- Decrypt - /** - * 解密 - * - * @param data 被解密的bytes - * @param keyType 私钥或公钥 {@link KeyType} - * @return 解密后的bytes - */ @Override public byte[] decrypt(byte[] data, KeyType keyType) { final Key key = getKeyByType(keyType); diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricDecryptor.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricDecryptor.java new file mode 100644 index 000000000..74083d609 --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricDecryptor.java @@ -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; + +/** + * 非对称解密器接口,提供: + *

    + *
  • 从bytes解密
  • + *
  • 从Hex(16进制)解密
  • + *
  • 从Base64解密
  • + *
  • 从BCD解密
  • + *
+ * + * @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; + +/** + * 非对称加密器接口,提供: + *
    + *
  • 加密为bytes
  • + *
  • 加密为Hex(16进制)
  • + *
  • 加密为Base64
  • + *
  • 加密为BCD
  • + *
+ * + * @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> { +public class BaseAsymmetric> implements Serializable { + private static final long serialVersionUID = 1L; /** * 算法 diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/ECIES.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/ECIES.java index e15987dc9..6c9b64de1 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/ECIES.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/ECIES.java @@ -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"; diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/RSA.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/RSA.java index e03f68605..d875ce06e 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/RSA.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/RSA.java @@ -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; diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java index 3e805478b..b221a1ba8 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java @@ -40,6 +40,7 @@ import java.security.PublicKey; * @since 4.3.2 */ public class SM2 extends AbstractAsymmetricCrypto { + private static final long serialVersionUID = 1L; /** * 算法EC diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Sign.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Sign.java index 8888b3756..c1eb53255 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Sign.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Sign.java @@ -29,6 +29,7 @@ import java.util.Set; * @since 3.3.0 */ public class Sign extends BaseAsymmetric { + private static final long serialVersionUID = 1L; /** 签名,用于签名和验证 */ protected Signature signature; diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java index dce23fe9a..abc31d182 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java @@ -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); - } - - /** - * 解密Hex(16进制)或Base64表示的字符串 - * - * @param data 被解密的String,必须为16进制字符串或Base64表示形式 - * @return 解密后的bytes - */ - public byte[] decrypt(String data) { - return decrypt(SecureUtil.decode(data)); - } - - /** - * 解密Hex(16进制)或Base64表示的字符串 - * - * @param data 被解密的String - * @param charset 解密后的charset - * @return 解密后的String - */ - public String decryptStr(String data, Charset charset) { - return StrUtil.str(decrypt(data), charset); - } - - /** - * 解密Hex(16进制)或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); diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricDecryptor.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricDecryptor.java new file mode 100644 index 000000000..c96d2d892 --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricDecryptor.java @@ -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; + +/** + * 对称解密器接口,提供: + *
    + *
  • 从bytes解密
  • + *
  • 从Hex(16进制)解密
  • + *
  • 从Base64解密
  • + *
+ * + * @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; + +/** + * 对称加密器接口,提供: + *
    + *
  • 加密为bytes
  • + *
  • 加密为Hex(16进制)
  • + *
  • 加密为Base64
  • + *
+ * + * @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 + * + *

+ * FPE是一种格式保持与明文相同的加密方式,通常用于数据脱敏中,因为它需要保持明密文的格式相同, + * 例如社保号经过加密之后并不是固定长度的杂文,而是相同格式、打乱的号码,依然是社保号的格式。 + *

+ *

+ * FPE算法可以保证: + * + *

    + *
  • 数据长度不变。加密前长度是N,加密后长度仍然是N
  • + *
  • 数据类型不变,加密前是数字类型,加密后仍然是数字类型
  • + *
  • 加密过程可逆,加密后的数据可以通过密钥解密还原原始数据
  • + *
+ * + * @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); + } +}