当我使用MemoryStream时,为什么这两个文件的哈希值相同?

8

我正在编写一个从jpg文件创建哈希值的C#例程。如果我将一个字节数组传递给我的SHA512对象,那么我会得到预期的结果。然而,如果我传递一个内存流,那么这两个文件总是具有相同的哈希值。

示例1:

        SHA512 mySHA512 = SHA512.Create();

        Image img1 = Image.FromFile(@"d:\img1.jpg");
        Image img2 = Image.FromFile(@"d:\img2.jpg");
        MemoryStream ms1 = new MemoryStream();
        MemoryStream ms2 = new MemoryStream();

        img1.Save(ms1, ImageFormat.Jpeg);
        byte[] buf1 = ms1.GetBuffer();
        byte[] hash1 = mySHA512.ComputeHash(buf1);

        img2.Save(ms2, ImageFormat.Jpeg);
        byte[] buf2 = ms2.GetBuffer();
        byte[] hash2 = mySHA512.ComputeHash(buf2);

        if (Convert.ToBase64String(hash1) == Convert.ToBase64String(hash2))
            MessageBox.Show("Hashed the same");
        else
            MessageBox.Show("Different hashes");

这会生成“不同的哈希值”。但是,ComputeHash方法的其中一个重载接受一个流对象,我更愿意使用它。当我这样做时:

        SHA512 mySHA512 = SHA512.Create();

        Image img1 = Image.FromFile(@"d:\img1.jpg");
        Image img2 = Image.FromFile(@"d:\img2.jpg");
        MemoryStream ms1 = new MemoryStream();
        MemoryStream ms2 = new MemoryStream();

        img1.Save(ms1, ImageFormat.Jpeg);
        byte[] hash1 = mySHA512.ComputeHash(ms1);

        img2.Save(ms2, ImageFormat.Jpeg);
        byte[] hash2 = mySHA512.ComputeHash(ms2);

        if (Convert.ToBase64String(hash1) == Convert.ToBase64String(hash2))
            MessageBox.Show("Hashed the same");
        else
            MessageBox.Show("Different hashes");

这会产生“相同的哈希值”。

我错过了什么?


它们不是同一张图片的副本,对吧? - Joel Coehoorn
如果在 .Save 调用后寻求流的开头会发生什么?例如:ms1.Seek(0, SeekOrigin.Begin); - Joe
1
在这种情况下,您可以使用 mySHA512.ComputeHash.ComputeHash(ms1.GetBuffer(),0,(int)ms1.Length)。避免不必要的复制。 - CodesInChaos
除了下面的答案之外,我还要指出MemoryStream.GetBytes()方法比调用GetBuffer()更正确。前者将返回被限制在流实际内容中的数据,而后者则返回完整的缓冲区,包括末尾未使用的填充。 - Peter Duniho
1个回答

18

你没有将MemoryStream倒回,因此哈希值是从一个空字节序列计算出来的。请使用

ms1.Position = 0;
ms2.Position = 0;

在调用 Save 方法之后,还有一个需要注意的地方:不要以这种方式使用 GetBuffer 方法。应该使用 ToArray 方法,它会返回一个长度与流相同的字节数组,而 GetBuffer 方法返回的原始缓冲区通常会有一些填充,你不希望意外地使用它们。当然,如果确保只使用其中的相关部分,那么可以使用 GetBuffer 方法,这样可以避免创建数据的新副本。


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