【问题标题】:Combining MD5 hash values结合 MD5 哈希值
【发布时间】:2011-01-13 22:18:49
【问题描述】:

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

在伪代码中给出以下算法:

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

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

【问题讨论】:

  • 你为什么要这样做?
  • 为了计算太大而无法放入内存的文件的 MD5 值
  • MD5 在计算过程中只有 128 位状态跟踪 512 位文件块;谁在乎文件有多大?
  • @CarlNorum 问题在于哈希实现可能保持状态但无法访问它的接口。考虑pgcrypto 中的digest 函数,它仅适用于数据块,无法提供额外的数据,仅仅是因为状态在单独调用时被覆盖。因此,了解单个哈希是否可以组合是很有用的。 postgresql.org/docs/9.1/static/pgcrypto.html 一些用户这样做... postgresql-archive.org/…

标签: md5 algorithm checksum


【解决方案1】:

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 命令失败时比较两个大文件。

【讨论】:

    【解决方案2】:

    这是一种 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 是一样的。就是这样。

    【讨论】:

      【解决方案3】:

      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 仍然足够。

      【讨论】:

      • 我有一个需要对 MD5 求和的用例。我并行读取多个文件,并希望对整个集合有一个校验和(假设文件按文件名字母顺序排列)。
      【解决方案4】:

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

      【讨论】:

      • 那么下面是一个很好的例子,说明如何不实现多个MD5组合?该用户只是为大文件的各个块连接多个单独的哈希值。 postgresql-archive.org/…
      • @Thorsten:连接固定大小块的哈希和然后再次对连接的字符串进行哈希以获得单个哈希值可能是合适的。如果您对整个文件进行哈希处理,得到的哈希总和与您得到的哈希总和不同。这意味着如果您需要将连接与未以这种方式计算的连接进行比较,则连接是无用的,但如果您定义自己的协议,您可以决定定义某个块大小并始终以这种方式计算您的哈希值。散列的质量并不比原始散列函数差。 edonkey p2p 文件共享使用这样的哈希。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

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

        wikipedia article 中的伪代码应概述该算法的工作原理。

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

            猜你喜欢
            • 2012-08-17
            • 1970-01-01
            • 2011-05-17
            • 1970-01-01
            • 2016-03-16
            • 2020-12-08
            • 2017-05-04
            相关资源
            最近更新 更多