Подтвердить что ты не робот

Как вычислить хэш 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);
4b9b3361

Ответ 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;
}