Java 流与块大小对齐的密码更新 ()/doFinal()
Java Cipher update()/doFinal() with Streams aligned to block size
假设我们有 InputStream 读取数据,OutputStream 写入数据和 Cipher。
我们以这种方式加密数据:
int bs = 4096;
Cipher cipher = ...;
InputStream input = ...;
OutputStream output = ...;
int count = 0;
byte[] buffer = new byte[bs];
while (true)
{
count = input.read(buffer, 0, bs);
if (count < bs)
break;
byte[] encrypted = cipher.update(buffer, 0, count);
output.write(encrypted, 0, encrypted.length);
}
if (count > 0)
{
byte[] final = cipher.doFinal(buffer, 0, count);
output.write(final, 0, final.length);
}
但是如果数据恰好是 4096 字节或整数倍呢?这样我们将调用 update 但下一次迭代我们从输入中得到 count = -1 因为什么都没有,所以我们跳过 doFinal() 部分。如何防止跳过 doFinal()?或者我们可以只调用长度为 0 的 doFinal(buffer, 0, 0) 吗?
如果输入缓冲区 returns 的数据少于缓冲区大小,则输入缓冲区不一定到达流的末尾,因此将其用作最终块的整个想法是错误的(它通常适用于文件, 但它可能不适用于其他流,并且 尤其是 CipherInputStream
.
你需要做的就是写数据直到read
方法returns -1
:
void copy(InputStream source, OutputStream target) throws IOException {
byte[] buf = new byte[4096];
int length;
while ((length = source.read(buf)) != -1) {
target.write(buf, 0, length);
}
}
在这里你使用关于 InputStream#read(byte[] b)
的这个事实:
If the length of b
is zero, then no bytes are read and 0
is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at the end of the file, the value -1
is returned; otherwise, at least one byte is read and stored into b
.
此外,您可以简单地使用 CipherOutputStream
来包装给定的 OutputStream
。从 Java 9 开始,您还可以使用 InputStream#transferTo(OutputStream)
让生活变得更简单,无需搞乱缓冲读/写。
最后,强烈推荐使用try-with-resources。不要忘记关闭这些流,尤其是 CipherOutputStream
,否则密文的最后部分可能不会写入输出流。
毫无疑问,在没有数据的情况下调用 doFinal
也可以。 doFinal
没有数据只是转换密码实现的内部缓冲区中剩余的任何字节。
假设我们有 InputStream 读取数据,OutputStream 写入数据和 Cipher。 我们以这种方式加密数据:
int bs = 4096;
Cipher cipher = ...;
InputStream input = ...;
OutputStream output = ...;
int count = 0;
byte[] buffer = new byte[bs];
while (true)
{
count = input.read(buffer, 0, bs);
if (count < bs)
break;
byte[] encrypted = cipher.update(buffer, 0, count);
output.write(encrypted, 0, encrypted.length);
}
if (count > 0)
{
byte[] final = cipher.doFinal(buffer, 0, count);
output.write(final, 0, final.length);
}
但是如果数据恰好是 4096 字节或整数倍呢?这样我们将调用 update 但下一次迭代我们从输入中得到 count = -1 因为什么都没有,所以我们跳过 doFinal() 部分。如何防止跳过 doFinal()?或者我们可以只调用长度为 0 的 doFinal(buffer, 0, 0) 吗?
如果输入缓冲区 returns 的数据少于缓冲区大小,则输入缓冲区不一定到达流的末尾,因此将其用作最终块的整个想法是错误的(它通常适用于文件, 但它可能不适用于其他流,并且 尤其是 CipherInputStream
.
你需要做的就是写数据直到read
方法returns -1
:
void copy(InputStream source, OutputStream target) throws IOException {
byte[] buf = new byte[4096];
int length;
while ((length = source.read(buf)) != -1) {
target.write(buf, 0, length);
}
}
在这里你使用关于 InputStream#read(byte[] b)
的这个事实:
If the length of
b
is zero, then no bytes are read and0
is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at the end of the file, the value-1
is returned; otherwise, at least one byte is read and stored intob
.
此外,您可以简单地使用 CipherOutputStream
来包装给定的 OutputStream
。从 Java 9 开始,您还可以使用 InputStream#transferTo(OutputStream)
让生活变得更简单,无需搞乱缓冲读/写。
最后,强烈推荐使用try-with-resources。不要忘记关闭这些流,尤其是 CipherOutputStream
,否则密文的最后部分可能不会写入输出流。
毫无疑问,在没有数据的情况下调用 doFinal
也可以。 doFinal
没有数据只是转换密码实现的内部缓冲区中剩余的任何字节。