合并MD5哈希值

19

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

给出以下伪代码算法:

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

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


你为什么想要这样做? - AndiDog
为了计算那些太大而无法放入内存的文件的MD5值 - channel72
5
在计算过程中,MD5只有一个128位的状态来跟踪512位文件块;文件大小有多大又有什么关系呢? - Carl Norum
@CarlNorum问题出现在接口上,哈希实现可能会保持状态,但是无法访问它。考虑pgcrypto中的digest函数,它仅适用于一块数据,因为状态是在单个调用之后被隐藏起来的,所以无法提供附加数据。因此,了解是否可以组合个体哈希非常有用。 https://www.postgresql.org/docs/9.1/static/pgcrypto.html 一些用户这样做... http://www.postgresql-archive.org/md5-large-object-id-tp5866710p5869128.html - Thorsten Schöning
7个回答

16
为了计算无法在内存中容纳的大文件的MD5值。
有鉴于此,您不想“合并”两个MD5哈希。使用任何MD5实现,您都可以拥有一个保持当前校验和状态的对象。因此,您可以随时提取MD5校验和,这在对共享相同开头的两个文件进行哈希处理时非常方便。对于大文件,您只需要不断地输入数据 - 无论是一次性哈希文件还是分块哈希文件,状态都将被记住。在两种情况下,您将获得相同的哈希值。

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


我有一个需要对MD5进行求和的用例。我并行读取多个文件,并希望对整个集合进行单个校验和计算(假设文件按文件名字母顺序排列)。 - Synesso

2
一个Python 2.7的例子,来自AndiDog的回答。文件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'

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


2
openSSL库允许您将数据块添加到正在进行的哈希(sha1/md5)中,然后当您完成添加所有数据时,调用Final方法,它将输出最终哈希值。
您不需要对每个单独的块计算md5再添加,而是将数据添加到openSSL库中的进行哈希的方法中。这将为您提供所有单个数据块的md5哈希值,并且输入数据大小没有限制。 http://www.openssl.org/docs/crypto/md5.html#

2
这个问题似乎没有太多意义,因为MD5算法可以接受任何长度的输入。一个好的库应该有函数,让你不需要一次性添加整个消息,因为消息会被分块并按顺序哈希,正在处理的块仅依赖于前一个循环中的结果哈希。

维基百科文章中的伪代码应该可以给出算法如何工作的概述。


1
大多数摘要计算实现允许您将数据分成较小的块进行处理。您无法将多个MD5摘要组合在一起,使结果等于整个输入的MD5。MD5会进行一些填充,并在最终阶段使用已处理字节数,这使得原始引擎状态无法从最终摘要值中恢复。

所以以下是一个不好的多重MD5组合实现的例子?该用户只是将大文件的各个块的多个单独哈希连接在一起。http://www.postgresql-archive.org/md5-large-object-id-tp5866710p5869128.html - Thorsten Schöning
@Thorsten:将固定大小块的哈希值连接起来,然后再对连接后的字符串进行哈希以获得单个哈希值是可行的。所得到的哈希值与您对整个文件进行哈希所得到的哈希值不同。这意味着如果您需要将其与未按此方式计算的哈希值进行比较,则连接是无用的。但是,如果您定义自己的协议,可以决定定义特定的块大小,并始终以此方式计算哈希值。哈希的质量不会比原始哈希函数差。edonkey P2P文件共享就使用了这样的哈希值。 - x4u

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));
        }

是一样的。就这样。


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