C# 使用CryptoStreams加密和HMAC数据

C# 使用CryptoStreams加密和HMAC数据,c#,encryption,cryptography,digital-signature,hmac,C#,Encryption,Cryptography,Digital Signature,Hmac,假设我们有一条用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

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

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

因此,我提出了一个暂时可行的方法。我创建了一个名为
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
中。这隐藏了一个事实,即
aestream
macStream
CryptoStream
,因此当你在
macStream
上调用
FlushFinalBlock
时,它不会在
aestream
上调用
FlushFinalBlock
,因为我正在处理类似的主题,我会在这里给出一个答案:

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

然后,您可以在
aesStream
上执行
FlushFinalBlock()
后写入HMAC字节

不要忘记处理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。这是加密和MAC。微妙的区别在于,您正在散列明文而不是密文。再见,谢谢。
aestream
macStream
作为参数。据我所知,它散列加密流,而不是原始输入(消息)。也许我弄错了。哦,对不起,你是对的。流被加密,然后散列。对不起,当我读到这篇文章时,它看起来像是hmac流接受了明文流,aesStream接受了hmac流,所以看起来明文被加密了。我错过什么了吗?编辑nvm它正在写入而不是读取,因此消息会写入aesStream,aesStream会写入hmacStream,hmacStream会写入客户端。别担心。