OpenSSL源码分析之MD5算法

本文深入讲解了MD5算法的工作原理及其实现细节,包括信息填充、主循环操作等步骤,并提供了OpenSSL中MD5算法的具体源码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


        MD 5 Message-DigestAlgorithm5 ),也叫消息摘要算法第五版,是上一代算法MD4 的升级版,是当前计算机领域用于确保信息传输完整一致而广泛使用的 散列算法 之一(又译哈希算法、摘要算法等),主流编程语言普遍已有MD5 的实现。MD5 MD4 MD3 MD2 改进而来,主要增强算法复杂度和不可逆性。目前,MD5 算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的错误检查领域。

        MD5算法的基本流程如下(流程参考自http://kmplayer.iteye.com/blog/628936):

                                                                                           

  1. 对每个流程作出解释:
    信息填充
    目的:使信息位长度恰好是512的整数倍。
    填充的方法:在信息的后面填充一个1和无数个0,直到使信息的位长(BitsLength)将被扩展至N*512+448,才停止用0对信息的填充。然后,在这个结果后面附加一个以64位二进制表示的填充前信息长度。这样做的原因是为满足后面处理中对信息长度的要求。


    主循环操作
    首先给定链接变量,具体包括四个32位的整数参数,分别为:

    A=0x01234567B=0x89abcdefC=0xfedcba98D=0x76543210

    将上面四个链接变量复制到另外四个变量中:AaBbCcDd
    将当前将要处理的512位信息分成16组,每组32位,分别表示为:M0M1,……,M15
    ti
    表示一个常数,可以如下选择:在第i步中,ti4294967296*abs(sin(i))的整数部分,i的单位是弧度。
    每轮循环操作都很相似,都要进行16次操作。每次操作对abcd中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组Mj和一个常数ti。再将所得结果向右环移一个不定的数s,并加上abcd中之一。最后用该结果取代abcd中之一。

    1. 每次轮循环可以表示成

                                                                               

  1. 一轮循环
    第一轮循环所用到的非线性函数: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) <<s)
    第一轮的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) <<s)
    第二轮的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) <<s)
    第三轮的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) <<s)
    第四轮的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)
    所有这些完成之后,将ABCD分别加上abcd。然后用下一分组数据继续运行算法,最后的输出是ABCD的级联。


    2.在OpenSSL中的源码

            OpenSSL中,MD5算法的源码在crypto/md5文件中,和OpenSSL中的其他哈希函数一样,MD5的实现也是主要由初始化函数fips_md_init、加密函数HASH_UPDATE、和结果处理函数HASH_FINAL,其中HASH_UPDATE需要使用块加密函数HASH_BLOCK_DATA_ORDER。这些函数的宏定义分别分布在crypto.hfips_md_init)和md32_common.h(HASH_UPDATEHASH_FINALHASH_BLOCK_DATA_ORDER)中并在md5.h的头文件中转换成相应的函数名,主要思路还是和上次解析的md4函数一样,就不做叙述了。贴上笔者抠出来的可以在X86上独立编译的md5算法模块。

    #include <stdio.h>
    #include <string.h>
    
    #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");
    
    }



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值