https://blog.csdn.net/hla199106/article/details/45129963
https://blog.csdn.net/White3zz/article/details/103130609
#define _CRT_SECURE_NO_WARNINGS
#ifndef MD5_H
#define MD5_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned char uint8_t;
typedef unsigned uint32_t;
typedef unsigned long long uint64_t;
// MD5压缩函数4轮循环中使用的生成函数,每轮不同
#define F(b, c, d) (((b) & (c)) | ((~b) & (d)))
#define G(b, c, d) (((b) & (d)) | ((c) & (~d)))
#define H(b, c, d) ((b) ^ (c) ^ (d))
#define I(b, c, d) ((c) ^ ((b) | (~d)))
// 循环左移
#define LEFTROTATE(num, n) (((num) << n) | ((num >> (32 - n))))
// 64次迭代运算采用的左循环移位的s值
const uint32_t S[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
// T表,32位字,一共有64个元素,对应64次迭代,也成为加法常数
uint32_t T[64];
uint32_t A, B, C, D;
static int Initialized = 0;
void MD5Init()
{
A = 0x67452301;
B = 0xefcdab89;
C = 0x98badcfe;
D = 0x10325476;
if (Initialized == 0) {
Initialized = 1;
uint64_t n = (uint64_t)1 << 32;
for (int i = 1; i < 65; i++)
T[i - 1] = fabs(sin(i) * n); //4294967296UL
}
}
// 大端形式转换
void int2byte1(uint32_t val, uint8_t *bytes)
{
bytes[0] = (uint8_t)(val >> 24);
bytes[1] = (uint8_t)(val >> 16);
bytes[2] = (uint8_t)(val >> 8);
bytes[3] = (uint8_t)val;
}
// 两个工具函数 小端形式转换
void int2byte(uint32_t val, uint8_t *bytes)
{
bytes[0] = (uint8_t)val;
bytes[1] = (uint8_t)(val >> 8);
bytes[2] = (uint8_t)(val >> 16);
bytes[3] = (uint8_t)(val >> 24);
}
uint32_t byte2int(const uint8_t *bytes)
{
return (uint32_t)bytes[0]
| ((uint32_t)bytes[1] << 8)
| ((uint32_t)bytes[2] << 16)
| ((uint32_t)bytes[3] << 24);
}
// MD5主函数
int StrMD5(const char* str, uint32_t len, char *result)
{
uint8_t block[64];
uint8_t* temp = NULL;
uint32_t X[16];
MD5Init();
uint32_t new_len = len + (((len & 0x3F) >= 56) ? (56 + 64 - (len & 0x3F)) : (56 - (len & 0x3F)));
// 还要增加64bit
if (!(temp = (uint8_t*)malloc(len + new_len + 8))) return -1;
memcpy(temp, str, len);
// 填充1000...0
temp[len] = 0x80;
for (uint32_t offset = len + 1; offset < new_len; offset++)
temp[offset] = 0;
// 在末尾再附加总长度count的低64位,由于这里的count单位是byte,所以要乘以8
int2byte(len * 8, temp + new_len);
int2byte(len >> 29, temp + new_len + 4); //参考了其他代码,count>>29相当于count*8>>32,但可以避免值溢出
for (uint32_t offset = 0; offset < new_len; offset += 64) {
memcpy(block, temp + offset, 64);
// 保存512位的每32位分组,在X[k]时会用到
for (int i = 0; i < 16; i++) {
X[i] = byte2int(block + i * 4);
}
uint32_t a, b, c, d, temp, g, k;
a = A, b = B, c = C, d = D;
// 主循环,共四轮,每轮16次迭代,共64次迭代
for (int i = 0; i < 64; i++) {
if (i < 16) {
g = F(b, c, d);
k = i;
} else if (i < 32) {
g = G(b, c, d);
k = (1 + 5 * i) % 16;
} else if (i < 48) {
g = H(b, c, d);
k = (5 + 3 * i) % 16;
} else {
g = I(b, c, d);
k = (7 * i) % 16;
}
temp = d, d = c, c = b, b = b + LEFTROTATE((a + g + X[k] + T[i]), S[i]), a = temp;
}
A += a, B += b, C += c, D += d;
}
free(temp);
uint32_t AA, BB, CC, DD;
int2byte1(A, (uint8_t*)&AA);
int2byte1(B, (uint8_t*)&BB);
int2byte1(C, (uint8_t*)&CC);
int2byte1(D, (uint8_t*)&DD);
sprintf( result, "%08X%08X%08X%08X", AA, BB, CC, DD );
return 0;
}
int FileMD5(const char* filepath, char *result)
{
FILE *fp = NULL;
uint8_t buffer[4096], block[64];
uint8_t* tempstr = NULL;
size_t count = 0; // count用于记录总长度,补位的时候需要用到
uint32_t X[16];
int flag = 0;
if ((fp = fopen(filepath, "rb+")) == NULL) {
printf("[ERROR] File in %s not found.", filepath);
return -1;
}
MD5Init();
while (!feof(fp)) {
memset(buffer, 0, sizeof(buffer));
// fread函数返回读取的次数,设定每次读取一个字符,就可以知道字符长度了
uint32_t len = fread(buffer, 1, 4096, fp);
// 更新文件总长度
count += len;
// 当读取文件到末尾时,意味着需要进行补位操作了,此时读到的len可能不足512bit,也可能刚好等于512bit
if (feof(fp)) {
flag = 1;
// 因为恰好等于448bit不行,所以new_len直接等于len+1
uint32_t new_len;
for (new_len = len + 1; new_len % 64 != 56; new_len++);
// 还要增加64bit
tempstr = (uint8_t*)malloc(new_len + 8);
memcpy(tempstr, buffer, len);
// 填充1000...0
tempstr[len] = 0x80;
for (int offset = len + 1; offset < new_len; offset++)
tempstr[offset] = 0;
// 在末尾再附加总长度count的低64位,由于这里的count单位是byte,所以要乘以8
int2byte(count * 8, tempstr + new_len);
int2byte(count >> 29, tempstr + new_len + 4); //参考了其他代码,count>>29相当于count*8>>32,但可以避免值溢出
len = new_len;
}
// 虽然每次只读取512bit,但是还是采用这样的方式,可以防止最后的一次由于补位导致可能出现的 len > 512bit 的情况(此时就要分两次了)
for (int offset = 0; offset < len; offset += 64) {
// 读到结尾时,我们把补位后的数据存在了temp中,为了处理的统一,将temp中的数据保存到buffer上
if (flag == 1) {
memcpy(block, tempstr + offset, 64);
} else {
memcpy(block, buffer + offset, 64);
}
// 保存512位的每32位分组,在X[k]时会用到
for (int i = 0; i < 16; i++) {
X[i] = byte2int(block + i * 4);
}
uint32_t a, b, c, d, temp, g, k;
a = A, b = B, c = C, d = D;
// 主循环,共四轮,每轮16次迭代,共64次迭代
for (int i = 0; i < 64; i++) {
if (i < 16) {
g = F(b, c, d);
k = i;
} else if (i < 32) {
g = G(b, c, d);
k = (1 + 5 * i) % 16;
} else if (i < 48) {
g = H(b, c, d);
k = (5 + 3 * i) % 16;
} else {
g = I(b, c, d);
k = (7 * i) % 16;
}
temp = d, d = c, c = b, b = b + LEFTROTATE((a + g + X[k] + T[i]), S[i]), a = temp;
}
A += a, B += b, C += c, D += d;
}
}
fclose(fp);
free(tempstr);
uint32_t AA, BB, CC, DD;
int2byte1(A, (uint8_t*)&AA);
int2byte1(B, (uint8_t*)&BB);
int2byte1(C, (uint8_t*)&CC);
int2byte1(D, (uint8_t*)&DD);
sprintf( result, "%08X%08X%08X%08X", AA, BB, CC, DD );
return 0;
}
#ifdef __cplusplus
}
#endif
#endif // !MD5_H
//#include "md5.h"
//#include <conio.h>
//
//int main()
//{
// char result[33];
//
// const char* str = "你要测试的字符串你要测试的字符串你要测试的字符串你要测试的字符串";
// if (StrMD5(str, strlen(str), result) == 0) {
// printf("\n[INFO] The result is: %s\n", result);
// }
//
// const char* filepath = "C:\\Users\\wzdyq\\Downloads\\RunningCheeseChrome.7z";
// if (FileMD5(filepath, result) == 0) {
// printf("\n[INFO] The result is: %s\n", result);
// }
//
// printf("\nPress any key to exit...");
// _getch();
//
// return 0;
//}