From 4c237ba95e9b9aedec334e019a4d64fa54b29617 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 7 Sep 2021 23:54:57 +0800 Subject: [PATCH] add test and change AES costructor --- CHANGELOG.md | 1 + .../java/cn/hutool/core/util/RandomUtil.java | 15 ++++++ .../main/java/cn/hutool/core/util/ReUtil.java | 2 +- .../java/cn/hutool/crypto/symmetric/AES.java | 25 ++++----- .../hutool/crypto/test/symmetric/AESTest.java | 51 +++++++++++++++++++ .../crypto/test/symmetric/ChaCha20Test.java | 38 ++++++++++++++ 6 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/ChaCha20Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 972213e19..a67f6a052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【crypto 】 SymmetricCrypto增加setMode方法,update采用累加模式(pr#1642@Github) * 【core 】 ZipReader支持Filter * 【all 】 Sftp、Ftp、HttpDownloader增加download重载,支持避免传输文件损坏(pr#407@Gitee) +* 【crypto 】 AES修改构造的IvParameterSpec为AlgorithmParameterSpec(issue#1814@Gitee) ### 🐞Bug修复 * 【core 】 修复ListUtil.split方法越界问题(issue#I48Q0P@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java index b9a91db0e..aae07f8be 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java @@ -125,6 +125,21 @@ public class RandomUtil { return random; } + /** + * 获取algorithms/providers中提供的强安全随机生成器
+ * 注意:此方法可能造成阻塞或性能问题 + * + * @return {@link SecureRandom} + * @since 5.7.12 + */ + public static SecureRandom getSecureRandomStrong() { + try { + return SecureRandom.getInstanceStrong(); + } catch (NoSuchAlgorithmException e) { + throw new UtilException(e); + } + } + /** * 获取随机数产生器 * diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java index 20ff64b82..fcad035a5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java @@ -96,7 +96,7 @@ public class ReUtil { } /** - * 获得匹配的字符串,,获得正则中分组1的内容 + * 获得匹配的字符串,获得正则中分组1的内容 * * @param pattern 编译后的正则模式 * @param content 被匹配的内容 diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/AES.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/AES.java index 566c28d6f..dc03596a5 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/AES.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/AES.java @@ -8,6 +8,7 @@ import cn.hutool.crypto.Padding; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; +import java.security.spec.AlgorithmParameterSpec; /** * AES加密算法实现
@@ -121,14 +122,14 @@ public class AES extends SymmetricCrypto { /** * 构造 * - * @param mode 模式{@link Mode} - * @param padding {@link Padding}补码方式 - * @param key 密钥,支持三种密钥长度:128、192、256位 - * @param iv 偏移向量,加盐 + * @param mode 模式{@link Mode} + * @param padding {@link Padding}补码方式 + * @param key 密钥,支持三种密钥长度:128、192、256位 + * @param paramsSpec 算法参数,例如加盐等 * @since 3.3.0 */ - public AES(Mode mode, Padding padding, SecretKey key, IvParameterSpec iv) { - this(mode.name(), padding.name(), key, iv); + public AES(Mode mode, Padding padding, SecretKey key, AlgorithmParameterSpec paramsSpec) { + this(mode.name(), padding.name(), key, paramsSpec); } /** @@ -180,13 +181,13 @@ public class AES extends SymmetricCrypto { /** * 构造 * - * @param mode 模式 - * @param padding 补码方式 - * @param key 密钥,支持三种密钥长度:128、192、256位 - * @param iv 加盐 + * @param mode 模式 + * @param padding 补码方式 + * @param key 密钥,支持三种密钥长度:128、192、256位 + * @param paramsSpec 算法参数,例如加盐等 */ - public AES(String mode, String padding, SecretKey key, IvParameterSpec iv) { - super(StrUtil.format("AES/{}/{}", mode, padding), key, iv); + public AES(String mode, String padding, SecretKey key, AlgorithmParameterSpec paramsSpec) { + super(StrUtil.format("AES/{}/{}", mode, padding), key, paramsSpec); } //------------------------------------------------------------------------- Constrctor end } 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 ba8ef1c6d..805f6e846 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,10 +7,14 @@ 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 { @@ -112,4 +116,51 @@ public class AESTest { final String decryptStr = aes.decryptStr(result1); 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 + */ + @Test + public void gcmTest() { + final SecretKey key = KeyUtil.generateKey("AES"); + byte[] iv = RandomUtil.randomBytes(12); + + AES aes = new AES("GCM", "NoPadding", + key, + new GCMParameterSpec(128, iv)); + + // 原始数据 + String phone = "13534534567"; + // 加密 + byte[] encrypt = aes.encrypt(phone); + final String decryptStr = aes.decryptStr(encrypt); + Assert.assertEquals(phone, decryptStr); + } } diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/ChaCha20Test.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/ChaCha20Test.java new file mode 100644 index 000000000..027cc241f --- /dev/null +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/ChaCha20Test.java @@ -0,0 +1,38 @@ +package cn.hutool.crypto.test.symmetric; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.KeyUtil; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import org.junit.Assert; +import org.junit.Test; + +import javax.crypto.spec.IvParameterSpec; + +/** + * 见:https://stackoverflow.com/questions/32672241/using-bouncycastles-chacha-for-file-encryption + */ +public class ChaCha20Test { + + @Test + public void encryptAndDecryptTest() { + String content = "test中文"; + + // 32 for 256 bit key or 16 for 128 bit + byte[] key = RandomUtil.randomBytes(32); + // 64 bit IV required by ChaCha20 + byte[] iv = RandomUtil.randomBytes(12); + + final SymmetricCrypto chacha = new SymmetricCrypto("ChaCha20", + KeyUtil.generateKey("ChaCha", key), + new IvParameterSpec(iv) + ); + + // 加密为16进制表示 + String encryptHex = chacha.encryptHex(content); + // 解密为字符串 + String decryptStr = chacha.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8); + + Assert.assertEquals(content, decryptStr); + } +}