一、哈希函数简介
信息安全的核心技术是应用密码技术。密码技术的应用远不止局限于提供机密性服务,密码技术也提供数据完整性服务。密码学上的散列函数(Hash Functions)就是能提供数据完整性保障的一个重要工具。Hash函数常用来构造数据的短“指纹”,消息的发送者使用所有的消息产生一个短“指纹”,并将该短“指纹”与消息一起传输给接收者。即使数据存储在不安全的地方,接收者重新计算数据的指纹,并验证指纹是否改变,就能够检测数据的完整性。这是因为一旦数据在中途被破坏或改变,短指纹就不再正确。
散列函数是一个函数,它以一个变长的报文作为输入,并产生一个定长的散列码,有时也称为报文摘要,作为函数的输出。散列函数最主要的作用是用于鉴别,鉴别在网络安全中起到举足轻重的地位。鉴别的目的有以下两个:第一,验证信息的发送者不是冒充的,同时发信息者也不能抵赖,此为信源识别;第二,验证信息完整性,在传递或存储过程中未被篡改,重放或延迟等。
二、MD5哈希算法流程
对于任意长度的明文,MD5首先对其进行分组,使得每一组的长度为512位,然后对这些明文分组反复重复处理。
对于每个明文分组的摘要生成过程如下:
(1)将512位的明文分组划分为16个子明文分组,每个子明文分组为32位。
(2)申请4个32位的链接变量,记为A、B、C、D。
(3)子明文分组与链接变量进行第1轮运算。
(4)子明文分组与链接变量进行第2轮运算。
(5)子明文分组与链接变量进行第3轮运算。
(6)子明文分组与链接变量进行第4轮运算。
(7)链接变量与初始链接变量进行求和运算。
(8)链接变量作为下一个明文分组的输入重复进行以上操作。
(9)最后,4个链接变量里面的数据就是MD5摘要。
三、MD5分组过程
对于任意长度的明文,MD5可以产生128位的摘要。任意长度的明文首先需要添加位数,使明文总长度为448(mod512)位。在明文后添加位的方法是第一个添加位是l,其余都是0。然后将真正明文的长度(没有添加位以前的明文长度)以64位表示,附加于前面已添加过位的明文后,此时的明文长度正好是512位的倍数。
当明文长度大于2的64次方时,仅仅使用低64位比特填充,附加到最后一个分组的末尾。
经过添加处理的明文,其长度正好为512位(64个字节)的整数倍,然后按512位的长度进行分组(block),可以划分成L份明文分组,我们用Y0,Y1,……,YL-1表示这些明文分组。对于每一个明文分组,都要重复反复的处理,如图1-1所示。
图1-1 MD5的分组处理方法
四、MD5子明文分组和链接变量
对于512位的明文分组,MD5将其再分成16份子明文分组(sub-block),每份子明文分组为32位,我们使用M[k](k= 0, 1,……15)来表示这16份子明文分组。这里的概念要弄清楚,一个添加位后的明文可以划分为L份明文分组,而一个明文分组又可以划分为16份子明文分组。
MD5有4轮非常相似的运算,每一轮包括16个类似的步骤,每一个步骤的数据处理都是针对4个32位记录单元中的数据进行的。这4个链接变量的初始值以16进位制表示如下(低字节优先)A:0x01234567,B:0x89ABCDEF,C:0xFEDCBA98,D:0x76543210,这时
A、B、C、D四个链接变量的值为:A=0x67452301,B=0xEFCDAB89,C=0x98BADCFE,D=0x10325476
。链接变量用于存放中间散列函数值,经过4轮运算(共64个步骤)之后,链接变量A,B,C,D中的128位即为中间散列函数值。中间散列函数值作为下一个明文分组的输入继续使用,当所有的明文分组都处理完毕后,链接变量A,B,C,D中的128位数据就是摘要。
五、MD5第1轮运算
MD5有4轮非常相似的运算,每一轮包括16个类似的步骤,当第1轮运算中的第1步骤开始处理时,A、B、C、D四个链接变量中的值先赋值到另外4个记录单元A′,B′,C′,D′中。这4个值将保留,用于在第4轮的最后一个步骤完成之后与A,B,C,D进行求和操作。
第1轮的操作程序为FF(a,b,c,d,M[k],S,T[i])
它表示的逻辑为:a←b+((a+F(b,c,d)+M[k]+T[i])<<<S)
其中,a、b、c、d为32位的变量,M[k]表示相应的子明文分组,对于4轮共64步的MD5算法中T[i]是64个不同的固定的数值,S为循环左移的位数,F(x,y,z)是第一轮的逻辑函数,最后将结果存放在链接变量A中,固定值T[i],循环左移位数和逻辑函数将在以后讨论。
第1轮16步的固定值T[i]的取值如表1-2所示。
表8-1-2 MD5第1轮固定数T
T[1]=D76AA478
|
T[5]=F57C0FAF
|
T[9]=698098D8
|
T[13]=6B901122
|
T[2]=E8C7B756
|
T[6]=4787C62A
|
T[10]=8B44F7AF
|
T[14]=FD987193
|
T[3]=242070DB
|
T[7]=A8304613
|
T[11]=FFFF5BB1
|
T[15]=A679438E
|
T[4]=C1BDCEEE
|
T[8]=FD469501
|
T[12]=895CD7BE
|
T[16]=49B40821
|
MD5规定,第一轮的16步的操作程序如表1-3所示。
表1-3 MD5第1轮16步运算
步骤数
|
运算
|
1
|
FF(A,B,C,D,M[0],7,0xD76AA478)
|
2
|
FF(D,A,B,C,M[1],12,0xE8C7B756)
|
3
|
FF(C,D,A,B,M[2],17,0x242070DB)
|
4
|
FF(B,C,D,A,M[3],22,0xC1BDCEEE)
|
5
|
FF(A,B,C,D,M[4],7,0xF57C0FAF)
|
6
|
FF(D,A,B,C,M[5],12,0x4787C62A)
|
7
|
FF(C,D,A,B,M[6],17,0xA8304613)
|
8
|
FF(B,C,D,A,M[7],22,0xFD469501)
|
9
|
FF(A,B,C,D,M[8],7,0x698098D8)
|
10
|
FF(D,A,B,C,M[9],12,0x8B44F7AF)
|
11
|
FF(C,D,A,B,M[10],17,0xFFFF5BB1)
|
12
|
FF(B,C,D,A,M[11],22,0x895CD7BE)
|
13
|
FF(A,B,C,D,M[12],7,0x6B901122)
|
14
|
FF(D,A,B,C,M[13],12,0xFD987193)
|
15
|
FF(C,D,A,B,M[14],17,0xA6794383)
|
16
|
FF(B,C,D,A,M[15],22,0x49B40821)
|
MD5算法中,第一轮的逻辑函数为F(x,y,z)= ( x & y )|( ~x & z ),MD5的算法比较复杂,每一轮包括16步类似的运算,下面我们以第1轮的第1步和第2步为例来展示每一步的运算。
例如,子明文分组M[0] = 0x4368696E,第1轮的操作程序为FF(a,b,c,d,M[k],S,T[i]),它表示的逻辑为:
a←b+((a+F(b,c,d)+M[k]+T[i])<<<S)
第一轮的逻辑函数F(x,y,z)= ( x & y )|( ~x & z ),由表1-3知,第1轮第1步的运算为:FF(A,B,C,D,M[0],7,0xD76AA478),注意到这里的0xD76AA478就是T[1]的值,变量a、b、c、d分别代表链接变量A、B、C、D。首先,b、c、d要经过逻辑函数F,即:
然后得到的值要与A、M[0]和T[1]相加得0x67452301+0x98BADCFE+0x6E696843+0xD76AA478 = 0x45D40CBA,0x45D40CBA要循环左移7位,得到结果:0xEA065D22,0xEA065D22与b相加得:0xEA065D22 + 0xEFCDAB89 = 0xD9D408AB,最后,将这个结果赋值给a,第1步的计算就完成了,只有链接变量A发生了改变,这时链接标量的值为:
A = 0xD9D408AB
B = 0x89ABCDEF
C = 0xFEDCBA98
D = 0x76543210
经过16个步骤之后,MD5的第一轮运算就完成了,链接变量A、B、C、D将携带第1轮运算后的数值进入第二轮运算。
六、MD5后3轮运算
MD5第2轮、第3轮和第4轮算运与第一轮运算相似,这里给出相应的操作程序、固定数T、每一步运算和逻辑函数。
第2轮的逻辑函数为:G( x, y, z ) = ( x & z )|( y & ~z )。
第3轮的逻辑函数为:H( x, y, z ) = x⊕y⊕z。
第4轮的逻辑函数为:I( x, y, z ) = y⊕( x & ~z )。
第2轮的操作程序为:GG(A,B,C,D,M[k],S,T[i])。
它表示的逻辑为:a←b+((a+G(B,C,D)+M[k]+T[i])<<<S)。
第3轮的操作程序为:HH(A,B,C,D,M[k],S,T[i])。
它表示的逻辑为:a←b+((a+H(B,C,D)+M[k]+T[i])<<<S)。
第4轮的操作程序为:II(A,B,C,D,M[k],S,T[i])。
它表示的逻辑为:a←b+((a+I(B,C,D)+M[k]+T[i])<<<S)。
后3轮的每个步骤的运算如表1-4所示。
表1-4 MD5后3轮16步运算
第二轮
|
1
|
GG(A,B,C,D,M[1],5,0xF61E2562)
|
2
|
GG(D,A,B,C,M[6],9,0xC040B340)
|
3
|
GG(C,D,A,B,M[11],14,0x275E5A51)
|
4
|
GG(B,C,D,A,M[0],20,0xE9B6C7AA)
|
5
|
GG(A,B,C,D,M[5],5,0xD62F105D)
|
6
|
GG(D,A,B,C,M[10],9,0x02441453)
|
7
|
GG(C,D,A,B,M[15],14,0xD8A1E681)
|
8
|
GG(B,C,D,A,M[4],20,0xE7D3FBC8)
|
9
|
GG(A,B,C,D,M[9],5,0x21E1CDE6)
|
10
|
GG(D,A,B,C,M[14],9,0xC33707D6)
|
11
|
GG(C,D,A,B,M[3],14,0xF4D50D87)
|
12
|
GG(B,C,D,A,M[8],20,0x455A14ED)
|
13
|
GG(A,B,C,D,M[13],5,0xA9E3E905)
|
14
|
GG(D,A,B,C,M[2],9,0xFCEFA3F8)
|
15
|
GG(C,D,A,B,M[7],14,0x676F02D9)
|
16
|
GG(B,C,D,A,M[12],20,0x8D2A4C8A)
|
第三轮
|
1
|
HH(A,B,C,D,M[5],4,0xFFFA3942)
|
2
|
HH(D,A,B,C,M[8],11,0x8771F681)
|
3
|
HH(C,D,A,B,M[11],16,0x6D9D6122)
|
4
|
HH(B,C,D,A,M[14],23,0xFDE5380C)
|
5
|
HH(A,B,C,D,M[1],4,0xA4BEEA44)
|
6
|
HH(D,A,B,C,M[4],11,0x4BDECFA9)
|
7
|
HH(C,D,A,B,M[7],16,0xF6BB4B60)
|
8
|
HH(B,C,D,A,M[10],23,0xBEBFBC70)
|
9
|
HH(A,B,C,D,M[13],4,0x289B7EC6)
|
10
|
HH(D,A,B,C,M[0],11,0xEAA127FA)
|
11
|
HH(C,D,A,B,M[3],16,0xD4EF3085)
|
12
|
HH(B,C,D,A,M[6],23,0x04881D05)
|
13
|
HH(A,B,C,D,M[9],4,0xD9D4D039)
|
14
|
HH(D,A,B,C,M[12],11,0xE6DB99E5)
|
15
|
HH(C,D,A,B,M[15],16,0x1FA27CF8)
|
16
|
HH(B,C,D,A,M[2],23,0xC4AC5665)
|
第四轮
|
1
|
II(A,B,C,D,M[0],6,0xF4292244)
|
2
|
II(D,A,B,C,M[7],10,0x411AFF97)
|
3
|
II(C,D,A,B,M[14],15,0xAB9423A7)
|
4
|
II(B,C,D,A,M[5],21,0xFC93A039)
|
5
|
II(A,B,C,D,M[12],6,0x655B59C3)
|
6
|
II(D,A,B,C,M[3],10,0x8F0CCC92)
|
7
|
II(C,D,A,B,M[10],15,0xFFEFF47D)
|
8
|
II(B,C,D,A,M[1],21,0x85845DD1)
|
9
|
II(A,B,C,D,M[8],6,0x6AFA87E4F)
|
10
|
II(D,A,B,C,M[15],10,0xFE2CE6E0)
|
11
|
II(C,D,A,B,M[6],15,0xA3014314)
|
12
|
II(B,C,D,A,M[13],21,0x4E0811A1)
|
13
|
II(A,B,C,D,M[4],6,0xF7537E82)
|
14
|
II(D,A,B,C,M[11],10,0xBD3AF235)
|
15
|
II(C,D,A,B,M[2],15,0x2AD7D2BB)
|
16
|
II(B,C,D,A,M[9],21,0xEB86D391
|
后3轮的固定数T[i]的值如表1-5所示。
表1-5 后3轮的固定数T[i]
T[17]=F61E2562
|
T[33]=FFFA3942
|
T[49]=F4292244
|
T[18]=C040B340
|
T[34]=8771F681
|
T[50]=432AFF97
|
T[19]=265E5A51
|
T[35]=699D6122
|
T[51]=AB9423A7
|
T[20]=E9B6C7AA
|
T[36]=FDE5380C
|
T[52]=FC93A039
|
T[21]=D62F105D
|
T[37]=A4BEEA44
|
T[53]=655B59C3
|
T[22]=02441453
|
T[38]=4BDECFA9
|
T[54]=8F0CCC92
|
T[23]=D8A1E681
|
T[39]=F6BB4B60
|
T[55]=FFEFF47D
|
T[24]=E7D3FBC8
|
T[40]=BEBFBC70
|
T[56]=85845DD1
|
T[25]=21E1CDE6
|
T[41]=289B7EC6
|
T[57]=6FA87E4F
|
T[26]=C33707D6
|
T[42]=EAA127FA
|
T[58]=FE2CE6E0
|
T[27]=F4D50D87
|
T[43]=D4EF3085
|
T[59]=A3014314
|
T[28]=455A14ED
|
T[44]=04881D05
|
T[60]=4E0811A1
|
T[29]=A9E3E905
|
T[45]=D9D4D039
|
T[61]=F7657E82
|
T[30]=FCEEA3F8
|
T[46]=E6DB99E5
|
T[62]=BD3AF235
|
T[31]=676F02D9
|
T[47]=1FA27CF8
|
T[63]=2AD7D2BB
|
T[32]=8D2A4C8A
|
T[48]=C4AC5665
|
T[64]=EB86D391
|
七、求和运算
第四轮最后一步骤的A,B,C,D输出,将分别与A′,B′,C′,D′记录单元中数值进行求和操作。其结果将成为处理下一个512位明文分组时记录单元A,B,C,D的初始值。当完成了最后一个明文分组运算时,A,B,C,D中的数值就是最后的散列函数值。
八、程序的实现
补充说明:
明文信息的读取、填充:
(1)将需加密的信件信息(如一份文件)分次读取到缓冲区中,一次最好读取64*n 个字节,这样就是n 组,方便处理。
(2) 判断信息是否已全部读完,没有则对刚才读取的信息分组进行计算,如果已经读完了就要在信息尾部进行适当的填充。
(3)对信息进行填充,使其字节数除以64 (512位)时余数为56(448位)。比如在处理一个文件时,
(4)最后一次读取为70 字节,70%64=6 小于56,则需在尾部填充56-6=50 个字节,得(70+50)
%64=56。注:若消息为64n 倍数字节,则最后一次读取0 字节,据本规则将填充56 字节。
(5)最后一次读取为124 字节,124%64=60 大于56 了,则先将这一组填满(此处为4 字节)再
在下一组空间上填56 个字节,得(124+4+56)%64=56。(64-124%64+56)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//! 定义MD5状态数据结构类型
typedef struct
unsigned int state[4]; // 初始链接变量;保存16字节摘要
unsigned int count[2]; // 明文位数(用64位保存,count[0]表示低32位,count[1]表示高32位)
unsigned char PADDING[64]; // 填充位,最大64*8位
unsigned char buffer[64]; // 输入缓冲(暂存512位明文)
}MD5_State;
//! F, G, H and I 基本MD5函数
#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循环左移n位
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
//! 4轮运算中FF(第1轮), GG(第2轮), HH(第3轮), and II(第4轮)转换
void FF(unsigned int &a,unsigned int b,unsigned int c,unsigned int d,unsigned int x,unsigned int s,unsigned ac)
a += F ((b), (c), (d)) + (x) + (unsigned int)(ac);
a = ROTATE_LEFT ((a), (s));
a+= (b);
void GG(unsigned int &a,unsigned int b,unsigned int c,unsigned int d,unsigned int x,unsigned int s,unsigned ac)
a += G ((b), (c), (d)) + (x) + (unsigned int)(ac);
a = ROTATE_LEFT ((a), (s));
a += (b);
void HH(unsigned int &a,unsigned int b,unsigned int c,unsigned int d,unsigned int x,unsigned int s,unsigned ac)
a += H ((b), (c), (d)) + (x) + (unsigned int)(ac);
a = ROTATE_LEFT ((a), (s));
a+= (b);
void II(unsigned int &a,unsigned int b,unsigned int c,unsigned int d,unsigned int x,unsigned int s,unsigned ac)
a += I ((b), (c), (d)) + (x) + (unsigned int)(ac);
a = ROTATE_LEFT ((a), (s));
a += (b);
/******************************************************************************/
// 名称:Encode
// 功能:数据类型轮换(unsigned long int -> unsigned char)(未处理前明文的长度转换)
// 参数:output: 指向unsigned char类型输出缓冲区
// input: 指向unsigned long int
/******************************************************************************/
void Encode( unsigned char *output, unsigned int *input, unsigned int len )
//unsigned int为32位,需要转换存到指定char数组中
unsigned int i, j;
for(i = 0, j = 0; j < len; i++, j += 4)
output[j] = (unsigned char)(input[i] & 0xff); //长度的低8位(一个字节)
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); //长度的中间8位
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);//次高8位
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);//高8位
/******************************************************************************/
//功能:数据类型转换(unsigned char -> unsigned int)(进行子明文分组(512位分成16组,每组32位))
//参数: output: 指向unsigned int类型输入缓冲区
// input: 指向unsigned char
/******************************************************************************/
void Decode( unsigned int *output, unsigned char *input, unsigned int len )
//将明文(char 数组)的每32位为一组存到一个32位的unsigned int数组中,后面将会用来做运算
unsigned int i, j;
for( i=0, j=0; j<len; i++, j+=4 )
output[i] = ((unsigned int)input[j]) | (((unsigned int)input[j+1]) << 8) |
(((unsigned int)input[j+2]) << 16) | (((unsigned int)input[j+3]) << 24);
/******************************************************************************/
// 名称:MD5Init
// 功能:初始链接变量赋值;初始化填充位
// 参数:指向MD5状态数据变量
// 返回:无
// 备注:填充位第1位为1,其余位为0
/******************************************************************************/
void MD5_Init( MD5_State *s )
s->count[0] = s->count[1] = 0;
//! 初始链接变量
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
//! 初始填充位(目标形式: 0x80000000......,共计512位)
memset( s->PADDING, 0, sizeof(s->PADDING) );
*(s->PADDING)=0x80; //第一位为1,其余为0(1000 0000 0000 0000 0000 0000 ....)
// s->PADDING = {
// 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/******************************************************************************/
// 名称:MD5Transform
// 功能:MD5 4轮运算
// 参数:state: 链接变量;block: 子明文分组
// 返回:无
// 备注:4轮共计64步运算
/******************************************************************************/
void MD5_Transform( unsigned int state[4], unsigned char block[64] )
unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode( x, block, 64 ); //对512位明文分成16组
//! 第1轮
FF (a, b, c, d, x[0], 7, 0xd76aa478); // 1
FF (d, a, b, c, x[ 1], 12, 0xe8c7b756); // 2
FF (c, d, a, b, x[ 2], 17, 0x242070db); // 3
FF (b, c, d, a, x[ 3], 22, 0xc1bdceee); // 4
FF (a, b, c, d, x[ 4], 7, 0xf57c0faf); // 5
FF (d, a, b, c, x[ 5], 12, 0x4787c62a); // 6
FF (c, d, a, b, x[ 6], 17, 0xa8304613); // 7
FF (b, c, d, a, x[ 7], 22, 0xfd469501); // 8
FF (a, b, c, d, x[ 8], 7, 0x698098d8); // 9
FF (d, a, b, c, x[ 9], 12, 0x8b44f7af); // 10
FF (c, d, a, b, x[10], 17, 0xffff5bb1); // 11
FF (b, c, d, a, x[11], 22, 0x895cd7be); // 12
FF (a, b, c, d, x[12], 7, 0x6b901122); // 13
FF (d, a, b, c, x[13], 12, 0xfd987193); // 14
FF (c, d, a, b, x[14], 17, 0xa679438e); // 15
FF (b, c, d, a, x[15], 22, 0x49b40821); // 16
//! 第2轮
GG (a, b, c, d, x[ 1], 5, 0xf61e2562); // 17
GG (d, a, b, c, x[ 6], 9, 0xc040b340); // 18
GG (c, d, a, b, x[11], 14, 0x265e5a51); // 19
GG (b, c, d, a, x[ 0], 20, 0xe9b6c7aa); // 20
GG (a, b, c, d, x[ 5], 5, 0xd62f105d); // 21
GG (d, a, b, c, x[10], 9, 0x2441453); // 22
GG (c, d, a, b, x[15], 14, 0xd8a1e681); // 23
GG (b, c, d, a, x[ 4], 20, 0xe7d3fbc8); // 24
GG (a, b, c, d, x[ 9], 5, 0x21e1cde6); // 25
GG (d, a, b, c, x[14], 9, 0xc33707d6); // 26
GG (c, d, a, b, x[ 3], 14, 0xf4d50d87); // 27
GG (b, c, d, a, x[ 8], 20, 0x455a14ed); // 28
GG (a, b, c, d, x[13], 5, 0xa9e3e905); // 29
GG (d, a, b, c, x[ 2], 9, 0xfcefa3f8); // 30
GG (c, d, a, b, x[ 7], 14, 0x676f02d9); // 31
GG (b, c, d, a, x[12], 20, 0x8d2a4c8a); // 32
//! 第3轮
HH (a, b, c, d, x[ 5], 4, 0xfffa3942); // 33
HH (d, a, b, c, x[ 8], 11, 0x8771f681); // 34
HH (c, d, a, b, x[11], 16, 0x6d9d6122); // 35
HH (b, c, d, a, x[14], 23, 0xfde5380c); // 36
HH (a, b, c, d, x[ 1], 4, 0xa4beea44); // 37
HH (d, a, b, c, x[ 4], 11, 0x4bdecfa9); // 38
HH (c, d, a, b, x[ 7], 16, 0xf6bb4b60); // 39
HH (b, c, d, a, x[10], 23, 0xbebfbc70); // 40
HH (a, b, c, d, x[13], 4, 0x289b7ec6); // 41
HH (d, a, b, c, x[ 0], 11, 0xeaa127fa); // 42
HH (c, d, a, b, x[ 3], 16, 0xd4ef3085); // 43
HH (b, c, d, a, x[ 6], 23, 0x4881d05); // 44
HH (a, b, c, d, x[ 9], 4, 0xd9d4d039); // 45
HH (d, a, b, c, x[12], 11, 0xe6db99e5); // 46
HH (c, d, a, b, x[15], 16, 0x1fa27cf8); // 47
HH (b, c, d, a, x[ 2], 23, 0xc4ac5665); // 48
//! 第4轮
II (a, b, c, d, x[ 0], 6, 0xf4292244); // 49
II (d, a, b, c, x[ 7], 10, 0x432aff97); // 50
II (c, d, a, b, x[14], 15, 0xab9423a7); // 51
II (b, c, d, a, x[ 5], 21, 0xfc93a039); // 52
II (a, b, c, d, x[12], 6, 0x655b59c3); // 53
II (d, a, b, c, x[ 3], 10, 0x8f0ccc92); // 54
II (c, d, a, b, x[10], 15, 0xffeff47d); // 55
II (b, c, d, a, x[ 1], 21, 0x85845dd1); // 56
II (a, b, c, d, x[ 8], 6, 0x6fa87e4f); // 57
II (d, a, b, c, x[15], 10, 0xfe2ce6e0); // 58
II (c, d, a, b, x[ 6], 15, 0xa3014314); // 59
II (b, c, d, a, x[13], 21, 0x4e0811a1); // 60
II (a, b, c, d, x[ 4], 6, 0xf7537e82); // 61
II (d, a, b, c, x[11], 10, 0xbd3af235); // 62
II (c, d, a, b, x[ 2], 15, 0x2ad7d2bb); // 63
II (b, c, d, a, x[ 9], 21, 0xeb86d391); // 64
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
memset( (unsigned char*)x, 0, sizeof (x) );
/******************************************************************************/
// 名称:MD5_Update
// 功能:明文填充,明文分组,16个子明文分组
// 参数:指向SHA状态变量
// 返回:无
/******************************************************************************/
void MD5_Update( MD5_State *s, unsigned char *input, unsigned int inputLen )
unsigned int i, index, partLen;
//! 明文填充
//! 字节数 mod 64(当前拥有的明文字节数)
index = (unsigned int)((s->count[0] >> 3) & 0x3F);//一个字符一个字节占8位(count里面存的是位数,所以需要转换为字节数)
//index = (unsigned int)((s->count[0] >> 3)%64);
//! 更新位数
if((unsigned long int)(s->count[0] += ((unsigned long int)inputLen <<3))< ((unsigned long int)inputLen << 3))//inputLen是字节数,转换为位数比较
//发生溢出,则需要进位(无符号数其Max值+1==0)
s->count[1]++; //进位
s->count[1] += ((unsigned int)inputLen >> 29); //输入的高位字节数 inputlen*8 >> 32
partLen = 64 - index; //还差的明文字节数
//! MD5 4轮运算
if (inputLen >= partLen) //如果明文字符串长度能提供还差的字符长度,则继续执行
memcpy( (unsigned char*)&s->buffer[index], (unsigned char*)input, partLen ); //将还差的明文拷到缓冲区,等待处理
MD5_Transform( s->state, s->buffer ); //处理
for( i = partLen; i + 63 < inputLen; i += 64 ) //查找是否存在下一个64字节的明文
MD5_Transform( s->state, &input[i] ); //有则继续处理
index = 0;
i = 0;
memcpy ((unsigned char*)&s->buffer[index], (unsigned char*)&input[i], inputLen-i); //对不够64字节的明文字符串,先拷到缓冲区等待构建完整的512位明文
/******************************************************************************/
// 名称:MD5_Final
// 功能:MD5最后变换
// 参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
// 返回:无
/******************************************************************************/
void MD5_Final( MD5_State *s, unsigned char digest[16] )
unsigned char bits[8]; //记录未处理前的明文的长度
unsigned int index, padLen;
Encode (bits, s->count, 8); //将未处理前的明文长度转换到bits数组中
//! 长度小于448位(mod 512(64个字节)),对明文进行填充(448位为56个字节)
index = (unsigned int)((s->count[0] >> 3) & 0x3f); //计算已有的位数
padLen = (index < 56) ? (56 - index) : (120 - index);//补充说明(5)(计算还差的位数 64-index+56=120-index)
MD5_Update( s, s->PADDING, padLen ); //填充还差位数的明文
MD5_Update( s, bits, 8); //加入未处理前的明文的长度
Encode( digest, s->state, 16 ); //将MD5摘要转换到输出缓冲区里
//初始化到最开始的状态,处理下一个明文
memset ((unsigned char*)s, 0, sizeof (*s));
MD5_Init( s );
/******************************************************************************/
// 名称:SHA_digest
// 功能:生成文件摘要
// 参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
// 返回:无
/******************************************************************************/
void md5_digest( void const *strContent, unsigned int iLength, unsigned char output[16] )
unsigned char *q = (unsigned char*)strContent;
MD5_State s;
MD5_Init( &s ); //初始化MD5状态数据结构
MD5_Update( &s, q, iLength ); //处理64*n个字节的明文,对每512位的明文进行处理(有可能没有64个字节)
MD5_Final( &s, output ); //处理最后需要填充的明文
int main( int argc, char **argv )
unsigned char buff[16];
// unsigned int x=4294967295;
// printf("%u\n",(unsigned int)(x+1));
//需加密的明文
char str[4][100] =
"ab",
"md5",
"hello word!",
for(int i=0;i<4;i++)
md5_digest(str[i],(unsigned int)strlen(str[i]),buff);
printf("MD5(%s) = \n",str[i]);
//MD5摘要是128位,以16进制的形式输出
for(int j=0;j<16;j++)
printf("%x",(buff[j] & 0xF0)>>4);
printf("%x",buff[j] & 0x0F);
printf("\n\n");
return 0;
//http://www.cmd5.com上测试的数据进行验证
md5(a,32) = 0cc175b9c0f1b6a831c399e269772661
md5(ab,32) = 187ef4436122d1cc2f40dc2b92f0eba0
md5(md5,32) = 1bc29b36f623ba82aaf6724fd3b16718
md5(hello word!,32) = 39505368546302be2704b3d53b24203c
九、MD5总结
写这篇文章的原因是在课程学习中,学习到了散列函数(简单讲了MD5和SHA1),课程也没要求掌握你实现方式,但是自己更喜欢把学习的东西就学好一些,掌握的更多一些,在实验的时候,其它同学都在用提供的实验完成实验报告,而我更倾向于原理的理解,上面一些原理的东西都摘自于实验原理的介绍文章中,同时也提供了部分源代码,也需要自己动手补充一部分,当然这是选做,上面很多代码就源于那个地方,但是当时并没有充分的注释,说得也不是很清楚,自己拷贝下来后也琢磨了很久,同时也网上找了一些资料,但是有趣的是网上的代码很多都是和这个一样的,而且都没有很好的注释,实在是太多了,我想说都理解了吗,为什么没有一些自己的注释,基本是都是一个模板,当时也许是自己太愚笨了,觉得比较难的部分,别人觉得其实很简单,完全没有必要再注释了。
上面的全部代码,我都自己琢磨过,也进行了相应的注释,自认为注释的还是比较清楚了吧,给以后研究学习的同学作为参考,很多注释是自己慢慢调试,思考后得出来的,但是表述不是很精炼,也许自己明白了,但是写出来的时候却没有说明白,感兴趣的可以互相学习,共同进步,当然其中可能自己也理解错了,还望指教一二。
一.MD5 即:Message-Digest Algorithm 5 (信息-摘要算法5),用于确保信息传输完整一致,是计算机广泛使用的Hash算法,将数据运算为另一个固定长度的值,是hash算法的基本原理,MD5的前身有 MD2 ,MD3, MD4。
MD5算法特点:
1. 压缩性:任意长度的数据,算出的 MD5值长度都是固定的,16个字节。
2.容易计算:从原数据计算出MD5 非常容易。
3.抗修改性:对原数据进行任意的改动,哪怕只.
MD5是message-digest algorithm 5(信息-摘要算法)的缩写,被广泛用于加密和解密技术上,它可以说是文件的“数字指纹”。
任何一个文件,都有且只有一个独一无二的MD5信息值,并且如果这个文件被修改过,它的MD5值也将随之改变。因此,我们可以通过对比同一文件的MD5值,来校验这个文件是否被“篡改”过。
MD5是一种 Hash 算法,作用是为了信息安全,再具体点,MD5 值...
C hash 函数欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入
unsigned int getHashCode(CString str)
if(str.isEmpty())
retur
Hash(哈希),又称“散列”。
散列(hash)英文原意是“混杂”、“拼凑”、“重新表述”的意思。
在某种程度上,散列是与排序相反的一种操作,排序是将集合中的元素按照某种方式比如字典顺序排列在一起,而散列通过计算哈希值,打破元素之间原有的关系,使集合中的元素按照散列函数的分类进行排列。
在介绍一些集合时,我们总强调需要重写某个类的 equlas() 方法和 hashC...
https://zhuanlan.zhihu.com/p/26592209
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origi...
insert into admin(passwd) values(md5('admin'));
结果:21232f297a57a5a743894a0e4a801fc3
java代码:转http://blog.sina.com.cn/s/blog_6b275753010161t3.html
MD5Util.java
package example.yue