我需要通过TCP(从100字节到每秒100兆字节)在Java到C++程序的块中加密和发送数据,并且需要提前发送数据的大小,这样接收方知道什么时候停止读取当前消息并处理它,然后等待下一条消息(连接保持打开,因此没有其他方法指示消息结束;由于数据可以是二进制的,因此我无法使用标志来指示消息结束,因为加密字节可能会随机地与我在某个点上选择的任何标志相同)。
我的问题是在加密之前计算加密消息的大小,这通常会由于填充等原因而不同于输入长度。
假设我已经初始化如下:

AlgorithmParameterSpec paramSpec = new IvParameterSpec(initv);
encipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mac = Mac.getInstance("HmacSHA512");
encipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
mac.init(key);
buf = new byte[encipher.getOutputSize(blockSize)];

然后我按原样发送数据(还有一个类似的函数,它使用流进行输入,而不是byte[]):
public void writeBytes(DataOutputStream out, byte[] input) {
 try {
  //mac.reset(); // Needed ?
  int left = input.length;
  int offset = 0;
  while (left > 0)
  {
   int chunk = Math.min(left, blockSize);
   int ctLength = encipher.update(input, offset, chunk, buf, 0);
   mac.update(input, offset, chunk);
   out.write(buf, 0, ctLength);
   left -= chunk;
   offset += chunk;
  }
  out.write(encipher.doFinal(mac.doFinal());
  out.flush();
 } catch (Exception e) {
  e.printStackTrace();
 }
}

但是如何预先计算将发送到接收计算机的输出大小呢?
基本上,我想在循环之前输出.writeInt(MessageSize)。但是如何计算消息大小呢?cipher的getOutputSize()文档说,“这个调用考虑到了以前更新调用中的任何未处理(缓冲)数据和填充。”所以这似乎意味着同一个函数参数的值可能会在多次调用update()doFinal()时发生变化。我可以假设如果blockSize是AES CBC块大小的倍数以避免填充,那么我应该为每个块设置一个常量值吗?也就是说,只需检查_blockSize % encipher.getOutputSize(1) != 0,然后在write函数中,
int messageSize = (input.length / blockSize) * encipher.getOutputSize(blockSize) +
                  encipher.getOutputSize(input.length % blockSize + mac.getMacLength());

是吗?是吗?
如果没有,我还有什么选择?

最佳答案

使用pkcs5填充时,填充后的消息大小将为:

padded_size = original_size + BLOCKSIZE - (original_size % BLOCKSIZE);

上面给出了整个消息的完整大小(直到doFinal()调用),给出了输入消息的完整大小。我突然想到,您实际上只想知道最后一部分的长度—您只需要存储doFinal()调用的输出字节数组,并在该数组上使用.length()方法。

08-03 15:23