【问题标题】:How to calculate the MD5 hash of a large file in C?如何在 C 中计算大文件的 MD5 哈希?
【发布时间】:2012-05-06 15:58:44
【问题描述】:

我正在使用 OpenSSL 库编写 C 语言。

如何使用 md5 计算大文件的哈希?

据我所知,我需要将整个文件作为 char 数组加载到 RAM 中,然后调用哈希函数。但是如果文件大约 4Gb 长呢?听起来是个坏主意。

已解决:感谢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);

【问题讨论】:

  • 我很想知道需要它吗?如果它不是线程安全的,那肯定是个坏主意,因为它可能会长时间阻塞程序并且 P.O.用户
  • 加密文件与使用 MD5 之类的哈希函数对其进行哈希处理不同。你真的是说哈希,还是要加密文件?
  • MD5 是基于流的。您不需要一次将整个 4GB 加载到内存中 - 您可以分块读取它。
  • 对不起,我的意思当然是散列文件,而不是加密。

标签: c hash md5


【解决方案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

【讨论】:

  • 可能是无符号字符数据[1024];而 ((bytes = fread (data, 1, 1024, inFile)) != 0) 是无符号字符数据[4096]; while ((bytes = fread (data, 1, 4096, inFile)) != 0) ?
【解决方案2】:

首先,MD5 是一种散列算法。它不加密任何东西。

无论如何,您可以以任意大小的块读取文件。调用MD5_Init 一次,然后使用从文件中读取的每个数据块调用MD5_Update。完成后,致电MD5_Final 获取结果。

【讨论】:

  • 我试过了。我已将文件拆分为 16 字节块并将它们传递给 MD5_Update,但哈希值错误。
  • 所以修复代码中导致哈希错误的错误。 (此外,16 字节块会很慢。至少使用 64KB 块会好很多。)
【解决方案3】:

您不必一次将整个文件加载到内存中。您可以使用函数MD5_Init(), MD5_Update() and 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;
    }
    

    【讨论】:

    • 我使用各种文件缓冲区大小(40k、4k、23 和 1)运行 askovpen 的代码,每次都得到相同的哈希值。有人知道更多吗?
    • 对于迟到的任何人 - 这是因为 fread 只会读取到文件的末尾,而 bytes 的值是读取的字节数。因此,如果您的 FILE_BUFFER_LENGTH 为 512,并且文件在第一次迭代字节 = 512 和第二次迭代字节 = 1 时为 513 字节长,只要始终使用它,缓冲区大小无关紧要。
    • 如果您正在读取任意长度的文件(例如,您想要获取前 N 个字节的哈希值(如果您正在检查文件 1 是否是文件 2) 你需要使用 1 的缓冲区大小 或 使用 2 个 while 循环。第一个读取缓冲区长度块中的数据(同时命中 bytes_read+buffer_length
    猜你喜欢
    • 2015-12-18
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    • 2021-06-19
    • 2010-10-20
    • 2017-01-27
    相关资源
    最近更新 更多