4

假设我们有一条使用 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,但我想避免这种情况。有任何想法吗?

4

3 回答 3

4

由于我正在处理类似主题的atm,我将在这里回答:

就像 Maarten Bodewes 写的那样,我建议你先加密然后 MAC。

然后,您可以FlushFinalBlock()aesStream.

不要忘记处理 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();
于 2017-09-13T07:42:48.560 回答
2

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

于 2013-03-01T00:05:47.140 回答
0

因此,我实施了一个似乎暂时有效的 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. 这隐藏了aesStreama CryptoStreamfrommacStream所以当你调用它FlushFinalBlockmacStream它不会调用FlushFinalBlock的事实aesStream

于 2013-03-01T00:57:18.467 回答