md5的输入可以为任意长度,加密后的输出为128位。
第一步,追加填充位
把输入的数据填充,使其输入的数据长度位数(bit)长度除512的余数为448。填充是必须的,即使本来的位长除512的余为448,也要再填充512位,也就是说最少填充1位,最多512位。因此,最后信息的长度为N*512+448,N>=0。填充后的最短信息为448位。
填充的方法是,在原来信息的后面填充一个1和好几个0,直到满足条件。
然后再把上面的结果后面填充一个64位数表示填充前数据的长度,单位是bit。经果这一步,最后得到数据的长度应该是512的整数倍。
第二步:初始化计算用的常量
初始的128位值为初试链接变量,用四个32位常数表示,用来计算信息摘要,表示为:
word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
以低位字节在前,所以在程序中定义为:
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
第三步,定义辅助函数
定义四个辅助函数,每个辅助函数作为输入三个32位数字,并产生一个32位数字。F( X ,Y ,Z ) = ( X & Y ) | ( (~X) & Z )
G( X ,Y ,Z ) = ( X & Z ) | ( Y & (~Z) )
H( X ,Y ,Z ) =X ^ Y ^ Z
I( X ,Y ,Z ) =Y ^ ( X | (~Z) )
在程序中定义为:
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
第四步,处理分组数据
把第一步得到的消息数据按512位分组,然后循环处理每组的数据。
完整代码:
#include <iostream>
#include <string>
using namespace std;
//2.
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
//3.
#define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的时候,高位一定要补零,而不是补充符号位
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
//常量ti uint32_t(abs(sin(i+1))*(2pow32))
const uint32_t k[] = {
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 };
//向左位移数
const uint32_t s[] = { 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 };
//一组信息(512位)转化为uint32_t的个数
constexpr int step = 512 / sizeof(uint32_t) / 8;
//strBaye的长度
uint32_t strlength;
//A,B,C,D的临时变量
uint32_t atemp;
uint32_t btemp;
uint32_t ctemp;
uint32_t dtemp;
//1.
/*
*填充函数
*处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
*填充方式为先加一个1,其它位补零
*最后加上64位的原来长度
*/
uint32_t* fill(string str)
{
//最终的数据分组数,以512位(64字节)为一组
uint32_t groupCount = ((str.length() + 8) / 64) + 1;
//最终信息的长度,以uint32_t个数来表示
strlength = groupCount * step;
//以uint32_t数组来储存分组后的信息,uint32_t为4字节,64/4=16,所以一组有16个整数
uint32_t *strByte = new uint32_t[strlength];
//初始化信息为零
for (uint32_t i = 0; i < strlength; i++)
strByte[i] = 0;
//将原始数据以一个uint32_t的大小(4字节)进行分组,
//i>>2表示i/4,所以循环四次确定一个uint32_t
//也就是str中的字节数移动4,strByte中的uint32_t确定一个
for (uint32_t i = 0; i <str.length(); i++)
{
strByte[i >> 2] |= (str[i]) << ((i % 4) * 8);
}
//尾部添加1 一个uint32_t保存4个字符信息,所以用128左移
strByte[str.length() >> 2] |= 0x80 << (((str.length() % 4)) * 8);
//添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个
size_t length = str.length() * 8;
memmove_s(&strByte[strlength - 2], sizeof(size_t), &length, sizeof(size_t));
return strByte;
}
//4.处理每个分组数据,每组循环计算64次,最后更新A,B,C,D临时变量的值
void mainLoop(uint32_t M[])
{
//f保存每轮辅助函数的运算结果,g为0到15之间的数
uint32_t f, g;
uint32_t a = atemp;
uint32_t b = btemp;
uint32_t c = ctemp;
uint32_t d = dtemp;
for (uint32_t i = 0; i < 64; i++)
{
if (i<16) {
f = F(b, c, d);
g = i;
}
else if (i<32)
{
f = G(b, c, d);
g = (5 * i + 1) % 16;
}
else if (i<48) {
f = H(b, c, d);
g = (3 * i + 5) % 16;
}
else {
f = I(b, c, d);
g = (7 * i) % 16;
}
uint32_t tmp = d;
d = c;
c = b;
b = b + shift((a + f + k[i] + M[g]), s[i]);
a = tmp;
}
atemp = a + atemp;
btemp = b + btemp;
ctemp = c + ctemp;
dtemp = d + dtemp;
}
//5.将最后128位结果转化位HEX字符串
string changeHex(int a)
{
const char str16[] = "0123456789abcdef";
int b;
string str1;
string str = "";
for (int i = 0; i<4; i++)
{
str1 = "";
b = ((a >> i * 8) % (1 << 8)) & 0xff; //逆序处理每个字节
for (int j = 0; j < 2; j++)
{
str1.insert(0, 1, str16[b % 16]);
b = b / 16;
}
str += str1;
}
return str;
}
string getMD5(string source)
{
atemp = A; //初始化
btemp = B;
ctemp = C;
dtemp = D;
uint32_t *strByte = fill(source);
//循环的次数为,信息的长度按512位分组的个数次
for (uint32_t i = 0; i<strlength / step; i++)
{
//填充一组数据
uint32_t num[step];
for (uint32_t j = 0; j<step; j++)
num[j] = strByte[i * step + j];
mainLoop(num);
}
return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));
}
uint32_t main()
{
string ss;
string s = getMD5("abc");
cout << s << endl; //900150983cd24fb0d6963f7d28e17f72
return 0;
}