diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java index a131ab464..f3167f3a5 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java @@ -49,8 +49,9 @@ public interface Cipher { void init(CipherMode mode, Parameters parameters); /** - * 根据输入长度,获取输出长度,输出长度与算法相关
- * 输出长度只针对本次输入关联,即len长度的数据对应输出长度加doFinal的长度 + * 返回输出缓冲区为了保存下一个update或doFinal操作的结果所需的长度(以字节为单位)
+ * 下一个update或doFinal调用的实际输出长度可能小于此方法返回的长度。
+ * 一般为块大小对应的输出大小 * * @param len 输入长度 * @return 输出长度,-1表示非块加密 @@ -58,7 +59,8 @@ public interface Cipher { int getOutputSize(int len); /** - * 执行运算,可以是加密运算或解密运算 + * 执行运算,可以是加密运算或解密运算
+ * 此方法主要处理一块数据,一块数据处理完毕后,应调用{@link #doFinal(byte[], int)}处理padding等剩余数据。 * * @param in 输入数据 * @param inOff 输入数据开始位置 @@ -82,7 +84,8 @@ public interface Cipher { int doFinal(byte[] out, int outOff); /** - * 处理数据,并返回最终结果 + * 处理数据,并返回最终结果
+ * 此方法用于完整处理一块数据并返回。 * * @param in 输入数据 * @return 结果数据 @@ -92,12 +95,15 @@ public interface Cipher { } /** - * 处理数据,并返回最终结果 + * 处理数据,并返回最终结果
+ * 此方法用于完整处理一块数据并返回。 * - * @param in 输入数据 + * @param in 输入数据 * @param inOffset 输入开始的 input中的偏移量 * @param inputLen 输入长度 * @return 结果数据 + * @see #process(byte[], int, int, byte[], int) + * @see #doFinal(byte[], int) */ default byte[] processFinal(final byte[] in, final int inOffset, final int inputLen) { final byte[] buf = new byte[getOutputSize(in.length)]; diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java index da4326be5..c2514dcc8 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java @@ -66,6 +66,16 @@ public class JceCipher extends SimpleWrapper implements Cip return this.raw.getOutputSize(len); } + /** + * 返回新缓冲区中的初始化向量(IV)
+ * 这在创建随机IV的情况下,或在基于密码的加密或解密的上下文中是有用的,其中IV是从用户提供的密码导出的。 + * + * @return 新缓冲区中的初始化向量,如果基础算法不使用IV,或者尚未设置IV,则为null。 + */ + public byte[] getIV() { + return this.raw.getIV(); + } + @Override public void init(final CipherMode mode, final Parameters parameters) { Assert.isInstanceOf(JceParameters.class, parameters, "Only support JceParameters!"); @@ -120,6 +130,15 @@ public class JceCipher extends SimpleWrapper implements Cip } } + @Override + public byte[] processFinal(final byte[] in) { + try { + return this.raw.doFinal(in); + } catch (final Exception e) { + throw new CryptoException(e); + } + } + @Override public byte[] processFinal(final byte[] data, final int inOffset, final int inputLen) { try { diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java index 34f07103d..dadd839da 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java @@ -294,6 +294,32 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, return HexUtil.encodeStr(update(data)); } + /** + * 完成多部分加密或解密操作,具体取决于此密码的初始化方式。 + * + * @return 带有结果的新缓冲区 + */ + public byte[] doFinal() { + final Cipher cipher = this.cipher.getRaw(); + lock.lock(); + try { + return cipher.doFinal(); + } catch (final Exception e) { + throw new CryptoException(e); + } finally { + lock.unlock(); + } + } + + /** + * 完成多部分加密或解密操作,具体取决于此密码的初始化方式。 + * + * @return 带有结果的新缓冲区 + */ + public String doFinalHex() { + return HexUtil.encodeStr(doFinal()); + } + // --------------------------------------------------------------------------------- Encrypt @Override diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java index 355840ecd..e5613ff73 100644 --- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java +++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java @@ -13,20 +13,17 @@ package org.dromara.hutool.crypto.symmetric; import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.RandomUtil; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.crypto.CipherMode; -import org.dromara.hutool.crypto.KeyUtil; -import org.dromara.hutool.crypto.Mode; -import org.dromara.hutool.crypto.Padding; -import org.dromara.hutool.crypto.SecureUtil; -import org.junit.jupiter.api.Assertions; +import org.dromara.hutool.crypto.*; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * 对称加密算法单元测试 * @@ -49,14 +46,14 @@ public class SymmetricTest { // 解密 final byte[] decrypt = aes.decrypt(encrypt); - Assertions.assertEquals(content, StrUtil.str(decrypt, CharsetUtil.UTF_8)); + assertEquals(content, StrUtil.str(decrypt, CharsetUtil.UTF_8)); // 加密为16进制表示 final String encryptHex = aes.encryptHex(content); // 解密为字符串 final String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.UTF_8); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -74,14 +71,14 @@ public class SymmetricTest { // 解密 final byte[] decrypt = aes.decrypt(encrypt); - Assertions.assertEquals(content, StrUtil.utf8Str(decrypt)); + assertEquals(content, StrUtil.utf8Str(decrypt)); // 加密为16进制表示 final String encryptHex = aes.encryptHex(content); // 解密为字符串 final String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.UTF_8); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -95,14 +92,14 @@ public class SymmetricTest { // 解密 final byte[] decrypt = aes.decrypt(encrypt); - Assertions.assertEquals(content, StrUtil.utf8Str(decrypt)); + assertEquals(content, StrUtil.utf8Str(decrypt)); // 加密为16进制表示 final String encryptHex = aes.encryptHex(content); // 解密为字符串 final String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.UTF_8); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -113,20 +110,20 @@ public class SymmetricTest { // 加密为16进制表示 final String encryptHex = aes.encryptHex(content); - Assertions.assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", encryptHex); + assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", encryptHex); } @Test public void pbeWithoutIvTest() { final String content = "4321c9a2db2e6b08987c3b903d8d11ff"; final SymmetricCrypto crypto = new SymmetricCrypto(SymmetricAlgorithm.PBEWithMD5AndDES, - "0123456789ABHAEQ".getBytes()); + "0123456789ABHAEQ".getBytes()); // 加密为16进制表示 final String encryptHex = crypto.encryptHex(content); final String data = crypto.decryptStr(encryptHex); - Assertions.assertEquals(content, data); + assertEquals(content, data); } @Test @@ -136,11 +133,16 @@ public class SymmetricTest { // 加密为16进制表示 aes.setMode(CipherMode.ENCRYPT); - final String randomData = aes.updateHex(content.getBytes(StandardCharsets.UTF_8)); - aes.setMode(CipherMode.ENCRYPT); - final String randomData2 = aes.updateHex(content.getBytes(StandardCharsets.UTF_8)); - Assertions.assertEquals(randomData2, randomData); - Assertions.assertEquals(randomData, "cd0e3a249eaf0ed80c330338508898c4"); + final String randomData = aes.encryptHex(content.getBytes(StandardCharsets.UTF_8)); + assertEquals("cd0e3a249eaf0ed80c330338508898c4bddcfd665a1b414622164a273ca5daf7b4ebd2c00aaa66b84dd0a237708dac8e", randomData); + + /// 分段加密,update时只是生成部分密文 + String randomData2 = aes.updateHex(content.getBytes(StandardCharsets.UTF_8)); + assertEquals("cd0e3a249eaf0ed80c330338508898c4", randomData2); + + // doFinal后生成剩余内容 + randomData2 += aes.doFinalHex(); + assertEquals(randomData, randomData2); } @@ -153,7 +155,7 @@ public class SymmetricTest { final String encryptHex = aes.encryptHex(content); // 解密 final String decryptStr = aes.decryptStr(encryptHex); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -167,21 +169,21 @@ public class SymmetricTest { final ByteArrayOutputStream contentStream = new ByteArrayOutputStream(); aes.decrypt(IoUtil.toStream(encryptStream), contentStream, true); - Assertions.assertEquals(content, StrUtil.utf8Str(contentStream.toByteArray())); + assertEquals(content, StrUtil.utf8Str(contentStream.toByteArray())); } @Test public void aesPkcs7PaddingTest() { final String content = RandomUtil.randomStringLower(RandomUtil.randomInt(200)); final AES aes = new AES("CBC", "PKCS7Padding", - RandomUtil.randomBytes(32), - "DYgjCEIMVrj2W9xN".getBytes()); + RandomUtil.randomBytes(32), + "DYgjCEIMVrj2W9xN".getBytes()); // 加密为16进制表示 final String encryptHex = aes.encryptHex(content); // 解密 final String decryptStr = aes.decryptStr(encryptHex); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -195,12 +197,12 @@ public class SymmetricTest { final byte[] encrypt = des.encrypt(content); final byte[] decrypt = des.decrypt(encrypt); - Assertions.assertEquals(content, StrUtil.utf8Str(decrypt)); + assertEquals(content, StrUtil.utf8Str(decrypt)); final String encryptHex = des.encryptHex(content); final String decryptStr = des.decryptStr(encryptHex); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -214,12 +216,12 @@ public class SymmetricTest { final byte[] encrypt = des.encrypt(content); final byte[] decrypt = des.decrypt(encrypt); - Assertions.assertEquals(content, StrUtil.utf8Str(decrypt)); + assertEquals(content, StrUtil.utf8Str(decrypt)); final String encryptHex = des.encryptHex(content); final String decryptStr = des.decryptStr(encryptHex); - Assertions.assertEquals(content, decryptStr); + assertEquals(content, decryptStr); } @Test @@ -228,8 +230,8 @@ public class SymmetricTest { final String key = "CompleteVictory"; final String encrypt = Vigenere.encrypt(content, key); - Assertions.assertEquals("zXScRZ]KIOMhQjc0\\bYRXZOJK[Vi", encrypt); + assertEquals("zXScRZ]KIOMhQjc0\\bYRXZOJK[Vi", encrypt); final String decrypt = Vigenere.decrypt(encrypt, key); - Assertions.assertEquals(content, decrypt); + assertEquals(content, decrypt); } }