MD
5
(Message-DigestAlgorithm5
),也叫消息摘要算法第五版,是上一代算法MD4
的升级版,是当前计算机领域用于确保信息传输完整一致而广泛使用的
散列算法
之一(又译哈希算法、摘要算法等),主流编程语言普遍已有MD5
的实现。MD5
由
MD4
、
MD3
、
MD2
改进而来,主要增强算法复杂度和不可逆性。目前,MD5
算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的错误检查领域。
MD5算法的基本流程如下(流程参考自http://kmplayer.iteye.com/blog/628936):
对每个流程作出解释:
信息填充
目的:使信息位长度恰好是512的整数倍。
填充的方法:在信息的后面填充一个1和无数个0,直到使信息的位长(BitsLength)将被扩展至N*512+448,才停止用0对信息的填充。然后,在这个结果后面附加一个以64位二进制表示的填充前信息长度。这样做的原因是为满足后面处理中对信息长度的要求。
主循环操作
首先给定链接变量,具体包括四个32位的整数参数,分别为:
A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210
将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。
将当前将要处理的512位信息分成16组,每组32位,分别表示为:M0,M1,……,M15。
ti表示一个常数,可以如下选择:在第i步中,ti是4294967296*abs(sin(i))的整数部分,i的单位是弧度。
每轮循环操作都很相似,都要进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组Mj和一个常数ti。再将所得结果向右环移一个不定的数s,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。
每次轮循环可以表示成
一轮循环
第一轮循环所用到的非线性函数:F(X,Y,Z)=(X&Y)|((~X)&Z)。
每一次操作可以表示为FF(a,b, c, d, Mj, s, ti):a= b + ((a + F(b, c, d) + Mj + ti) <。
第一轮的16次操作可以分别表示为:
FF(a,b, c, d, M0, 7, 0xd76aa478)
FF(d, a, b, c, M1, 12,0xe8c7b756)
FF(c, d, a, b, M2, 17, 0x242070db)
FF(b, c, d, a,M3, 22, 0xc1bdceee)
FF(a, b, c, d, M4, 7, 0xf57c0faf)
FF(d, a,b, c, M5, 12, 0x4787c62a)
FF(c, d, a, b, M6, 17,0xa8304613)
FF(b, c, d, a, M7, 22, 0xfd469501)
FF(a, b, c, d,M8, 7, 0x698098d8)
FF(d, a, b, c, M9, 12, 0x8b44f7af)
FF(c, d,a, b, M10, 17, 0xffff5bb1)
FF(b, c, d, a, M11, 22,0x895cd7be)
FF(a, b, c, d, M12, 7, 0x6b901122)
FF(d, a, b, c,M13, 12, 0xfd987193)
FF(c, d, a, b, M14, 17, 0xa679438e)
FF(b,c, d, a, M15, 22, 0x49b40821)
二轮循环
第二轮循环所用到的非线性函数:G(X,Y,Z)=(X&Z)|(Y&(~Z))。
每一次操作可以表示为GG(a,b, c, d, Mj, s, ti):a= b + ((a + G(b, c, d) + Mj + ti) <。
第二轮的16次操作可以分别表示为:
GG(a,b, c, d, M1, 5, 0xf61e2562)
GG(d, a, b, c, M6, 9,0xc040b340)
GG(c, d, a, b, M11, 14, 0x265e5a51)
GG(b, c, d, a,M0, 20, 0xe9b6c7aa)
GG(a, b, c, d, M5, 5, 0xd62f105d)
GG(d, a,b, c, M10, 9, 0x02441453)
GG(c, d, a, b, M15, 14,0xd8a1e681)
GG(b, c, d, a, M4, 20, 0xe7d3fbc8)
GG(a, b, c, d,M9, 5, 0x21e1cde6)
GG(d, a, b, c, M14, 9, 0xc33707d6)
GG(c, d,a, b, M3, 14, 0xf4d50d87)
GG(b, c, d, a, M8, 20,0x455a14ed)
GG(a, b, c, d, M13, 5, 0xa9e3e905)
GG(d, a, b, c,M2, 9, 0xfcefa3f8)
GG(c, d, a, b, M7, 14, 0x676f02d9)
GG(b, c,d, a, M12, 20, 0x8d2a4c8a)
三轮循环
第三轮循环所用到的非线性函数:H(X,Y,Z)=X^Y^Z。
每一次操作可以表示为HH(a,b, c, d, Mj, s, ti):a= b + ((a + H(b, c, d) + Mj + ti) <。
第三轮的16次操作可以分别表示为:
HH(a,b, c, d, M5, 4, 0xfffa3942)
HH(d, a, b, c, M8, 11,0x8771f681)
HH(c, d, a, b, M11, 16, 0x6d9d6122)
HH(b, c, d, a,M14, 23, 0xfde5380c)
HH(a, b, c, d, M1, 4, 0xa4beea44)
HH(d,a, b, c, M4, 11, 0x4bdecfa9)
HH(c, d, a, b, M7, 16,0xf6bb4b60)
HH(b, c, d, a, M10, 23, 0xbebfbc70)
HH(a, b, c, d,M13, 4, 0x289b7ec6)
HH(d, a, b, c, M0, 11, 0xeaa127fa)
HH(c,d, a, b, M3, 16, 0xd4ef3085)
HH(b, c, d, a, M6, 23,0x04881d05)
HH(a, b, c, d, M9, 4, 0xd9d4d039)
HH(d, a, b, c,M12, 11, 0xe6db99e5)
HH(c, d, a, b, M15, 16, 0x1fa27cf8)
HH(b,c, d, a, M2, 23, 0xc4ac5665)
四轮循环
第四轮循环所用到的非线性函数:I(X,Y,Z)=Y^(X|(~Z))。
每一次操作可以表示为:II(a,b, c, d, Mj, s, ti)表示a= b + ((a + I(b, c, d) + Mj + ti) <。
第四轮的16次操作可以分别表示为:
II(a,b, c, d, M0, 6, 0xf4292244)
II(d, a, b, c, M7, 10,0x432aff97)
II(c, d, a, b, M14, 15, 0xab9423a7)
II(b, c, d, a,M5, 21, 0xfc93a039)
II(a, b, c, d, M12, 6, 0x655b59c3)
II(d,a, b, c, M3, 10, 0x8f0ccc92)
II(c, d, a, b, M10, 15,0xffeff47d)
II(b, c, d, a, M1, 21, 0x85845dd1)
II(a, b, c, d,M8, 6, 0x6fa87e4f)
II(d, a, b, c, M15, 10, 0xfe2ce6e0)
II(c,d, a, b, M6, 15, 0xa3014314)
II(b, c, d, a, M13, 21,0x4e0811a1)
II(a, b, c, d, M4, 6, 0xf7537e82)
II(d, a, b, c,M11, 10, 0xbd3af235)
II(c, d, a, b, M2, 15, 0x2ad7d2bb)
II(b,c, d, a, M9, 21,0xeb86d391)
所有这些完成之后,将A、B、C、D分别加上a、b、c、d。然后用下一分组数据继续运行算法,最后的输出是A、B、C和D的级联。
2.在OpenSSL中的源码
在OpenSSL中,MD5算法的源码在crypto/md5文件中,和OpenSSL中的其他哈希函数一样,MD5的实现也是主要由初始化函数fips_md_init、加密函数HASH_UPDATE、和结果处理函数HASH_FINAL,其中HASH_UPDATE需要使用块加密函数HASH_BLOCK_DATA_ORDER。这些函数的宏定义分别分布在crypto.h(fips_md_init)和md32_common.h(HASH_UPDATE、HASH_FINAL、HASH_BLOCK_DATA_ORDER)中并在md5.h的头文件中转换成相应的函数名,主要思路还是和上次解析的md4函数一样,就不做叙述了。贴上笔者抠出来的可以在X86上独立编译的md5算法模块。
#include
#include
#define MD5_LONG unsigned long
#define MD5_CBLOCK 64
#define MD5_LBLOCK (MD5_CBLOCK/4)
#define MD5_DIGEST_LENGTH 16
#define MD32_REG_T long
#define DATA_ORDER_IS_LITTLE_ENDIAN
#define INIT_DATA_A (unsigned long)0x67452301L
#define INIT_DATA_B (unsigned long)0xefcdab89L
#define INIT_DATA_C (unsigned long)0x98badcfeL
#define INIT_DATA_D (unsigned long)0x10325476L
#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24) )
#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>>24)&0xff), \
l)
#define HASH_MAKE_STRING(c,s) do { \
unsigned long ll; \
ll=(c)->A; (void)HOST_l2c(ll,(s)); \
ll=(c)->B; (void)HOST_l2c(ll,(s)); \
ll=(c)->C; (void)HOST_l2c(ll,(s)); \
ll=(c)->D; (void)HOST_l2c(ll,(s)); \
} while (0)
#define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d))
#define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c))
#define H(b,c,d) ((b) ^ (c) ^ (d))
#define I(b,c,d) (((~(d)) | (b)) ^ (c))
#define R0(a,b,c,d,k,s,t) { \
a+=((k)+(t)+F((b),(c),(d))); \
a=ROTATE(a,s); \
a+=b; };\
#define R1(a,b,c,d,k,s,t) { \
a+=((k)+(t)+G((b),(c),(d))); \
a=ROTATE(a,s); \
a+=b; };
#define R2(a,b,c,d,k,s,t) { \
a+=((k)+(t)+H((b),(c),(d))); \
a=ROTATE(a,s); \
a+=b; };
#define R3(a,b,c,d,k,s,t) { \
a+=((k)+(t)+I((b),(c),(d))); \
a=ROTATE(a,s); \
a+=b; };
typedef struct MD5state_st1{
MD5_LONG A,B,C,D;
MD5_LONG Nl,Nh;
MD5_LONG data[MD5_LBLOCK];
unsigned int num;
}MD5_CTX;
unsigned char cleanse_ctr = 0;
int MD5_Init(MD5_CTX *c){
memset (c,0,sizeof(*c));
c->A=INIT_DATA_A;
c->B=INIT_DATA_B;
c->C=INIT_DATA_C;
c->D=INIT_DATA_D;
return 1;
}
void md5_block_data_order(MD5_CTX *c, const void *data_, size_t num){
const unsigned char *data=data_;
register unsigned MD32_REG_T A,B,C,D,l;
#ifndef MD32_XARRAY
/* See comment in crypto/sha/sha_locl.h for details. */
unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15;
# define X(i) XX##i
#else
MD5_LONG XX[MD5_LBLOCK];
# define X(i) XX[i]
#endif
A=c->A;
B=c->B;
C=c->C;
D=c->D;
for (;num--;){
HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l;
/* Round 0 */
R0(A,B,C,D,X( 0), 7,0xd76aa478L); HOST_c2l(data,l); X( 2)=l;
R0(D,A,B,C,X( 1),12,0xe8c7b756L); HOST_c2l(data,l); X( 3)=l;
R0(C,D,A,B,X( 2),17,0x242070dbL); HOST_c2l(data,l); X( 4)=l;
R0(B,C,D,A,X( 3),22,0xc1bdceeeL); HOST_c2l(data,l); X( 5)=l;
R0(A,B,C,D,X( 4), 7,0xf57c0fafL); HOST_c2l(data,l); X( 6)=l;
R0(D,A,B,C,X( 5),12,0x4787c62aL); HOST_c2l(data,l); X( 7)=l;
R0(C,D,A,B,X( 6),17,0xa8304613L); HOST_c2l(data,l); X( 8)=l;
R0(B,C,D,A,X( 7),22,0xfd469501L); HOST_c2l(data,l); X( 9)=l;
R0(A,B,C,D,X( 8), 7,0x698098d8L); HOST_c2l(data,l); X(10)=l;
R0(D,A,B,C,X( 9),12,0x8b44f7afL); HOST_c2l(data,l); X(11)=l;
R0(C,D,A,B,X(10),17,0xffff5bb1L); HOST_c2l(data,l); X(12)=l;
R0(B,C,D,A,X(11),22,0x895cd7beL); HOST_c2l(data,l); X(13)=l;
R0(A,B,C,D,X(12), 7,0x6b901122L); HOST_c2l(data,l); X(14)=l;
R0(D,A,B,C,X(13),12,0xfd987193L); HOST_c2l(data,l); X(15)=l;
R0(C,D,A,B,X(14),17,0xa679438eL);
R0(B,C,D,A,X(15),22,0x49b40821L);
/* Round 1 */
R1(A,B,C,D,X( 1), 5,0xf61e2562L);
R1(D,A,B,C,X( 6), 9,0xc040b340L);
R1(C,D,A,B,X(11),14,0x265e5a51L);
R1(B,C,D,A,X( 0),20,0xe9b6c7aaL);
R1(A,B,C,D,X( 5), 5,0xd62f105dL);
R1(D,A,B,C,X(10), 9,0x02441453L);
R1(C,D,A,B,X(15),14,0xd8a1e681L);
R1(B,C,D,A,X( 4),20,0xe7d3fbc8L);
R1(A,B,C,D,X( 9), 5,0x21e1cde6L);
R1(D,A,B,C,X(14), 9,0xc33707d6L);
R1(C,D,A,B,X( 3),14,0xf4d50d87L);
R1(B,C,D,A,X( 8),20,0x455a14edL);
R1(A,B,C,D,X(13), 5,0xa9e3e905L);
R1(D,A,B,C,X( 2), 9,0xfcefa3f8L);
R1(C,D,A,B,X( 7),14,0x676f02d9L);
R1(B,C,D,A,X(12),20,0x8d2a4c8aL);
/* Round 2 */
R2(A,B,C,D,X( 5), 4,0xfffa3942L);
R2(D,A,B,C,X( 8),11,0x8771f681L);
R2(C,D,A,B,X(11),16,0x6d9d6122L);
R2(B,C,D,A,X(14),23,0xfde5380cL);
R2(A,B,C,D,X( 1), 4,0xa4beea44L);
R2(D,A,B,C,X( 4),11,0x4bdecfa9L);
R2(C,D,A,B,X( 7),16,0xf6bb4b60L);
R2(B,C,D,A,X(10),23,0xbebfbc70L);
R2(A,B,C,D,X(13), 4,0x289b7ec6L);
R2(D,A,B,C,X( 0),11,0xeaa127faL);
R2(C,D,A,B,X( 3),16,0xd4ef3085L);
R2(B,C,D,A,X( 6),23,0x04881d05L);
R2(A,B,C,D,X( 9), 4,0xd9d4d039L);
R2(D,A,B,C,X(12),11,0xe6db99e5L);
R2(C,D,A,B,X(15),16,0x1fa27cf8L);
R2(B,C,D,A,X( 2),23,0xc4ac5665L);
/* Round 3 */
R3(A,B,C,D,X( 0), 6,0xf4292244L);
R3(D,A,B,C,X( 7),10,0x432aff97L);
R3(C,D,A,B,X(14),15,0xab9423a7L);
R3(B,C,D,A,X( 5),21,0xfc93a039L);
R3(A,B,C,D,X(12), 6,0x655b59c3L);
R3(D,A,B,C,X( 3),10,0x8f0ccc92L);
R3(C,D,A,B,X(10),15,0xffeff47dL);
R3(B,C,D,A,X( 1),21,0x85845dd1L);
R3(A,B,C,D,X( 8), 6,0x6fa87e4fL);
R3(D,A,B,C,X(15),10,0xfe2ce6e0L);
R3(C,D,A,B,X( 6),15,0xa3014314L);
R3(B,C,D,A,X(13),21,0x4e0811a1L);
R3(A,B,C,D,X( 4), 6,0xf7537e82L);
R3(D,A,B,C,X(11),10,0xbd3af235L);
R3(C,D,A,B,X( 2),15,0x2ad7d2bbL);
R3(B,C,D,A,X( 9),21,0xeb86d391L);
A = c->A += A;
B = c->B += B;
C = c->C += C;
D = c->D += D;
}
}
int MD5_Update(MD5_CTX *c, const void *data_, size_t len){
const unsigned char *data=data_;
unsigned char *p;
MD5_LONG l;
size_t n;
if (len==0) return 1;
l=(c->Nl+(((MD5_LONG)len)<<3))&0xffffffffUL;
if (l < c->Nl)
c->Nh++;
c->Nh+=(MD5_LONG)(len>>29);
c->Nl=l;
n = c->num;
if (n != 0){
p=(unsigned char *)c->data;
if (len >= MD5_CBLOCK || len+n >= MD5_CBLOCK){
memcpy (p+n,data,MD5_CBLOCK-n);
md5_block_data_order(c,p,1);
n = MD5_CBLOCK-n;
data += n;
len -= n;
c->num = 0;
memset (p,0,MD5_CBLOCK);
}else{
memcpy (p+n,data,len);
c->num += (unsigned int)len;
return 1;
}
}
n = len/MD5_CBLOCK;
if (n > 0){
md5_block_data_order(c,data,n);
n *= MD5_CBLOCK;
data += n;
len -= n;
}
if (len != 0){
p = (unsigned char *)c->data;
c->num = (unsigned int)len;
memcpy (p,data,len);
}
return 1;
}
int MD5_Final(unsigned char *md, MD5_CTX *c){
unsigned char *p = (unsigned char *)c->data;
size_t n = c->num;
p[n] = 0x80; /* there is always room for one */
n++;
if (n > (MD5_CBLOCK-8)){
memset (p+n,0,MD5_CBLOCK-n);
n=0;
md5_block_data_order(c,p,1);
}
memset (p+n,0,MD5_CBLOCK-8-n);
p += MD5_CBLOCK-8;
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
(void)HOST_l2c(c->Nh,p);
(void)HOST_l2c(c->Nl,p);
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
(void)HOST_l2c(c->Nl,p);
(void)HOST_l2c(c->Nh,p);
#endif
p -= MD5_CBLOCK;
md5_block_data_order(c,p,1);
c->num=0;
memset (p,0,MD5_CBLOCK);
#ifndef HASH_MAKE_STRING
#error "HASH_MAKE_STRING must be defined!"
#else
HASH_MAKE_STRING(c,md);
#endif
return 1;
}
void OPENSSL_cleanse(void *ptr, size_t len){
unsigned char *p = ptr;
size_t loop = len, ctr = cleanse_ctr;
while(loop--){
*(p++) = (unsigned char)ctr;
ctr += (17 + ((size_t)p & 0xF));
}
p=memchr(ptr, (unsigned char)ctr, len);
if(p)
ctr += (63 + (size_t)p);
cleanse_ctr = (unsigned char)ctr;
}
unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md){
MD5_CTX c;
static unsigned char m[MD5_DIGEST_LENGTH];
if (md == NULL) md=m;
if (!MD5_Init(&c))
return NULL;
MD5_Update(&c,d,n);
MD5_Final(md,&c);
OPENSSL_cleanse(&c,sizeof(c));
return(md);
}
int main(){
unsigned char in[]="12345678987";
/*out = 55 ff 40 9e 85 05 61 0c 02 8a d5 d4 ff a5 ea f7*/
unsigned char out[20];
size_t n;
int i;
n = strlen((const char*)in);
MD5(in,n,out);
printf("\n\nMD5 digest result :\n");
for(i=0;i<16;i++)
printf("%02x ",out[i]);
printf("\n");
}