diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b233f852..a8fc619d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.11 (2021-08-26) +# 5.7.11 (2021-08-27) ### 🐣新特性 +* 【crypto 】 修改SymmetricCrypto初始化逻辑 ### 🐞Bug修复 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 454b61ed7..4def26ed2 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 @@ -132,9 +132,7 @@ public class SymmetricCrypto implements Serializable { */ public SymmetricCrypto(String algorithm, SecretKey key, AlgorithmParameterSpec paramsSpec) { init(algorithm, key); - if (null != paramsSpec) { - setParams(paramsSpec); - } + initParams(algorithm, paramsSpec); } // ------------------------------------------------------------------ Constructor end @@ -150,11 +148,6 @@ public class SymmetricCrypto implements Serializable { Assert.notBlank(algorithm, "'algorithm' must be not blank !"); this.secretKey = key; - // 对于PBE算法使用随机数加盐 - if (algorithm.startsWith("PBE")) { - this.params = new PBEParameterSpec(RandomUtil.randomBytes(8), 100); - } - // 检查是否为ZeroPadding,是则替换为NoPadding,并标记以便单独处理 if (algorithm.contains(Padding.ZeroPadding.name())) { algorithm = StrUtil.replace(algorithm, Padding.ZeroPadding.name(), Padding.NoPadding.name()); @@ -253,8 +246,8 @@ public class SymmetricCrypto implements Serializable { /** * 加密,针对大数据量,可选结束后是否关闭流 * - * @param data 被加密的字符串 - * @param out 输出流,可以是文件或网络位置 + * @param data 被加密的字符串 + * @param out 输出流,可以是文件或网络位置 * @param isClose 是否关闭流 * @throws IORuntimeException IO异常 * @since 5.6.3 @@ -266,9 +259,9 @@ public class SymmetricCrypto implements Serializable { final Cipher cipher = initCipher(Cipher.ENCRYPT_MODE); cipherOutputStream = new CipherOutputStream(out, cipher); long length = IoUtil.copy(data, cipherOutputStream); - if(this.isZeroPadding){ + if (this.isZeroPadding) { final int blockSize = cipher.getBlockSize(); - if(blockSize > 0){ + if (blockSize > 0) { // 按照块拆分后的数据中多余的数据 final int remainLength = (int) (length % blockSize); if (remainLength > 0) { @@ -284,7 +277,7 @@ public class SymmetricCrypto implements Serializable { throw new CryptoException(e); } finally { lock.unlock(); - if(isClose){ + if (isClose) { IoUtil.close(data); IoUtil.close(cipherOutputStream); } @@ -471,8 +464,8 @@ public class SymmetricCrypto implements Serializable { /** * 解密,针对大数据量,结束后不关闭流 * - * @param data 加密的字符串 - * @param out 输出流,可以是文件或网络位置 + * @param data 加密的字符串 + * @param out 输出流,可以是文件或网络位置 * @param isClose 是否关闭流,包括输入和输出流 * @throws IORuntimeException IO异常 * @since 5.6.3 @@ -483,9 +476,9 @@ public class SymmetricCrypto implements Serializable { try { final Cipher cipher = initCipher(Cipher.DECRYPT_MODE); cipherInputStream = new CipherInputStream(data, cipher); - if(this.isZeroPadding){ + if (this.isZeroPadding) { final int blockSize = cipher.getBlockSize(); - if(blockSize > 0){ + if (blockSize > 0) { copyForZeroPadding(cipherInputStream, out, blockSize); return; } @@ -499,7 +492,7 @@ public class SymmetricCrypto implements Serializable { throw new CryptoException(e); } finally { lock.unlock(); - if(isClose){ + if (isClose) { IoUtil.close(data); IoUtil.close(cipherInputStream); } @@ -612,12 +605,46 @@ public class SymmetricCrypto implements Serializable { // --------------------------------------------------------------------------------- Private method start + /** + * 初始化加密解密参数,如IV等 + * + * @param algorithm 算法 + * @param paramsSpec 用户定义的{@link AlgorithmParameterSpec} + * @return this + * @since 5.7.11 + */ + private SymmetricCrypto initParams(String algorithm, AlgorithmParameterSpec paramsSpec) { + if (null == paramsSpec) { + byte[] iv = null; + final Cipher cipher = this.cipher; + if (null != cipher) { + iv = cipher.getIV(); + } + + // 随机IV + if (StrUtil.startWithIgnoreCase(algorithm, "PBE")) { + // 对于PBE算法使用随机数加盐 + if (null == iv) { + iv = RandomUtil.randomBytes(8); + } + paramsSpec = new PBEParameterSpec(iv, 100); + } else if (StrUtil.startWithIgnoreCase(algorithm, "AES")) { + if (null != iv) { + //AES使用Cipher默认的随机盐 + paramsSpec = new IvParameterSpec(iv); + } + } + } + + return setParams(paramsSpec); + } + /** * 初始化{@link Cipher}为加密或者解密模式 * * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE} * @return {@link Cipher} - * @throws InvalidKeyException 无效key + * @throws InvalidKeyException 无效key * @throws InvalidAlgorithmParameterException 无效算法 */ private Cipher initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -686,14 +713,15 @@ public class SymmetricCrypto implements Serializable { /** * 拷贝解密后的流 - * @param in {@link CipherInputStream} - * @param out 输出流 + * + * @param in {@link CipherInputStream} + * @param out 输出流 * @param blockSize 块大小 * @throws IOException IO异常 */ private void copyForZeroPadding(CipherInputStream in, OutputStream out, int blockSize) throws IOException { int n = 1; - if(IoUtil.DEFAULT_BUFFER_SIZE > blockSize){ + if (IoUtil.DEFAULT_BUFFER_SIZE > blockSize) { n = Math.max(n, IoUtil.DEFAULT_BUFFER_SIZE / blockSize); } // 此处缓存buffer使用blockSize的整数倍,方便读取时可以正好将补位的0读在一个buffer中 @@ -704,9 +732,9 @@ public class SymmetricCrypto implements Serializable { boolean isFirst = true; int preReadSize = 0; for (int readSize; (readSize = in.read(buffer)) != IoUtil.EOF; ) { - if(isFirst){ + if (isFirst) { isFirst = false; - } else{ + } else { // 将前一批数据写出 out.write(preBuffer, 0, preReadSize); } @@ -718,7 +746,7 @@ public class SymmetricCrypto implements Serializable { while (i >= 0 && 0 == preBuffer[i]) { i--; } - out.write(preBuffer, 0, i+1); + out.write(preBuffer, 0, i + 1); out.flush(); } // --------------------------------------------------------------------------------- Private method 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 625a45105..ba8ef1c6d 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 @@ -16,7 +16,7 @@ import java.security.SecureRandom; public class AESTest { @Test - public void encryptTest() { + public void encryptCBCTest() { // 构建 AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, "1234567890123456".getBytes(), "1234567890123456".getBytes()); @@ -25,7 +25,7 @@ public class AESTest { } @Test - public void encryptTest2() { + public void encryptCTSTest() { String content = "test中文"; AES aes = new AES(Mode.CTS, Padding.PKCS5Padding, "0CoJUm6Qyw8W8jue".getBytes(), "0102030405060708".getBytes()); diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/SymmetricTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/SymmetricTest.java index adfe4fa8a..42646ddf6 100644 --- a/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/SymmetricTest.java +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/symmetric/SymmetricTest.java @@ -14,11 +14,11 @@ import cn.hutool.crypto.symmetric.DESede; import cn.hutool.crypto.symmetric.SymmetricAlgorithm; import cn.hutool.crypto.symmetric.SymmetricCrypto; import cn.hutool.crypto.symmetric.Vigenere; -import java.nio.charset.StandardCharsets; import org.junit.Assert; import org.junit.Test; import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; /** * 对称加密算法单元测试 @@ -109,6 +109,19 @@ public class SymmetricTest { Assert.assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", encryptHex); } + @Test + public void pbeWithoutIvTest() { + String content = "4321c9a2db2e6b08987c3b903d8d11ff"; + SymmetricCrypto crypto = new SymmetricCrypto(SymmetricAlgorithm.PBEWithMD5AndDES, + "0123456789ABHAEQ".getBytes()); + + // 加密为16进制表示 + String encryptHex = crypto.encryptHex(content); + final String data = crypto.decryptStr(encryptHex); + + Assert.assertEquals(content, data); + } + @Test public void aesUpdateTest() { String content = "4321c9a2db2e6b08987c3b903d8d11ff";