14

在大文件上计算单个 MD5 校验和时,一般使用什么技术将各种 MD5 值组合成单个值?你只是把它们加在一起吗?我对任何可以做到这一点的特定语言、库或 API 并不真正感兴趣;相反,我只是对它背后的技术感兴趣。有人可以解释它是如何完成的吗?

给定以下伪代码算法:

MD5Digest X
for each file segment F
   MD5Digest Y = CalculateMD5(F)
   Combine(X,Y)

但是具体会Combine做什么呢?它将两个 MD5 摘要加在一起,还是什么?

4

7 回答 7

16

为了计算太大而无法放入内存的文件的 MD5 值

考虑到这一点,您不想“组合”两个 MD5 哈希。对于任何MD5 实现,您都有一个保持当前校验和状态的对象。因此,您可以随时提取 MD5 校验和,这在对共享相同开头的两个文件进行哈希处理时非常方便。对于大文件,您只需不断输入数据 - 一次或分块对文件进行哈希处理没有区别,因为会记住状态。在这两种情况下,您都会得到相同的哈希值。

于 2010-02-06T18:55:52.777 回答
6

MD5 是一种迭代算法。您不需要计算大量的小型 MD5,然后以某种方式将它们组合起来。您只需读取文件的一小部分并在进行时将它们添加到摘要中,因此您不必一次将整个文件保存在内存中。这是一个java实现。

FileInputStream f = new FileInputStream(new File("bigFile.txt"));
MessageDigest digest = MessageDigest.getInstance("md5");
byte[] buffer = new byte[8192];
int len = 0;
while (-1 != (len = f.read(buffer))) {
   digest.update(buffer,0,len);
}
byte[] md5hash = digest.digest();

等等。您拥有整个文件的 MD5,而不会一次将整个文件放在内存中。

值得注意的是,如果由于某种原因您确实需要文件子部分的 MD5 哈希值(这有时对于对通过低带宽连接传输的大文件进行临时检查很有用),那么您可以通过克隆来获取它们任何时候的摘要对象,就像这样

byte[] interimHash = ((MessageDigest)digest.clone()).digest();

这不会影响实际的摘要对象,因此您可以继续使用整个 MD5 散列。

还值得注意的是,MD5 是用于加密目的的过时哈希(例如验证来自不受信任来源的文件真实性),并且在大多数情况下应该用更好的东西代替,例如 SHA-1。对于非加密目的,例如验证两个可信来源之间的文件完整性,MD5 仍然足够。

于 2010-02-06T18:56:32.790 回答
2

openSSL 库允许您将数据块添加到正在进行的哈希 (sha1/md5) 中,然后当您完成添加所有数据时调用该Final方法,它将输出最终哈希。

您不会在每个单独的块上计算 md5 然后添加它,而是将数据添加到 openssl 库中正在进行的哈希方法。然后,这将为您提供所有单个数据块的 md5 散列,对输入数据大小没有限制。

http://www.openssl.org/docs/crypto/md5.html#

于 2010-02-06T18:50:23.397 回答
2

这个问题没有多大意义,因为 MD5 算法需要任何长度的输入。一个体面的库应该具有这样的功能,这样您就不必一次添加整个消息,因为消息被分解为按顺序散列的块,正在处理的块仅取决于前一个的结果散列环形。

维基百科文章中的伪代码应概述该算法的工作原理。

于 2010-02-06T18:53:29.220 回答
2

AndiDog 答案的 Python 2.7 示例。文件 123.txt 有多行。

>>> import hashlib
>>> md5_A, md5_B, md5_C = hashlib.md5(), hashlib.md5(), hashlib.md5()
>>> with open('123.txt', 'r') as f_r:
...     md5_A.update(f_r.read()) # read whole contents
... 
>>> with open('123.txt', 'r') as f_r:
...     for line in f_r: # read file line by line
...         md5_B.update(line)
... 
>>> with open('123.txt', 'r') as f_r:
...     while True: # read file chunk by chunk
...         chunk = f_r.read(10)
...         if not chunk: break
...         md5_C.update(chunk)
... 
>>> md5_A.hexdigest()
'5976ddfa19bc2e1669ac3bd836101f58'
>>> md5_B.hexdigest()
'5976ddfa19bc2e1669ac3bd836101f58'
>>> md5_C.hexdigest()
'5976ddfa19bc2e1669ac3bd836101f58'

对于无法放入内存的大文件,可以逐行或逐块读取。此 MD5 的一种用法是在 diff 命令失败时比较两个大文件。

于 2014-11-05T22:34:57.947 回答
1

大多数摘要计算实现允许您以较小的块为它们提供数据。您不能以结果等于整个输入的 MD5 的方式组合多个 MD5 摘要。MD5 做了一些填充,并在最后阶段使用已处理的字节数,这使得原始引擎状态无法从最终摘要值中恢复。

于 2010-02-06T18:55:56.910 回答
1

这是一种组合哈希的 C# 方法。让我们制作扩展方法来简化用户代码。

public static class MD5Append
{
    public static int Append(this MD5 md5, byte[] data)
    {
        return md5.TransformBlock(data, 0, data.Length, data, 0);
    }

    public static void AppendFinal(this MD5 md5, byte[] data)
    {
        md5.TransformFinalBlock(data, 0, data.Length);
    }
}

用法:

   using (var md5 = MD5CryptoServiceProvider.Create("MD5"))
        {
            md5.Initialize();

            var abcBytes = Encoding.Unicode.GetBytes("abc");
            md5.Append(abcBytes);
            md5.AppendFinal(abcBytes);

            var h1 = md5.Hash;

            md5.Initialize(); // mandatory
            var h2= md5.ComputeHash(Encoding.Unicode.GetBytes("abcabc"));

            Console.WriteLine(Convert.ToBase64String(h1));
            Console.WriteLine(Convert.ToBase64String(h2));
        }

h1 和 h2 相同。就是这样。

于 2013-11-01T18:47:56.357 回答