.NET MD5 Hash of String和Stream之间的区别

时间:2013-01-08 00:34:58

标签: .net hash f# md5

我目前遇到的问题是必须散列会导致太多内存压力的文件,而我正试图找出是否可以使用文件流动态创建散列。

在研究可能性时,我决定编写一个快速的小测试,并确保MD5的ComputeHash在接受字符串和流的方法调用之间返回相同的哈希值。

let CreateMD5HashFromString (value: string) =
     Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(value)))

let CreateMD5HashFromStream (value: Stream) =
     Convert.ToBase64String(MD5.Create().ComputeHash(value))

我正在使用以下单元测试来测试呼叫:

[<TestMethod>]
member this.``CreateMD5Hash is the same between a string and a file stream`` () =
    let sampleText = File.ReadAllText("Sample.txt")
    let textMD5 = Security.CreateMD5HashFromString(sampleText);
    let streamMD5 = Security.CreateMD5HashFromStream(File.OpenRead("Sample.txt"))

    Assert.AreEqual(textMD5, streamMD5)

正在阅读一个小样本文件进行测试。此测试失败,因为生成的哈希值不同。对我来说这似乎不正确,但不完全确定。有谁知道这些应该是否相同?

另外,第二个问题,我是通过使用ComputeHash的流重载来保存自己的内存问题还是在散列之前加载整个流?我试图解散相关的.NET程序集,但是试图找出HashCore在引擎盖下做的事情就迷失了。

2 个答案:

答案 0 :(得分:3)

实际上非常简单:你不能假设文本等于它的底层二进制表示。

创建 < p>

public static void Main(string[] args)
{
    System.IO.File.WriteAllBytes("test", System.Text.Encoding.ASCII.GetBytes("test string"));

    var inputString = System.IO.File.ReadAllText("test");
    var inputBytes = System.IO.File.ReadAllBytes("test");
    var inputStream = new System.IO.FileStream("test", System.IO.FileMode.OpenOrCreate);

    var stringHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(System.Text.Encoding.ASCII.GetBytes(inputString)));
    var streamHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(inputStream));
    var bytesHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(inputBytes));

    Console.WriteLine("String hash: {0}", stringHash);
    Console.WriteLine("Stream hash: {0}", streamHash);
    Console.WriteLine("Bytes hash: {0}", streamHash);

    Console.WriteLine("\nMD5s {0}", stringHash == streamHash && streamHash == bytesHash ? "match" : "don't match");
}

带输出

String hash: b421md6Yb6t6IWJbeRZYnA==
Stream hash: b421md6Yb6t6IWJbeRZYnA==
Bytes hash: b421md6Yb6t6IWJbeRZYnA==

MD5s match

但是,这仅在假设磁盘上的文件是纯ASCII时才有效。在任何其他情况下都有保证。例如,许多非ASCII文件以BOM(字节顺序标记)开始,以表示编码类型。这将在二进制字节数组哈希中表示,但不在内存中的字符串哈希中表示。 UTF-8和unicode通常可以为同一个字符串提供十几种不同的表示形式 - 当将string对象加载到与磁盘上不同的表示形式时,可以对字符串进行规范化。

答案 1 :(得分:2)

我认为关键问题是,源文件中使用了什么编码?

如果使用Encoding.ASCII.GetBytes获得的字节数组包含与Stream相同的字节,则哈希值将相同,但只有当您使用包含相同编码的文件时才会出现这种情况。与GetBytes一起使用的文件。文件中没有签名。

这与MD5功能无关,因此您可以通过检查来更轻松地测试(假设文件小于10kB - 否则您需要更大的缓冲区):

let res1 = Encoding.ASCII.GetBytes(File.ReadAllText("test.txt"))
let buffer = Array.zeroCreate 10240
let size = File.OpenRead("D:\\temp\\test.fsx").Read(buf, 0, 10240)
let res2 = buffer.[0 .. size - 1]

res1 = res2 // Are the byte arrays the same?

当我尝试运行时,我必须解决两件事:

  • 我使用的文件是用带有签名的UTF-8 保存的,因此开头有3个字节指定编码(如果我使用{{我只有相同的字节数组} 1}}

  • 我必须使用相同的编码保存文件(在这种情况下是ASCII,但是通常这样做可能很棘手)。或者,您可以在读取文件时指定编码,但随后可能会对无意义文本进行哈希处理。