【问题标题】:Java Cipher update()/doFinal() with Streams aligned to block size流与块大小对齐的 Java Cipher update()/doFinal()
【发布时间】:2021-10-18 21:43:51
【问题描述】:

假设我们有 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) 吗?

【问题讨论】:

  • 我认为更好的解决方案是迭代缓冲区,而不是 while(true)。或者分离成一个函数或方法,它会解决你的问题。

标签: java encryption stream


【解决方案1】:

如果输入缓冲区返回的数据少于缓冲区大小,则它不一定到达流的末尾,因此将其用作最终块的整个想法是错误的(它通常适用于文件,但它可能不起作用与其他信息流,尤其是 CipherInputStream

你需要做的是写入数据直到read方法返回-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)的事实:

如果b的长度为零,则不读取任何字节并返回0;否则,将尝试读取至少一个字节。如果由于流位于文件末尾而没有可用字节,则返回值-1;否则,至少读取一个字节并将其存储到b

此外,您可以简单地使用CipherOutputStream 包装给定的OutputStream。对于 Java 9 及以上版本,您还可以使用 InputStream#transferTo(OutputStream) 让生活更简单,无需处理缓冲读取/写入。

最后,强烈推荐使用 try-with-resources。不要忘记关闭这些流,尤其是CipherOutputStream,否则密文的最后部分可能不会写入您的输出流。


毫无疑问,在没有数据的情况下调用doFinal 也可以。 doFinal 没有数据只是转换密码实现的内部缓冲区中剩余的任何字节。

【讨论】:

    猜你喜欢
    • 2011-05-07
    • 2011-09-26
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 2020-01-24
    相关资源
    最近更新 更多