C语言实例_获取文件MD5值

一、MD5介绍

MD5(Message Digest Algorithm 5)是一种常用的哈希函数算法。将任意长度的数据作为输入,并生成一个唯一的、固定长度(通常是128位)的哈希值,称为MD5值。MD5算法以其高度可靠性和广泛应用而闻名。

MD5算法主要具备以下特点:

(1)不可逆性:给定MD5值无法通过逆运算得到原始数据。

(2)唯一性:不同的输入数据会生成不同的MD5值。

(3)高效性:对于给定的数据,计算其MD5值是非常快速的。

MD5值的应用场景包括:

(1)数据完整性验证:MD5值可以用于验证文件是否在传输过程中被篡改。发送方计算文件的MD5值并发送给接收方,接收方在接收到文件后重新计算MD5值,然后与发送方的MD5值进行比较,如果一致,则说明文件未被篡改。

(2)密码存储:在许多系统中,用户密码通常不会以明文形式存储,而是将其转换为MD5值后存储。当用户登录时,系统会将用户输入的密码转换为MD5值,然后与存储的MD5值进行比较,以验证密码的正确性。

(3)安全认证:MD5值也可用于数字证书等安全认证中,用于验证文件的完整性和认证信息的真实性。

(4)数据指纹:MD5值可以作为数据的唯一标识符,用于快速比对和查找重复数据。

二、示例代码

2.1 获取数据MD5值(openssl库)

在C语言中获取一段数据的MD5值,可以使用现有的第三方库实现。以下是一个使用 OpenSSL 库计算数据的MD5值的示例代码:

(1)需要安装 OpenSSL 库(如果尚未安装)并包含相关头文件:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>

(2)创建一个子函数来计算数据的MD5值:

cpp 复制代码
void calculate_md5(const unsigned char* data, size_t length, unsigned char* md5_hash) {
    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, data, length);
    MD5_Final(md5_hash, &ctx);
}

该函数接受三个参数:data 为待计算的数据指针,length 为数据长度,md5_hash 为存储MD5值的数组。

下面是一个完整的程序,展示如何调用以上子函数并打印MD5值:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>
​
void calculate_md5(const unsigned char* data, size_t length, unsigned char* md5_hash) {
    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, data, length);
    MD5_Final(md5_hash, &ctx);
}
​
void print_md5(const unsigned char* md5_hash) {
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
        printf("%02x", md5_hash[i]);
    }
    printf("\n");
}
​
int main() {
    const unsigned char data[] = "Hello, World!";
    size_t length = sizeof(data) - 1; // 减去字符串末尾的空字符
    unsigned char md5_hash[MD5_DIGEST_LENGTH];
​
    calculate_md5(data, length, md5_hash);
    printf("MD5: ");
    print_md5(md5_hash);
​
    return 0;
}

这个示例程序将输出一段数据的MD5值。可以将待计算的数据存储在 data 数组中,并根据需要调整数据长度。

这里使用的是 OpenSSL 提供的 MD5 函数。在编译时,需要链接 OpenSSL 库。在 Linux 系统上,可以使用 -lssl -lcrypto 参数进行链接。在 Windows 系统上,需要下载并安装 OpenSSL 库,并配置正确的链接路径和库文件名称。

2.2 获取文件的MD5值(openssl库)

以下是使用 OpenSSL 库计算文件的MD5值的示例代码:

(1)需要安装 OpenSSL 库(如果尚未安装)并包含相关头文件:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>

(2)创建一个子函数来计算文件的MD5值:

cpp 复制代码
void calculate_file_md5(const char* filename, unsigned char* md5_hash) {
    FILE* file = fopen(filename, "rb");
    if (file == NULL) {
        printf("Failed to open file: %s\n", filename);
        return;
    }
​
    MD5_CTX ctx;
    MD5_Init(&ctx);
​
    unsigned char buffer[1024];
    size_t read;
    while ((read = fread(buffer, 1, sizeof(buffer), file)) != 0) {
        MD5_Update(&ctx, buffer, read);
    }
​
    fclose(file);
​
    MD5_Final(md5_hash, &ctx);
}

该函数接受两个参数:filename 为待计算的文件名,md5_hash 为存储MD5值的数组。

下面是一个完整的示例程序,展示如何调用以上子函数并打印文件的MD5值:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>
​
void calculate_file_md5(const char* filename, unsigned char* md5_hash) {
    // ... 函数实现见上文 ...
​
void print_md5(const unsigned char* md5_hash) {
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
        printf("%02x", md5_hash[i]);
    }
    printf("\n");
}
​
int main() {
    const char* filename = "path/to/file";
    unsigned char md5_hash[MD5_DIGEST_LENGTH];
​
    calculate_file_md5(filename, md5_hash);
    printf("MD5: ");
    print_md5(md5_hash);
​
    return 0;
}

这个示例程序将打开指定文件并计算其MD5值。需要将文件路径存储在 filename 字符串中,并根据需要调整该字符串。

请这里使用的是 OpenSSL 提供的 MD5 函数。在编译时,需要链接 OpenSSL 库。在 Linux 系统上,可以使用 -lssl -lcrypto 参数进行链接。在 Windows 系统上,需要下载并安装 OpenSSL 库,并配置正确的链接路径和库文件名称。

2.3 自己写算法获取MD5值

实现MD5算法比较复杂,涉及位操作、逻辑运算、位移等。

以下是一个简化版本的纯C语言MD5算法实现:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
​
typedef unsigned char uint8;
typedef unsigned int uint32;
​
// MD5常量定义
const uint32 MD5_CONSTANTS[] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
​
// 循环左移
#define LEFT_ROTATE(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
​
// 转换为大端字节序
void to_big_endian(uint32 value, uint8* buffer) {
    buffer[0] = (uint8)(value & 0xff);
    buffer[1] = (uint8)((value >> 8) & 0xff);
    buffer[2] = (uint8)((value >> 16) & 0xff);
    buffer[3] = (uint8)((value >> 24) & 0xff);
}
​
// 处理消息块
void process_block(const uint8* block, uint32* state) {
    uint32 a = state[0];
    uint32 b = state[1];
    uint32 c = state[2];
    uint32 d = state[3];
    uint32 m[16];
​
    // 将消息块划分为16个32位字,并进行字节序转换
    for (int i = 0; i < 16; i++) {
        m[i] = (((uint32)block[i * 4 + 0]) << 0) |
               (((uint32)block[i * 4 + 1]) << 8) |
               (((uint32)block[i * 4 + 2]) << 16) |
               (((uint32)block[i * 4 + 3]) << 24);
    }
​
    // MD5循环运算
    for (int i = 0; i < 64; i++) {
        uint32 f, g;
​
        if (i < 16) {
            f = (b & c) | ((~b) & d);
            g = i;
        } else if (i < 32) {
            f = (d & b) | ((~d) & c);
            g = (5 * i + 1) % 16;
        } else if (i < 48) {
            f = b ^ c ^ d;
            g = (3 * i + 5) % 16;
        } else {
            f = c ^ (b | (~d));
            g = (7 * i) % 16;
        }
​
        uint32 temp = d;
        d = c;
        c = b;
        b = b + LEFT_ROTATE((a + f + MD5_CONSTANTS[i] + m[g]), 7);
        a = temp;
    }
​
    // 更新状态
    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
}
​
// 计算MD5值
void calculate_md5(const uint8* message, size_t length, uint8* digest) {
    // 初始化状态
    uint32 state[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
​
    // 填充消息
    size_t padded_length = ((length + 8) / 64 + 1) * 64;
    uint8* padded_message = (uint8*)calloc(padded_length, 1);
    memcpy(padded_message, message, length);
    padded_message[length] = 0x80;  // 添加一个1
    to_big_endian((uint32)(length * 8), padded_message + padded_length - 8);  // 添加长度(以位为单位)
​
    // 处理消息块
    for (size_t i = 0; i < padded_length; i += 64) {
        process_block(padded_message + i, state);
    }
​
    // 生成摘要
    for (int i = 0; i < 4; i++) {
        to_big_endian(state[i], digest + i * 4);
    }
    
    free(padded_message);
}
​
// 打印MD5值
void print_md5(const uint8* digest) {
    for (int i = 0; i < 16; i++) {
        printf("%02x", digest[i]);
    }
    printf("\n");
}
​
int main() {
    const char* message = "Hello, World!";
    size_t length = strlen(message);
    uint8 digest[16];
​
    calculate_md5((const uint8*)message, length, digest);
    printf("MD5: ");
    print_md5(digest);
​
    return 0;
}

这个程序可以计算给定字符串的MD5值。将待计算的数据存储在 message 字符串中,根据需要调整数据长度。

相关推荐
掘金者阿豪2 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
猪猪拆迁队1 小时前
虚拟工厂仿真引擎的架构设计:让一条产线可编程、可观测、可干预
后端·ai编程
字节跳动数据库1 小时前
文章分享——相似函数处理方法
人工智能·后端·程序员
云技纵横2 小时前
@Transactional 失效的 7 种场景:第 5 种最难排查
后端
用户6757049885022 小时前
你知道 Go 结构体和结构体指针调用的区别吗?一文带你彻底搞懂!
后端·go
程序员cxuan2 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
用户6757049885022 小时前
面试官问“装饰器模式”,这样回答薪资多要 3000!
后端
tntxia2 小时前
Geo Scene域名修改引起的一些问题
后端
用户298698530142 小时前
Java 实现 Word 文档加密与权限解除
java·后端
vanuan3 小时前
给你的A2A-Agent加把锁-认证鉴权实战指南
后端