2010-02-06 129 views
14

在大文件上計算單個MD5校驗和時,通常使用什麼技術將各種MD5值組合爲單個值?你只是把它們加在一起?我對任何特定的語言,庫或API都沒有興趣,相反,我只是對它背後的技術感興趣。有人可以解釋它是如何完成的嗎?組合MD5散列值

考慮下面的算法僞代碼:

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

但究竟什麼會Combine辦?它是否將兩個MD5摘要一起添加,或者是什麼?

+0

你爲什麼要這麼做? – AndiDog 2010-02-06 18:49:27

+0

爲了計算太大而不適合內存的文件的MD5值 – channel72 2010-02-06 18:50:05

+5

在計算過程中,MD5僅具有跟蹤512位文件塊的128位狀態;誰在乎這個文件有多大? – 2010-02-06 18:51:33

回答

16

爲了計算其過大,以適應在內存

考慮到這一點的文件的MD5值,你不想「結合」兩個MD5哈希值。使用任何 MD5實現,您都有一個保持當前校驗和狀態的對象。因此,您可以隨時提取MD5校驗和,這在散列共享相同開始的兩個文件時非常方便。對於大文件,您只需要繼續提供數據 - 如果您一次或分塊地對文件進行哈希處理,狀態會被記住,這並沒有什麼不同。在這兩種情況下,你都會得到相同的散列。

2

openSSL庫允許您將數據塊添加到正在進行的散列(sha1/md5),然後當您完成添加所有數據時,您將調用Final方法,並且它會輸出最終的散列。

您不計算每個塊上的md5然後添加它,而是將數據添加到來自openssl庫的正在進行的散列方法。然後這將給你一個所有單個數據塊的md5哈希值,對輸入數據大小沒有限制。

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

2

爲MD5算法將任意長度的輸入這個問題並沒有太大的意義。一個體面的函數庫應該有函數,這樣你就不必一次添加整個消息,因爲消息被分解爲順序散列的塊,正在處理的塊只取決於前一個結果的散列循環。

wikipedia article中的僞代碼應提供算法工作原理的概述。

1

大多數摘要計算實現允許您以較小的塊爲它們提供數據。您不能合併多個MD5摘要,其結果將等於整個輸入的MD5。 MD5會執行一些填充操作,並在最終階段使用已處理字節的數量,這會使最初的引擎狀態從最終摘要值無法恢復。

+0

所以下面是如何不實現多個MD5組合的好例子?該用戶只是將大量文件的各個塊的多個單獨哈希連接起來。 http://www.postgresql-archive.org/md5-large-object-id-tp5866710p5869128.html – 2017-05-11 07:42:16

+0

@Thorsten:連接固定大小的塊的哈希和可以是適當的,然後再次對連接的字符串進行哈希以得到單個哈希值。所得到的散列總和與散列整個文件時得到的散列總數不相同。這意味着如果您需要將它與不以這種方式計算的結果進行比較,則連接無用,但如果您定義了自己的協議,則可以決定定義特定的塊大小並通過這種方式計算散列值。散列的質量並不比原始散列函數差。 edonkey p2p文件共享使用哈希像這樣。 – x4u 2017-05-11 09:30:05

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

Et瞧。您擁有整個文件的MD5,而無需將整個文件同時存儲在內存中。

值得注意的是,如果由於某種原因,當您需要時,您確實需要文件的子部分的MD5哈希值(這對於在通過低帶寬連接傳輸的大文件上執行臨時檢查有時很有用),那麼您可以在任何時候克隆摘要對象得到他們,像這樣

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

這不會影響實際的摘要對象,所以你可以繼續整體MD5哈希工作。

它也值得注意的是,MD5是一個用於加密目的的過時散列(例如驗證來自不可信源的文件真實性),應該在大多數情況下用SHA-1更好地替換它。對於非加密目的(如驗證兩個可信來源之間的文件完整性),MD5仍然適用。

+0

我有一個需要總結MD5的用例。我並行讀取多個文件,並希望爲整個集合提供一個校驗和(假設文件按字母順序排列)。 – Synesso 2015-11-12 04:28:25

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是相同的。而已。

+0

歡迎來到SO,user1326493,並感謝您的回答。 – Brian 2013-11-01 19:08:49

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命令失敗時比較兩個大文件。