写入流时计算哈希值

22

我正在创建一个需要签名的加密文件格式。为此,我需要计算我写入流中的内容的哈希码。

在.Net框架中有许多可用的哈希算法,它们可以使用并且工作正常,但是这需要我对流进行三遍处理。

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

using (Stream fileStream = File.Open("myfile.bin", FileMode.Create))
{
    //Write content
    fileStream.Write(content, 0, content.Length);
}

byte[] hashValue = null;
using (Stream fileStream = File.Open("myfile.bin", FileMode.Open))
{
    HashAlgorithm hash = SHA256.Create();
    hashValue = hash.ComputeHash(fileStream);
}

using (Stream fileStream = File.Open("myfile.bin", FileMode.Append))
{
    fileStream.Write(hashValue, 0, hashValue.Length);
}

如果加密到文件是可以的,但如果加密到网络目标,则字节将不再可用。

因此,基本上我只需要处理数据一次。在CodeProject上有一篇文章,实现了CRC32作为流,每当写入数据时都会计算CRC32码。

类似于:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

using (FileStream fileStream = File.Create("myfile.bin"))
using (Stream crcStream = new CRCStream(fileStream)) //Takes a base stream
{
    //Write content
    crcStream.Write(content, 0, content.Length); 

    //Write checksum
    fileStream.Write(crcStream.WriteCRC, 0, 4);
}

显然,CRC32并不是哈希算法,但是如果有像HashStream这样的东西,它接受一个HashAlgorithm,每次调用写入/读取时都会更新哈希值,那就太好了。

类似于:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

HashAlgorithm hashAlgo = SHA256.Create();

using (FileStream fileStream = File.Create("myfile.bin"))
using (HashStream hashStream = new HashStream(hashAlgo, fileStream))
{
    //Write content to HashStream 
    hashStream.Write(content, 0, content.Length);

    //Write checksum
    fileStream.Write(hashStream.HashValue, 0, hashAlgo.HashSize / 8);
}

阅读文件应该以相似的方式进行,因此当您读取文件时(不包括哈希),已经计算出已读内容的哈希值。

是否可以使用.net框架现有组件构建类似的东西?

编辑:

谢谢Peter!我不知道CryptoStream可以接受HashAlgorithm。因此,要同时加密和哈希,我可以像这样做:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

SymmetricAlgorithm cryptoSymetric = Aes.Create();
HashAlgorithm cryptoHash = SHA256.Create();

using (FileStream file = new FileStream("Crypto.bin", FileMode.Create, FileAccess.Write))
using (CryptoStream hashStream = new CryptoStream(file, cryptoHash, CryptoStreamMode.Write))
using (CryptoStream cryptStream = new CryptoStream(hashStream, cryptoSymetric.CreateEncryptor(), CryptoStreamMode.Write))
{
    cryptStream.Write(content, 0, content.Length);
    cryptStream.FlushFinalBlock();

    byte[] hashValue = cryptoHash.Hash;

    file.Write(hashValue, 0, hashValue.Length);
}

我认为你应该保险起见,在 cryptStream.FlushFinalBlock() 之后调用 hashStream.FlushFinalBlock() - Peter Taylor
2
我尝试过了,但它抛出了一个异常。显然,FlushFinalBlock也会在基础流上调用。即使它被声明为Stream类型。 System.NotSupportedException Message=在CryptoStream上调用FlushFinalBlock()方法两次。它只能被调用一次。 - soren.bendtsen
1
您不必显式调用 FlushFinalBlock文档中指出:“调用 Close 方法将调用 FlushFinalBlock。” 因此,在 using 结束后,cryptoHash.Hash 将包含哈希值。 - Dave
2个回答

47

CryptoStream已经为您完成了这个操作。

SHA256 hashAlg = new SHA256Managed();
CryptoStream cs = new CryptoStream(_out, hashAlg, CryptoStreamMode.Write);
// Write data here
cs.FlushFinalBlock();
byte[] hash = hashAlg.Hash;

4
你可以使用HashAlgorithm.TransformBlock进行分块哈希变换。这可以包装在流实现中,因此你可以像你所建议的那样拥有一个HashStream。在获取哈希值之前,请记得调用TransformFinalBlock
HashAlgorithm是抽象的,所以当然需要选择你想要的具体实现。可以从MD5、SHA系列和其他选取。

1
你知道一个好的同步/异步流包装器的实现,可以在读取或写入过程中计算哈希值吗? - MuiBienCarlota

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接