使用CryptoStreams加密和HMAC数据

本文关键字:HMAC 数据 加密 CryptoStreams 使用 | 更新日期: 2023-09-27 18:01:33

假设我们有一个用HMAC签名的消息,然后该消息和HMAC被加密,然后通过TCP套接字发送:

// endpoint info excluded
TcpClient client = new TcpClient();
var stream = client.GetStream();
// assume pre-shared keys are used and set at this point
AesManaged aes = new AesManaged();
var aesEncryptor = aes.CreateEncryptor();
CryptoStream aesStream = new CryptoStream(
    stream, aesEncryptor, CryptoStreamMode.Write);
// assume pre-shared keys here too
HMACSHA256 mac = new HMACSHA256();
CryptoStream macStream = new CryptoStream(
    aesStream, mac, CryptoStreamMode.Write);
// assume a message with actual data is written to the macStream
// which updates the hash of the HMAC and also pipes the message
// to the aesStream which encrypts the data and writes it to the
// TCP socket stream
byte[] message = new byte[1024];
macStream.Write(message, 0, message.Length);
macStream.FlushFinalBlock();
// flushing the final block of the macStream actually flushes the
// final block of the aesStream, so I get an error when trying to
// write the HMAC hash to the aesStream
aesStream.Write(mac.Hash, 0, mac.Hash.Length);
aesStream.FlushFinalBlock();

我抽象出了很多代码,所以这不是一个工作示例。我可能会解决这个问题,我写数据两次,一次到HMAC。TransformBlock和aesStream,但我想避免那样。什么好主意吗?

使用CryptoStreams加密和HMAC数据

由于我正在处理类似的主题,我将在这里给出答案:

就像Maarten Bodewes写的,我建议你加密mac。

aesStream上执行FlushFinalBlock()之后,可以写入HMAC-Bytes。

不要忘记处理CryptoStreams和Algorithms对象!

var stream = client.GetStream();
HMACSHA256 mac = new HMACSHA256();
CryptoStream macStream = new CryptoStream(stream, mac, CryptoStreamMode.Write);
AesManaged aes = new AesManaged();
var aesEncryptor = aes.CreateEncryptor();
CryptoStream aesStream = new CryptoStream(macStream, aesEncryptor, CryptoStreamMode.Write);
byte[] message = new byte[1024];
aesStream.Write(message, 0, message.Length);
aesStream.FlushFinalBlock();
//No need to FlushFinalBlock() on macStream, would throw
//Write HMAC here
stream.Write(mac.Hash, 0, mac.Hash.Length);
//Dispose
aesStream.Dispose();
aes.Dispose();
macStream.Dispose();
mac.Dispose();

您确实应该在MAC之前执行加密以创建安全解决方案,特别是如果您通过套接字发送数据(因为您可能容易受到填充oracle攻击)。所以,虽然你的问题是有效的,是不是最好改变流的顺序。

所以我放了一个hack到位,似乎是工作的时刻。我创建了一个名为ProtectedStream的流包装器类。以下是要点:

public class ProtectedStream : Stream
{
  private Stream stream;
  public ProtectedStream(Stream stream)
  {
    if(stream == null)
      throw new ArgumentNullException("stream");
    this.stream = stream;
  }
  public override void Close()
  {
    this.stream.Close();
    base.Close();
  }
  public override int Read(byte[] buffer, int offset, int count)
  {
    return this.stream.Read(buffer, offset, count);
  }
  public override void Write(byte[] buffer, int offset, int count)
  {
    this.stream.Write(buffer, offset, count);
  }
  // and continue overriding every other overridable Stream member
  // in the same fashion
}

原始代码是这样的:

// assume pre-shared keys are used and set at this point
AesManaged aes = new AesManaged();
var aesEncryptor = aes.CreateEncryptor();
CryptoStream aesStream = new CryptoStream(
    stream, aesEncryptor, CryptoStreamMode.Write);
// assume pre-shared keys here too
HMACSHA256 mac = new HMACSHA256();
CryptoStream macStream = new CryptoStream(
    new ProtectedStream(aesStream), mac, CryptoStreamMode.Write);

注意aesStream被包裹在ProtectedStream中。这隐藏了aesStreammacStreamCryptoStream的事实,所以当你在macStream上调用FlushFinalBlock时,它不会在aesStream上调用FlushFinalBlock