具体MD5原理参考博客:
信息摘要算法之一:MD5算法解析及实现 - Moonan - 博客园 (cnblogs.com)
代码简单易懂,封装成函数。
复制即用 ,使用时调用REdata* Encryption_MD5(BYTE* Data, UINT64 Size)函数即可
#include<windows.h>
//4种基本运算
#define MD5_F(x,y,z) ((x & y) | (~x & z))
#define MD5_G(x,y,z) ((x & z) | (y & ~z))
#define MD5_H(x,y,z) (x^y^z)
#define MD5_I(x,y,z) (y ^ (x | ~z))
//加密或解密数据返回(数据为字节类型)
//Data:数据
//Size:数据数组长度(不计算具体大小)
class REdata
{
public:
BYTE* data;
DWORD size;
~REdata() { if (data)delete[] data; };
};
//部分计算
//a=b+((a+(G(b,c,d)+m+sin)<<< s)
//(当前传入的s为位移数,x为公式中 a+(G(b,c,d)+m+sin )
void MD5_Value(DWORD* a, DWORD b, DWORD x, BYTE s)
{
//((x << s) | (x >> (32 - s))):4字节数据x 向左循环位移s位(从左侧移出去的位从右侧重新导入)
*a = b + ((x << s) | (x >> (32 - s)));//注意括号位置以及运算优先级 例如:a+b|c其实是(a+b)|c的意思(因为没注意改了3小时代码,哭)
}
//处理一个512分组
//a,b,c,d每次传入的4个链接变量
//m[16]:当前512位(即64字节,16个4字节)的数据分组
//sin[64]:64个经过正弦计算的4字节数据集合
void MD5_OPRATE(DWORD* a, DWORD* b, DWORD* c, DWORD* d, DWORD m[16], DWORD sin[64])
{
//记录abcd的初始值
DWORD A = *a; DWORD B = *b; DWORD C = *c; DWORD D = *d;
//4294967296*abs(sin(j))的整数部分计算值数组索引
DWORD count = 0;
//4字节数据索引
BYTE ptr = 0;
//请自行查阅观察 每次4字节数据索引规律,每次sin值索引规律以及每次位移位数规定
//16轮ff运算
for (int i = 0; i < 4; i++)
{
MD5_Value(a, *b, *a + MD5_F(*b, *c, *d) + m[ptr++] + sin[count++], 7);
MD5_Value(d, *a, *d + MD5_F(*a, *b, *c) + m[ptr++] + sin[count++], 12);
MD5_Value(c, *d, *c + MD5_F(*d, *a, *b) + m[ptr++] + sin[count++], 17);
MD5_Value(b, *c, *b + MD5_F(*c, *d, *a) + m[ptr++] + sin[count++], 22);
}
//16轮GG运算
ptr = 12;
for (int i = 0; i < 4; i++)
{
ptr = (ptr + 5) % 16;
MD5_Value(a, *b, *a + MD5_G(*b, *c, *d) + m[ptr] + sin[count++], 5);
ptr = (ptr + 5) % 16;
MD5_Value(d, *a, *d + MD5_G(*a, *b, *c) + m[ptr] + sin[count++], 9);
ptr = (ptr + 5) % 16;
MD5_Value(c, *d, *c + MD5_G(*d, *a, *b) + m[ptr] + sin[count++], 14);
ptr = (ptr + 5) % 16;
MD5_Value(b, *c, *b + MD5_G(*c, *d, *a) + m[ptr] + sin[count++], 20);
}
//16轮HH运算
ptr = 2;
for (int i = 0; i < 4; i++)
{
ptr = (ptr + 3) % 16;
MD5_Value(a, *b, *a + MD5_H(*b, *c, *d) + m[ptr] + sin[count++], 4);
ptr = (ptr + 3) % 16;
MD5_Value(d, *a, *d + MD5_H(*a, *b, *c) + m[ptr] + sin[count++], 11);
ptr = (ptr + 3) % 16;
MD5_Value(c, *d, *c + MD5_H(*d, *a, *b) + m[ptr] + sin[count++], 16);
ptr = (ptr + 3) % 16;
MD5_Value(b, *c, *b + MD5_H(*c, *d, *a) + m[ptr] + sin[count++], 23);
}
//16论II计算
ptr = 9;
for (int i = 0; i < 4; i++)
{
ptr = (ptr + 7) % 16;
MD5_Value(a, *b, *a + MD5_I(*b, *c, *d) + m[ptr] + sin[count++], 6);
ptr = (ptr + 7) % 16;
MD5_Value(d, *a, *d + MD5_I(*a, *b, *c) + m[ptr] + sin[count++], 10);
ptr = (ptr + 7) % 16;
MD5_Value(c, *d, *c + MD5_I(*d, *a, *b) + m[ptr] + sin[count++], 15);
ptr = (ptr + 7) % 16;
MD5_Value(b, *c, *b + MD5_I(*c, *d, *a) + m[ptr] + sin[count++], 21);
}
//将abcd数据加上原始值
*a += A; *b += B; *c += C; *d += D;
}
//BYTE:1字节长度无符号整型
//DWORD:4字节长度无符号整型
//UINT64:8字节长度无符号整型
//MD5哈希值计算(信息长度需要小于2^64)
//Data:需要计算hash的数据
//Size:数据数组长度(不计算具体大小)
REdata* Encryption_MD5(BYTE* Data, UINT64 Size)
{
//数据长度取模64(512位)是否大于56(448位)?是则将内存补齐64的整数倍在补一个64字节。不是则只补充为64整数倍
//原因:在保证数据为64字节的整数倍的同时需要最后8字节储存原来数据大小。如果取模大于56,在填入长度时会覆盖掉一部分数据,因此还需要申请64字节(末尾8字节储存长度)
UINT64 Resize = Size % 64 > 56 ? (Size / 64 + 2) * 64 : (Size / 64 + 1) * 64;//长度补齐
//创建数据数组并将其初始化为0
BYTE* Target = new BYTE[Resize]();
//数据拷贝
memcpy(Target, Data, Size); //复制数据
//补位(按规则如果长度取模不等于56则需要添加1以及若干个零(单位比特),由于所有数据以及初始化为0,故只需要填充一个1即可,0x80(16进制)的二进制是10000000满足要求)
if (Size % 64 != 56)
Target[Size] = 0x80;
//计算原本数据长度(单位比特)
UINT64 BitSize = Size * 8;
//填充数据长度
memcpy(Target + Resize - 8, &BitSize, 8);
//标准的幻数初始值
DWORD A = 0x67452301; DWORD B = 0xefcdab89; DWORD C = 0x98badcfe; DWORD D = 0x10325476;
//分组,64字节一组(512位)
UINT64 GroupSize = Resize / 64;//计算一共要处理多少组
//64个弧度计算值 4294967296*abs(sin(j))的整数部分(由于经过了正弦变换,所以取名Sin)
DWORD Sin[64] =
{
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
};
//数据指针(指向当前用于计算的数据位置)
BYTE* Ptr = Target;
for (UINT64 i = 0; i < GroupSize; i++)
{
//将每64字节数据分为16组(4字节一组)
DWORD Array[16];
//数据拷贝并移动数据指针
memcpy(Array, Ptr, 64); Ptr += 64;
//计算当前分组得出abcd值(采用地址传递单纯是因为不想用引用传递)
MD5_OPRATE(&A, &B, &C, &D, Array, Sin);
}
//删除临时数据
delete[] Target;
//创建返回数据对象
REdata* rstruct = new REdata;
rstruct->data = new BYTE[16];
rstruct->size = 16;
//拷贝abcd的值作为最终结构装入16字节数组
memcpy(rstruct->data, &A, 4);
memcpy(rstruct->data + 4, &B, 4);
memcpy(rstruct->data + 8, &C, 4);
memcpy(rstruct->data + 12, &D, 4);
return rstruct;
}