MD5 哈希计算法详解

工作原理

MD5 是通过 逐步处理文件的每一部分 来计算整个文件的哈希值的,而不是一次性计算整个文件。具体来说,MD5 是按 512 位(64 字节) 的块来处理数据的,文件内容会被分成多个这样的块,然后逐一计算每个数据块的哈希,最后得到整个文件的 MD5 哈希值。

1. 文件分块

当你使用 MD5 计算文件的哈希值时,文件内容会被按固定大小的块(通常是 512 位,也就是 64 字节)分割。每个块会被单独处理,而不是一次性将整个文件载入内存进行计算。这使得 MD5 能够处理非常大的文件,甚至超过内存的大小。

2.逐步计算

每个 512 位(64 字节)大小的数据块会依次传入 MD5 算法进行哈希计算。每个块的计算会影响到最终的 MD5 哈希值,同时当前块的计算结果也会影响到下一个数据块的计算。

3.迭代更新

MD5 会迭代处理每一个数据块,更新中间状态(A, B, C, D),直到所有的数据块都被处理完。每一块的计算结果都将影响下一个块的计算。因此,MD5 的哈希值是通过逐步更新计算的。

4.填充数据

如果文件的大小不是 512 位的整数倍,MD5 会进行“填充”操作,即在数据末尾添加一定数量的“填充位”和一个表示文件长度的 64 位数字,确保数据的总长度是 512 位的倍数。这个填充操作是 MD5 的关键步骤之一,它保证了哈希计算的完整性。

一.填充数据(Padding)

MD5 对输入数据进行填充,使其长度是 512 位的倍数。填充的方式是先加一个 1 位的 1,再加若干个 0 位,直到数据长度差距为 64 位,最后填充一个 64 位的数字来表示原数据的长度。

二.初始化状态

MD5 的计算会从一个初始的 128 位(16 字节)常量开始。这个常量分成四个 32 位(4 字节)的块,通常称为 A, B, C, D,这些值是固定的,具体为:

  • A = 0x67452301
  • B = 0xEFCDAB89
  • C = 0x98BADCFE
  • D = 0x10325476

三. 数据分块(Processing in 512-bit blocks)

输入的数据会被按 512 位(64 字节)分块。如果数据不足 512 位,会进行填充(前面提到的 padding)。每个块会依次处理。处理时,MD5 会利用一个迭代算法对每个块进行变换,更新哈希值。

四.MD5 迭代过程

每个 512 位的数据块都会被处理 64 次。处理过程中使用了四种不同的操作,每种操作使用不同的数学函数(如逻辑与、或、异或和加法),并通过常量(K)来修改当前状态。具体的步骤如下:

  • 每次迭代使用一个 32 位的常数 K 和数据块中的每个 32 位子块。
  • 每次迭代都基于 A, B, C, D 的值更新。ABCD 是当前的哈希状态。
  • 经过 64 次迭代,A, B, C, D 的值被更新并应用于下一个数据块。

五.输出结果

在所有数据块处理完毕后,MD5 算法会输出最终的哈希值,长度是 128 位(16 字节)。这个值通常被转换成 32 位的十六进制字符串表示。

六.具体代码封装参考

#include <openssl/md5.h>
#define BUFFER_SIZE 4096
void calculate_md5(FILE *fp, unsigned char *md5_result) {
    rewind(fp);  // 重新定位文件指针到文件开头
    MD5_CTX context;//初始化一个 MD5 上下文 context,这是计算 MD5 哈希值所需的数据结构。
    MD5_Init(&context);

    unsigned char *data = (unsigned char *)malloc(BUFFER_SIZE);
    if (data == NULL) {
        perror("内存分配失败");
        return;
    }
    int bytes;
    while ((bytes = fread(data, 1, BUFFER_SIZE, fp)) > 0) {
        MD5_Update(&context, data, bytes);// 将读取的数据更新到 MD5 上下文 context 中,以便计算出最终的 MD5 哈希值。
    }

    free(data);  // 释放动态分配的缓冲区
    MD5_Final(md5_result, &context);//在所有数据读取完成后,使用 MD5_Final 函数将最终的 MD5 哈希值写入 md5_result 缓冲区中。
}

 代码中,MD5 计算是通过 MD5_InitMD5_UpdateMD5_Final 三个函数来实现的。让我们来逐步分析这三个函数的工作:

1.初始化 MD5 上下文 (MD5_Init)

MD5_CTX context;
MD5_Init(&context);

 这个函数初始化 MD5 计算所需要的上下文环境,它会设置一个初始的哈希状态(即 A, B, C, D),并准备开始计算。

2.逐步处理数据 (MD5_Update)

MD5_Update(&context, data, bytes);

 在文件读取过程中,每次从文件中读取一定大小的数据(如 BUFFER_SIZE 大小的数据块),都使用 MD5_Update 函数更新 MD5 的计算状态。data 是当前读取的字节数组,bytes 是实际读取的字节数。这个函数会将读取的数据和当前的哈希状态进行计算,不断更新 MD5 上下文。

3. 生成最终哈希值 (MD5_Final)

MD5_Final(md5_result, &context);

 当所有数据块都被处理完后,调用 MD5_Final 来计算最终的 MD5 哈希值,并将结果存储到 md5_result 中。这个哈希值是经过所有数据处理后得出的最终结果,是一个 128 位(16 字节)的哈希值。

七.MD5 算法核心思想

  • 迭代更新:对于每个数据块(512 位),通过 64 次迭代更新 A, B, C, D 四个变量,每次使用不同的数学函数来修改这些变量的值。
  • 输入长度不固定:无论输入数据多长,MD5 都能将其转换成一个固定大小的哈希值。
  • 输出不可逆:MD5 哈希值是不可逆的,即给定一个 MD5 值,无法从中恢复出原始数据。
  • 碰撞:理论上,MD5 是存在碰撞的(即不同的输入可以产生相同的哈希值),但实际上 MD5 仍然被广泛用于数据完整性验证中。

八.总结

MD5 的计算本质上是一个不断更新的过程,利用初始化的常量(A, B, C, D)和每个数据块,经过 64 次迭代更新哈希值,最终得出一个 128 位的哈希结果。整个过程是通过对数据块的处理,不断进行数学运算和常数变换来实现的。

在你的代码中:

  • MD5_Init 初始化上下文。
  • MD5_Update 逐步处理文件中的数据,更新哈希值。
  • MD5_Final 计算并返回最终的 MD5 哈希值。

这种算法确保了数据的唯一性和稳定性,即使是对数据做很小的修改,计算出的 MD5 哈希值也会发生显著变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值