2012-04-25 9 views
13

Tôi đang viết bằng C sử dụng thư viện OpenSSL.Cách tính giá trị băm MD5 của một tệp lớn trong C?

Tôi làm cách nào để tính giá trị băm của tệp lớn bằng md5?

Như tôi biết, tôi cần tải toàn bộ tệp vào RAM dưới dạng mảng char và sau đó gọi hàm băm. Nhưng nếu tập tin dài khoảng 4Gb thì sao? Âm thanh như một ý tưởng tồi.

SOLVED: Cảm ơn askovpen, tôi đã tìm thấy lỗi của mình. Tôi đã sử dụng

while ((bytes = fread (data, 1, 1024, inFile)) != 0) 
    MD5_Update (&mdContext, data, 1024); 

không

while ((bytes = fread (data, 1, 1024, inFile)) != 0) 
    MD5_Update (&mdContext, data, bytes); 
+0

Tôi tò mò muốn biết cần thiết cho nó? Nó chắc chắn có thể là một ý tưởng tồi nếu nó không phải là luồng an toàn, vì nó có thể chặn chương trình trong một thời gian dài và P.O. người dùng – crockpotveggies

+5

Mã hóa một tệp không giống như băm tệp bằng hàm băm như MD5. Bạn có thực sự có nghĩa là băm, hoặc bạn muốn mã hóa các tập tin? – Oleksi

+7

MD5 dựa trên luồng. Bạn không cần phải tải toàn bộ 4GB vào bộ nhớ cùng một lúc - bạn đọc nó theo khối. –

Trả lời

28

dụ

gcc -g -Wall -o file file.c -lssl -lcrypto

#include <stdio.h> 
#include <openssl/md5.h> 

int main() 
{ 
    unsigned char c[MD5_DIGEST_LENGTH]; 
    char *filename="file.c"; 
    int i; 
    FILE *inFile = fopen (filename, "rb"); 
    MD5_CTX mdContext; 
    int bytes; 
    unsigned char data[1024]; 

    if (inFile == NULL) { 
     printf ("%s can't be opened.\n", filename); 
     return 0; 
    } 

    MD5_Init (&mdContext); 
    while ((bytes = fread (data, 1, 1024, inFile)) != 0) 
     MD5_Update (&mdContext, data, bytes); 
    MD5_Final (c,&mdContext); 
    for(i = 0; i < MD5_DIGEST_LENGTH; i++) printf("%02x", c[i]); 
    printf (" %s\n", filename); 
    fclose (inFile); 
    return 0; 
} 

kết quả:

$ md5sum file.c 
25a904b0e512ee546b3f47574703d9fc file.c 
$ ./file 
25a904b0e512ee546b3f47574703d9fc file.c 
+0

may unsigned char data [1024]; khi ((byte = fread (dữ liệu, 1, 1024, inFile))! = 0) là dữ liệu chưa được ký [4096]; trong khi ((bytes = fread (dữ liệu, 1, 4096, inFile))! = 0) ? – user1031143

3

Bạn không cần phải tải toàn bộ tập tin trong bộ nhớ cùng một lúc. Bạn có thể sử dụng các hàm MD5_Init(), MD5_Update() and MD5_Final() để xử lý nó theo các khối để tạo băm. Nếu bạn lo lắng về việc làm cho nó hoạt động "nguyên tử", có thể cần phải khóa tệp để ngăn người khác thay đổi nó trong khi hoạt động.

6

Đầu tiên, MD5 là một thuật toán băm. Nó không mã hóa bất cứ thứ gì.

Dù sao, bạn có thể đọc tệp theo từng kích thước của bất kỳ kích thước nào bạn thích. Gọi MD5_Init một lần, sau đó gọi MD5_Update với từng đoạn dữ liệu bạn đã đọc từ tệp. Khi bạn hoàn tất, hãy gọi số MD5_Final để nhận kết quả.

+0

Tôi đã thử điều đó.Tôi đã chia nhỏ tập tin thành các khối 16 byte và chuyển chúng sang MD5_Update, nhưng băm đã sai. – user1256821

+1

Vì vậy, sửa lỗi trong mã của bạn khiến cho hàm băm sai. (Ngoài ra, 16 byte khối sẽ được làm chậm. Bạn sẽ làm tốt hơn rất nhiều bằng cách sử dụng các khối 64KB khi thuê.) –

1

Câu trả lời đầu là đúng, nhưng không đề cập đến điều gì đó: Giá trị của băm sẽ khác nhau cho mỗi bộ đệm kích thước được sử dụng. Giá trị sẽ nhất quán trên các băm, vì vậy kích thước bộ đệm giống nhau sẽ tạo ra cùng một giá trị băm, tuy nhiên nếu băm này sẽ được so sánh với băm của cùng một dữ liệu vào một thời điểm sau đó, cùng kích thước bộ đệm phải được sử dụng cho mỗi cuộc gọi. Ngoài ra, nếu bạn muốn đảm bảo mã digest của mình hoạt động chính xác và trực tuyến để so sánh băm với các trang web băm trực tuyến, có vẻ như chúng sử dụng độ dài đệm 1. Điều này cũng mang lại một suy nghĩ thú vị: hoàn toàn chấp nhận được để sử dụng độ dài đệm 1 để băm một tệp lớn, nó sẽ chỉ mất nhiều thời gian hơn (duh). Vì vậy, quy tắc của tôi là nếu nó chỉ để sử dụng nội bộ, sau đó tôi có thể thiết lập độ dài đệm phù hợp cho một tập tin lớn, nhưng nếu nó phải chơi tốt đẹp với các hệ thống khác, sau đó thiết lập chiều dài bộ đệm đến 1 và thỏa thuận với hậu quả thời gian.

int hashTargetFile(FILE* fp, unsigned char** md_value, int *md_len) { 

    #define FILE_BUFFER_LENGTH 1 

    EVP_MD_CTX *mdctx; 
    const EVP_MD *md; 
    int diglen; //digest length 
    int arrlen = sizeof(char)*EVP_MAX_MD_SIZE + 1; 
    int arrlen2 = sizeof(char)*FILE_BUFFER_LENGTH + 1; 
    unsigned char *digest_value = (char*)malloc(arrlen); 
    char *data = (char*)malloc(arrlen2); 
    size_t bytes; //# of bytes read from file 

    mdctx = EVP_MD_CTX_new(); 
    md = EVP_sha512(); 

    if (!mdctx) { 
     fprintf(stderr, "Error while creating digest context.\n"); 
     return 0; 
    } 

    if (!EVP_DigestInit_ex(mdctx, md, NULL)) { 
     fprintf(stderr, "Error while initializing digest context.\n"); 
     return 0; 
    } 

    while (bytes = fread(data, 1, FILE_BUFFER_LENGTH, fp) != 0) { 
     if (!EVP_DigestUpdate(mdctx, data, bytes)) { 
      fprintf(stderr, "Error while digesting file.\n"); 
      return 0; 
     } 
    } 

    if (!EVP_DigestFinal_ex(mdctx, digest_value, &diglen)) { 
     fprintf(stderr, "Error while finalizing digest.\n"); 
     return 0; 
    } 

    *md_value = digest_value; 
    *md_len = diglen; 

    EVP_MD_CTX_free(mdctx); 

    return 1; 
}