#include<stdio.h>
#include<string.h>
#include<process.h>
//每次操作中用到的四个非线性函数(每轮一个)
#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)))
//x向左循环移y位
#define RL(x,y) (((x)<<(y))|((x)>>(32-(y))))
//将x高低位互换
#define PP(x) (x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)
//每一轮16步操作中的4次操作,16步操作按照一定次序顺序进行
//abcd为四个变量,每一次更新变量a,abcd都是32bit
//FGHI为四个非线性函数
//x为M[j],即该512bit的数据块中的第j个32bit的子分组,0<=j<=15
//s为循环左移的位数
//ac为一个给定的常数,32bit
#define FF(a,b,c,d,x,s,ac) a=b+(RL((a+F(b,c,d)+x+ac),s))
#define GG(a,b,c,d,x,s,ac) a=b+(RL((a+G(b,c,d)+x+ac),s))
#define HH(a,b,c,d,x,s,ac) a=b+(RL((a+H(b,c,d)+x+ac),s))
#define II(a,b,c,d,x,s,ac) a=b+(RL((a+I(b,c,d)+x+ac),s))
//len为文件长度
//flen[2]为64位二进制表示的文件初始长度
//x[16]为一个512bit的数据块中的子分组
unsigned A,B,C,D,a,b,c,d,i,len,flen[2],x[16];
//文件名
char filename[200];
//文件指针
FILE *fp;
//MD5核心算法,共64步
void md5()
{
a=A,b=B,c=C,d=D;
//第1轮,16步,更新顺序为adcd,第1轮过后,abcd都更新了4次
FF(a,b,c,d,x[0],7,0xd76aa478);
FF(d,a,b,c,x[1],12,0xe8c7b756);
FF(c,d,a,b,x[2],17,0x242070db);
FF(b,c,d,a,x[3],22,0xc1bdceee);
FF(a,b,c,d,x[4],7,0xf57c0faf);
FF(d,a,b,c,x[5],12,0x4787c62a);
FF(c,d,a,b,x[6],17,0xa8304613);
FF(b,c,d,a,x[7],22,0xfd469501);
FF(a,b,c,d,x[8],7,0x698098d8);
FF(d,a,b,c,x[9],12,0x8b44f7af);
FF(c,d,a,b,x[10],17,0xffff5bb1);
FF(b,c,d,a,x[11],22,0x895cd7be);
FF(a,b,c,d,x[12],7,0x6b901122);
FF(d,a,b,c,x[13],12,0xfd987193);
FF(c,d,a,b,x[14],17,0xa679438e);
FF(b,c,d,a,x[15],22,0x49b40821);
//第2轮,16步,更新顺序为adcd,第2轮过后,abcd都更新了8次
GG(a,b,c,d,x[1],5,0xf61e2562);
GG(d,a,b,c,x[6],9,0xc040b340);
GG(c,d,a,b,x[11],14,0x265e5a51);
GG(b,c,d,a,x[0],20,0xe9b6c7aa);
GG(a,b,c,d,x[5],5,0xd62f105d);
GG(d,a,b,c,x[10],9,0x02441453);
GG(c,d,a,b,x[15],14,0xd8a1e681);
GG(b,c,d,a,x[4],20,0xe7d3fbc8);
GG(a,b,c,d,x[9],5,0x21e1cde6);
GG(d,a,b,c,x[14],9,0xc33707d6);
GG(c,d,a,b,x[3],14,0xf4d50d87);
GG(b,c,d,a,x[8],20,0x455a14ed);
GG(a,b,c,d,x[13],5,0xa9e3e905);
GG(d,a,b,c,x[2],9,0xfcefa3f8);
GG(c,d,a,b,x[7],14,0x676f02d9);
GG(b,c,d,a,x[12],20,0x8d2a4c8a);
//第3轮,16步,更新顺序为adcd,第3轮过后,abcd都更新了12次
HH(a,b,c,d,x[5],4,0xfffa3942);
HH(d,a,b,c,x[8],11,0x8771f681);
HH(c,d,a,b,x[11],16,0x6d9d6122);
HH(b,c,d,a,x[14],23,0xfde5380c);
HH(a,b,c,d,x[1],4,0xa4beea44);
HH(d,a,b,c,x[4],11,0x4bdecfa9);
HH(c,d,a,b,x[7],16,0xf6bb4b60);
HH(b,c,d,a,x[10],23,0xbebfbc70);
HH(a,b,c,d,x[13],4,0x289b7ec6);
HH(d,a,b,c,x[0],11,0xeaa127fa);
HH(c,d,a,b,x[3],16,0xd4ef3085);
HH(b,c,d,a,x[6],23,0x04881d05);
HH(a,b,c,d,x[9],4,0xd9d4d039);
HH(d,a,b,c,x[12],11,0xe6db99e5);
HH(c,d,a,b,x[15],16,0x1fa27cf8);
HH(b,c,d,a,x[2],23,0xc4ac5665);
//第4轮,16步,更新顺序为adcd,第4轮过后,abcd都更新了16次
II(a,b,c,d,x[0],6,0xf4292244);
II(d,a,b,c,x[7],10,0x432aff97);
II(c,d,a,b,x[14],15,0xab9423a7);
II(b,c,d,a,x[5],21,0xfc93a039);
II(a,b,c,d,x[12],6,0x655b59c3);
II(d,a,b,c,x[3],10,0x8f0ccc92);
II(c,d,a,b,x[10],15,0xffeff47d);
II(b,c,d,a,x[1],21,0x85845dd1);
II(a,b,c,d,x[8],6,0x6fa87e4f);
II(d,a,b,c,x[15],10,0xfe2ce6e0);
II(c,d,a,b,x[6],15,0xa3014314);
II(b,c,d,a,x[13],21,0x4e0811a1);
II(a,b,c,d,x[4],6,0xf7537e82);
II(d,a,b,c,x[11],10,0xbd3af235);
II(c,d,a,b,x[2],15,0x2ad7d2bb);
II(b,c,d,a,x[9],21,0xeb86d391);
//4轮过后,将初始值ABCD分别加上abcd,所得的即结果,然后用下一分组数据继续运行算法
A=A+a;
B=B+b;
C=C+c;
D=D+d;
}
//主函数
void main()
{
//初始化链接变量
A=0x67452301;
B=0xefcdab89;
C=0x98badcfe;
D=0x10325476;
printf("输入文件名:");
gets(filename);//取得文件名
if (!(fp=fopen(filename,"rb")))
{
printf("文件无法打开!\n");
exit(0);
}//以二进制打开文件
fseek(fp,0,SEEK_END);//文件指针转到文件末尾
len=ftell(fp);//ftell函数返回long,取得文件的总长度
rewind(fp);//使位置指针重新返回文件开头
flen[1]=len/0x20000000;//flen单位是bit
flen[0]=(len%0x20000000)*8;
for(i=0;i<len/64;i++)//循环运算直至文件结束,若不是64字节的整数倍,则完成这个for循环后,在剩余的字节后面补
{
memset(x,0,64); //将x所指向的某一块内存中的每个字节的内容全部设置为0
fread(&x,4,16,fp);//从指定文件fp中,以4字节为一组,读取16组数据,即读取64个字节,512bit
md5();//MD5运算
}
memset(x,0,64);//最后位不满64字节的内容分配空间
fread(&x,4,16,fp);
//文件结束补充字节,第1位为1,后面全为0
//从文件长度除以64字节的余数位开始
//128二进制即10000000
//再在这个结果后面附加一个以64位二进制表示的填充信息长度
((char*)x)[len%64]=128;
//若信息字节大于55个字节,则无需补1和0,直接填补一个以64位二进制表示的填充信息长度
if(len%64>55)
{
md5();
memset(x,0,64);
}
//文件末尾填补一个以64位二进制表示的填充信息长度
memcpy(x+14,flen,8);
md5();
fclose(fp);//关闭文件
printf("MD5密码:%08x%08x%08x%08x\n",PP(A),PP(B),PP(C),PP(D)); //高低位逆反输出
}