2010-02-06 44 views
14

Khi tính tổng kiểm tra MD5 trên một tệp lớn, kỹ thuật nào thường được sử dụng để kết hợp các giá trị MD5 khác nhau thành một giá trị duy nhất? Bạn chỉ cần thêm chúng lại với nhau? Tôi không thực sự quan tâm đến bất kỳ ngôn ngữ, thư viện hoặc API cụ thể nào sẽ làm điều này; thay vì tôi chỉ quan tâm đến kỹ thuật đằng sau nó. Ai đó có thể giải thích cách nó được thực hiện?Kết hợp các giá trị băm MD5

Với thuật toán sau đây trong pseudo-code:

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

Nhưng những gì chính xác Combine sẽ làm gì? Liệu nó có thêm hai bản phân tích MD5 với nhau hay không?

+0

Tại sao bạn muốn làm điều đó? – AndiDog

+0

Để tính giá trị MD5 cho các tệp quá lớn để vừa trong bộ nhớ – channel72

+5

MD5 chỉ có trạng thái 128 bit theo dõi đoạn tệp 512 bit trong khi tính toán; ai quan tâm đến tệp lớn như thế nào? –

Trả lời

16

Để tính toán giá trị MD5 cho các tập tin đó là quá lớn để phù hợp với bộ nhớ

Với ý nghĩ đó, bạn không muốn "kết hợp" hai băm MD5. Với bất kỳ việc triển khai MD5 nào, bạn có một đối tượng giữ trạng thái kiểm tra hiện tại. Vì vậy, bạn có thể trích xuất MD5 checksum bất cứ lúc nào, rất tiện dụng khi băm nhỏ hai tệp chia sẻ cùng một khởi đầu. Đối với các tệp lớn, bạn chỉ cần tiếp tục cho ăn dữ liệu - không có sự khác biệt nếu bạn băm tệp cùng một lúc hoặc trong các khối, vì trạng thái được ghi nhớ. Trong cả hai trường hợp, bạn sẽ nhận được cùng một băm.

2

Thư viện openSSL cho phép bạn thêm khối dữ liệu vào băm liên tục (sha1/md5) khi bạn đã hoàn tất việc thêm tất cả dữ liệu bạn gọi là phương thức Final và nó sẽ xuất ra băm cuối cùng.

Bạn không tính md5 trên từng khối riêng lẻ, sau đó thêm nó, thay vì bạn thêm dữ liệu vào phương pháp băm liên tục từ thư viện openssl. Điều này sau đó sẽ cung cấp cho bạn một băm md5 của tất cả các khối dữ liệu riêng lẻ mà không có giới hạn về kích thước dữ liệu đầu vào.

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

2

Câu hỏi này không có ý nghĩa nhiều như các thuật toán MD5 mất bất kỳ đầu vào chiều dài. Thư viện phong nha nên có các hàm để bạn không phải thêm toàn bộ thư vào một thời điểm vì thư được chia thành các khối được băm liên tiếp, với khối đang được xử lý tùy thuộc vào các băm kết quả từ trước đó vòng lặp.

Mã giả trong wikipedia article sẽ cung cấp tổng quan về cách hoạt động của thuật toán.

1

Hầu hết việc triển khai tính toán thông báo cho phép bạn cấp dữ liệu cho chúng trong các khối nhỏ hơn. Bạn không thể kết hợp nhiều thông báo MD5 theo cách mà kết quả sẽ bằng MD5 của toàn bộ đầu vào. MD5 thực hiện một số phần đệm và sử dụng số lượng byte được xử lý trong giai đoạn cuối làm cho trạng thái động cơ ban đầu không thể khôi phục được từ giá trị tiêu hóa cuối cùng.

+0

Vì vậy, sau đây là một ví dụ tuyệt vời về làm thế nào để không thực hiện nhiều kết hợp MD5? Người dùng đó chỉ cần ghép nối nhiều băm riêng lẻ cho các khối riêng lẻ của một tệp lớn. http://www.postgresql-archive.org/md5-large-object-id-tp5866710p5869128.html –

+0

@Thorsten: Có thể thích hợp để ghép các phần băm của các khối kích thước cố định và sau đó băm chuỗi liên kết một lần nữa để có được một Giá trị băm. Tổng số băm kết quả là không giống nhau mà bạn sẽ nhận được nếu bạn đã băm toàn bộ tập tin. Điều này có nghĩa là ghép nối là vô ích nếu bạn cần so sánh nó với một cái không được tính toán theo cách này nhưng nếu bạn định nghĩa giao thức của riêng bạn, bạn có thể quyết định xác định kích thước khối nhất định và tính toán băm của bạn luôn theo cách này. Chất lượng của hàm băm không tệ hơn hàm băm ban đầu. Việc chia sẻ tệp ed2key p2p được sử dụng băm như thế này. – x4u

6

MD5 là một thuật toán lặp lại. Bạn không cần phải tính toán một tấn MD5 nhỏ và sau đó kết hợp chúng bằng cách nào đó. Bạn chỉ cần đọc các phần nhỏ của tệp và thêm chúng vào thông báo khi bạn đang đi, vì vậy bạn không bao giờ phải có toàn bộ tệp trong bộ nhớ cùng một lúc. Đây là một triển khai 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 voila. Bạn có MD5 của toàn bộ tệp mà không bao giờ có toàn bộ tệp trong bộ nhớ cùng một lúc.

Cần lưu ý rằng nếu vì lý do nào đó bạn muốn MD5 băm phần phụ của tệp khi bạn đi (điều này đôi khi hữu ích khi thực hiện kiểm tra tạm thời trên một tệp lớn được chuyển qua kết nối băng thông thấp) thì bạn có thể nhận được chúng bằng cách nhân bản đối tượng tiêu hóa bất cứ lúc nào, như vậy

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

này không ảnh hưởng đến thực tế tiêu hóa đối tượng, do đó bạn có thể tiếp tục làm việc với các hash MD5 tổng thể. Nó cũng đáng chú ý là MD5 là một băm lỗi thời cho các mục đích mã hóa (như xác minh tính xác thực của tập tin từ một nguồn không đáng tin cậy) và nên được thay thế bằng một cái gì đó tốt hơn trong hầu hết các trường hợp, chẳng hạn như SHA-1. Đối với các mục đích không mã hóa, chẳng hạn như xác minh tính toàn vẹn của tệp giữa hai nguồn đáng tin cậy, MD5 vẫn còn đầy đủ.

+0

Tôi có một trường hợp sử dụng để tính tổng MD5. Tôi đọc nhiều tập tin song song và muốn có một tổng kiểm tra duy nhất cho toàn bộ bộ sưu tập (giả sử các tệp theo thứ tự bảng chữ cái). – Synesso

1

Đây là cách C# để kết hợp băm. Hãy tạo các phương thức mở rộng để đơn giản hóa mã người dùng.

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

Cách sử dụng:

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 và h2 đều giống nhau. Đó là nó.

+0

Chào mừng bạn đến với SO, user1326493 và cảm ơn bạn đã trả lời. – Brian

1

Ví dụ về Python 2.7 cho câu trả lời của AndiDog. Tệp 123.txt có nhiều dòng.

>>> 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' 

Đối với tệp lớn không phù hợp với bộ nhớ, có thể đọc từng dòng hoặc từng đoạn một. Một cách sử dụng MD5 này là so sánh hai tệp lớn khi lệnh diff không thành công.

Các vấn đề liên quan