Как вычислить хэш MD5 большого файла в C?
Я пишу в C, используя библиотеку OpenSSL.
Как рассчитать хэш большого файла с помощью md5?
Как я знаю, мне нужно загрузить весь файл в ОЗУ как массив char, а затем вызвать хеш-функцию. Но что, если файл имеет длину около 4 ГБ? Похоже на плохую идею.
SOLVED. Благодаря askovpen я нашел свою ошибку. Я использовал
while ((bytes = fread (data, 1, 1024, inFile)) != 0)
MD5_Update (&mdContext, data, 1024);
не
while ((bytes = fread (data, 1, 1024, inFile)) != 0)
MD5_Update (&mdContext, data, bytes);
Ответы
Ответ 1
Пример
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;
}
результат:
$ md5sum file.c
25a904b0e512ee546b3f47574703d9fc file.c
$ ./file
25a904b0e512ee546b3f47574703d9fc file.c
Ответ 2
Во-первых, MD5 является алгоритмом хэширования. Он ничего не шифрует.
В любом случае, вы можете прочитать файл в кусках любого размера, который вам нравится. Вызовите MD5_Init один раз, затем вызовите MD5_Update каждый фрагмент данных, которые вы читаете из файла. Когда вы закончите, вызовите MD5_Final, чтобы получить результат.
Ответ 3
Вам не нужно сразу загружать весь файл в память. Вы можете использовать функции MD5_Init(), MD5_Update() и MD5_Final() для обработки его в кусках для создания хэша. Если вы беспокоитесь о том, чтобы сделать это "атомной" операцией, может потребоваться заблокировать файл, чтобы кто-то другой не изменял его во время операции.
Ответ 4
Верхний ответ правильный, но ничего не сказал: значение хэша будет отличаться для каждого используемого размера буфера. Значение будет согласовано между хэшами, поэтому один и тот же размер буфера будет выдавать один и тот же хэш каждый раз, однако, если этот хеш будет сравниваться с хэшем из тех же данных в более позднее время, для каждого вызова должен использоваться один и тот же размер буфера.
Кроме того, если вы хотите, чтобы ваш код дайджеста функционировал правильно, и выходите онлайн, чтобы сравнить ваш хэш с онлайн-сайтами хэширования, кажется, что они используют длину буфера 1. Это также вызывает интересную мысль: вполне приемлемо использовать длину буфера 1 для хэширования большого файла, это займет больше времени (duh).
Итак, мое эмпирическое правило, если оно используется только для внутреннего использования, тогда я могу установить длину буфера соответственно для большого файла, но если он должен хорошо играть с другими системами, тогда установите длину буфера в 1 и обработайте временное следствие.
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;
}