当前位置:   article > 正文

MD5加密算法详细分析_C实现_md5 加密算法 c

md5 加密算法 c

MD5加密算法

本文为原创作品,转载请注明出处:http://write.blog.csdn.net/postedit/51736426——开心!


维基百科对其描述

MD5消息摘要算法英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由罗纳德·李维斯特设计,于1992年公开,用以替换MD4算法。这套算法的程序在 RFC 1321 中被加以规范。

数据(如一段文字)运算变为另一固定长度值,是散列算法的基础原理。


算法

Figure 1. 一个MD5运算— 由类似的64次循环构成,分成4组16次。 F 一个非线性函数;一个函数运算一次。 Mi 表示一个 32-bits 的输入数据, Ki 表示一个 32-bits 常数,用来完成每次不同的计算。

MD5是输入不定长度信息,输出固定长度128-bits的算法。经过程序流程,生成四个32位数据,最后联合起来成为一个128-bits散列。基本方式为,求余、取余、调整长度、与链接变量进行循环运算。得出结果。

{\displaystyle F(X,Y,Z)=(X\wedge {Y})\vee (\neg {X}\wedge {Z})} F(X,Y,Z) = (X\wedge{Y}) \vee (\neg{X} \wedge{Z})
{\displaystyle G(X,Y,Z)=(X\wedge {Z})\vee (Y\wedge \neg {Z})} G(X,Y,Z) = (X\wedge{Z}) \vee (Y \wedge \neg{Z})
{\displaystyle H(X,Y,Z)=X\oplus Y\oplus Z} H(X,Y,Z) = X \oplus Y \oplus Z
{\displaystyle I(X,Y,Z)=Y\oplus (X\vee \neg {Z})} I(X,Y,Z) = Y \oplus (X \vee \neg{Z})

{\displaystyle \oplus ,\wedge ,\vee ,\neg }\oplus, \wedge, \vee, \neg 是 XORANDOR , NOT 的符号。










更加详尽描述:

用途:

MD5的典型应用是对一段Message(字节串)产生fingerprint(指纹),以防止被“篡改”。举个例子,你将一段话写在一个叫 readme.txt文件中,并对这个readme.txt产生一个MD5的值并记录在案,然后你可以传播这个文件给别人,别人如果修改了文件中的任何内容,你对这个文件重新计算MD5时就会发现。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。
  

MD5还广泛用于加密和解密技术上,在很多操作系统中,用户的密码是以MD5值(或类似的其它算法)的方式保存的, 用户Login的时候,系统是把用户输入的密码计算成MD5值,然后再去和系统中保存的MD5值进行比较,而系统并不“知道”用户的密码是什么

原理:

       在一些初始化处理后,MD5以512位(64字节)分组来处理输入文本,每一分组又划分为16个32位(8字

)子分组。算法的输出由四个32(8字节)分组组成,将它们级联形成一个128位(32字节)散列值。 


在MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448(56位)。因此,信息的位长将被扩展至N*512+448,N为一个非负整数,N可以是零。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充然后在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,信息的位长=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。

  首先填充消息使其长度恰好为一个比512位的倍数仅小64位的数。填充方法是附一个1在消息后面,后接所要求的多个0,然后在其后附上64位的消息长度(填充前)。这两步的作用是使消息长度恰好是512位的整数倍(算法的其余部分要求如此),同时确保不同的消息在填充后不相同。 



四个32位变量初始化为: (这个为初始化变量,为固定值,经过研究所得)
A=0×01234567 
B=0×89abcdef 
C=0xfedcba98 
D=0×76543210 


它们称为链接变量(chaining variable)

 
  接着进行算法的主循环,循环的次数是消息中512位(64位)消息分组的数目。 

       将上面四个变量复制到别外的变量中:A到a,B到b,C到c,D到d。

  主循环有四轮(MD4只有三轮),每轮很相拟。第一轮进行16次操作。每次操作对a,b,c和d中的其中三个作一次非线性函数运算(利用下面非线性函数),然后将所得结果加上 第四个变量,文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a,b,c或d中之一。最后用该结果取代a,b,c或d中之一。 即下面的FF 、GG、HH、II函数
以一下是每次操作中用到的四个非线性函数(每轮一个)。 

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)) 

(&是与,|是或,~是非,^是异或) 

这些函数是这样设计的:(如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。 

函数F是按逐位方式操作:如果X,那么Y,否则Z。函数H是逐位奇偶操作符。)

设Mj表示消息的第j个子分组(从0到15),<<< s表示循环左移s位,则四种操作为: 
FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<<< s) 
GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<<< s) 
HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<<< s) 
II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<<< s) 

这四轮(64步)是: 

第一轮 
FF(a,b,c,d,M0,7,0xd76aa478) 
FF(d,a,b,c,M1,12,0xe8c7b756) 
FF(c,d,a,b,M2,17,0×242070db) 
FF(b,c,d,a,M3,22,0xc1bdceee) 
FF(a,b,c,d,M4,7,0xf57c0faf) 
FF(d,a,b,c,M5,12,0×4787c62a) 
FF(c,d,a,b,M6,17,0xa8304613) 
FF(b,c,d,a,M7,22,0xfd469501) 
FF(a,b,c,d,M8,7,0×698098d8) 
FF(d,a,b,c,M9,12,0×8b44f7af) 
FF(c,d,a,b,M10,17,0xffff5bb1) 
FF(b,c,d,a,M11,22,0×895cd7be) 
FF(a,b,c,d,M12,7,0×6b901122) 
FF(d,a,b,c,M13,12,0xfd987193) 
FF(c,d,a,b,M14,17,0xa679438e) 
FF(b,c,d,a,M15,22,0×49b40821) 

第二轮 
GG(a,b,c,d,M1,5,0xf61e2562) 
GG(d,a,b,c,M6,9,0xc040b340) 
GG(c,d,a,b,M11,14,0×265e5a51) 
GG(b,c,d,a,M0,20,0xe9b6c7aa) 
GG(a,b,c,d,M5,5,0xd62f105d) 
GG(d,a,b,c,M10,9,0×02441453) 
GG(c,d,a,b,M15,14,0xd8a1e681) 
GG(b,c,d,a,M4,20,0xe7d3fbc8) 
GG(a,b,c,d,M9,5,0×21e1cde6) 
GG(d,a,b,c,M14,9,0xc33707d6) 
GG(c,d,a,b,M3,14,0xf4d50d87) 
GG(b,c,d,a,M8,20,0×455a14ed) 
GG(a,b,c,d,M13,5,0xa9e3e905) 
GG(d,a,b,c,M2,9,0xfcefa3f8) 
GG(c,d,a,b,M7,14,0×676f02d9) 
GG(b,c,d,a,M12,20,0×8d2a4c8a) 

第三轮 
HH(a,b,c,d,M5,4,0xfffa3942) 
HH(d,a,b,c,M8,11,0×8771f681) 
HH(c,d,a,b,M11,16,0×6d9d6122) 
HH(b,c,d,a,M14,23,0xfde5380c) 
HH(a,b,c,d,M1,4,0xa4beea44) 
HH(d,a,b,c,M4,11,0×4bdecfa9) 
HH(c,d,a,b,M7,16,0xf6bb4b60) 
HH(b,c,d,a,M10,23,0xbebfbc70) 
HH(a,b,c,d,M13,4,0×289b7ec6) 
HH(d,a,b,c,M0,11,0xeaa127fa) 
HH(c,d,a,b,M3,16,0xd4ef3085) 
HH(b,c,d,a,M6,23,0×04881d05) 
HH(a,b,c,d,M9,4,0xd9d4d039) 
HH(d,a,b,c,M12,11,0xe6db99e5) 
HH(c,d,a,b,M15,16,0×1fa27cf8) 
HH(b,c,d,a,M2,23,0xc4ac5665) 

第四轮 
II(a,b,c,d,M0,6,0xf4292244) 
II(d,a,b,c,M7,10,0×432aff97) 
II(c,d,a,b,M14,15,0xab9423a7) 
II(b,c,d,a,M5,21,0xfc93a039) 
II(a,b,c,d,M12,6,0×655b59c3) 
II(d,a,b,c,M3,10,0×8f0ccc92) 
II(c,d,a,b,M10,15,0xffeff47d) 
II(b,c,d,a,M1,21,0×85845dd1) 
II(a,b,c,d,M8,6,0×6fa87e4f) 
II(d,a,b,c,M15,10,0xfe2ce6e0) 
II(c,d,a,b,M6,15,0xa3014314) 
II(b,c,d,a,M13,21,0×4e0811a1) 
II(a,b,c,d,M4,6,0xf7537e82) 
II(d,a,b,c,M11,10,0xbd3af235) 
II(c,d,a,b,M2,15,0×2ad7d2bb) 
II(b,c,d,a,M9,21,0xeb86d391) 
  

常数ti可以如下选择: 
在第i步中,ti是4294967296*abs(sin(i))的整数部分,i的单位是弧度。 

(2的32次方) 


所有这些完成之后,将A,B,C,D分别加上a,b,c,d。然后用下一分组数据继续运行算法,最后的输

出是A,B,C和D的级联。 


下面看代码,其中进行了十分详细的注释(代码经过linux下测试与传统MD5加密相同):


  1. /*************************************************************************
  2. > File Name: md5_1.cpp
  3. > Author:chudongfang
  4. > Mail:1149669942@qq.com
  5. > Created Time: 2016年06月22日 星期三 16时12分30秒
  6. ************************************************************************/
  7. #include <string.h>
  8. #include <math.h>
  9. #include <stdio.h>
  10. /***********************************
  11. * 非线性函数
  12. * (&是与,|是或,~是非,^是异或)
  13. *
  14. * 这些函数是这样设计的:
  15. * 如果X、Y和Z的对应位是独立和均匀的,
  16. * 那么结果的每一位也应是独立和均匀的。
  17. *
  18. * 函数F是按逐位方式操作:如果X,那么Y,否则Z。
  19. * 函数H是逐位奇偶操作符
  20. **********************************/
  21. #define F(x,y,z) ((x & y) | (~x & z))
  22. #define G(x,y,z) ((x & z) | (y & ~z))
  23. #define H(x,y,z) (x^y^z)
  24. #define I(x,y,z) (y ^ (x | ~z))
  25. /**************************************
  26. *向右环移n个单位
  27. * ************************************/
  28. #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
  29. /****************************************************
  30. * 每次操作对a,b,c和d中的其中三个作一次非线性函数运算
  31. * F(b,c,d) G(b,c,d) H(b,c,d) I(b,c,d)
  32. *
  33. * 然后将所得结果加上 第四个变量(a),
  34. * F(b,c,d)+a
  35. *
  36. * 文本的一个子分组(x)
  37. * F(b,c,d)+a+x
  38. *
  39. * 和一个常数(ac)。
  40. * F(b,c,d)+a+x+ac
  41. *
  42. * 再将所得结果向右环移一个不定的数(s),
  43. * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )
  44. *
  45. * 并加上a,b,c或d中之一(b)。
  46. * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
  47. *
  48. * 最后用该结果取代a,b,c或d中之一(a)。
  49. * a=ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
  50. *
  51. * ***************************************************/
  52. #define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  53. #define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  54. #define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  55. #define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  56. //储存一个MD5 text信息
  57. typedef struct
  58. {
  59. unsigned int count[2];
  60. //记录当前状态,其数据位数
  61. unsigned int state[4];
  62. //4个数,一共32位 记录用于保存对512bits信息加密的中间结果或者最终结果
  63. unsigned char buffer[64];
  64. //一共64字节,512位
  65. }MD5_CTX;
  66. //第一位1 其后若干个0,用于MD5Final函数时的补足
  67. unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  68. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  69. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  70. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  71. //函数声明区,每个函数在下面都有较详细说明,这里不再赘述
  72. void MD5Init(MD5_CTX *context);
  73. void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
  74. void MD5Final(MD5_CTX *context,unsigned char digest[16]);
  75. void MD5Transform(unsigned int state[4],unsigned char block[64]);
  76. void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
  77. void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
  78. /************************
  79. * 函数功能:初始化一个MD5 text
  80. * 函数参数:MD5 text 指针
  81. * ***********************/
  82. //初始化
  83. void MD5Init(MD5_CTX *context)
  84. {
  85. context->count[0] = 0;
  86. context->count[1] = 0;
  87. //分别赋固定值
  88. context->state[0] = 0x67452301;
  89. context->state[1] = 0xEFCDAB89;
  90. context->state[2] = 0x98BADCFE;
  91. context->state[3] = 0x10325476;
  92. }
  93. /************************************************
  94. * 函数功能:对一个MD5 text,把输入的数据进行分组,并进行加密
  95. * 未用到的数据把其储存在MD5 text中。
  96. *
  97. * 参数分析:
  98. * MD5_CTX *context :一个MD5 text
  99. * unsigned char *input :新添加的数据
  100. * unsigned int inputlen :新添加数据的长度(字节)
  101. *
  102. ***********************************************/
  103. void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
  104. {
  105. unsigned int i = 0,index = 0,partlen = 0;
  106. //index:当前状态的位数对64取余,其单位是字节
  107. //也可以写作: index=(context->count[0]/8)%64
  108. index = (context->count[0] >> 3) & 0x3F;
  109. //partlen:可以补齐64字节的字节数
  110. partlen = 64 - index;
  111. //下面代码是解决一个unsignde int 无法储存极大数据导致溢出的问题
  112. //当前位数加上新添加的位数,由于inputlen是以字节为单位,所以其转换为位数
  113. //相当于context->count[0] += inputlen*8;
  114. context->count[0] += inputlen << 3;
  115. //当其出现溢出的情况时,通过以下操作把两个16位的数连在一块,生成一个
  116. //32位的二进制数串,从而扩大其储存范围
  117. if(context->count[0] < (inputlen << 3))
  118. context->count[1]++;
  119. //该语句可替换为 context->count[1]+=(inputlen<<3)>>32;
  120. //便于理解
  121. context->count[1] += inputlen >> 29;
  122. //当其输入字节数的大于其可以补足64字节的字节数,进行补足
  123. if(inputlen >= partlen)
  124. {
  125. //向buffer中补足partlen个字节,使其到达64字节
  126. memcpy(&context->buffer[index],input,partlen);
  127. //buffer达到64字节512位,则把其作为一组进行运算
  128. MD5Transform(context->state,context->buffer);
  129. //如果输入的数据还可以组成多个64字节,则把其可以组成
  130. //的作为若干组进行运算
  131. for(i = partlen;i+64 <= inputlen;i+=64)
  132. MD5Transform(context->state,&input[i]);
  133. //恢复0值,照应 下面 把输入 剩余字节(不能组成64字节组) 储存的操作
  134. index = 0;
  135. }
  136. //否则,把输入的数据按顺序放在原来数据后面
  137. else
  138. {
  139. i = 0;
  140. }
  141. //放置剩余数据
  142. memcpy(&context->buffer[index],&input[i],inputlen-i);
  143. }
  144. /*************************************************
  145. * 函数功能:对数据进行补足,并加入数据位数信息,并进一步加密
  146. *
  147. * 参数分析:
  148. * MD5_CTX *context :一个MD5 text
  149. * unsigned char digest[16] :储存加密结果的数组
  150. *************************************************/
  151. void MD5Final(MD5_CTX *context,unsigned char digest[16])
  152. {
  153. unsigned int index = 0,padlen = 0;
  154. //bits: 8个字节,64位
  155. unsigned char bits[8];
  156. //index:对64取余结果
  157. index = (context->count[0] >> 3) & 0x3F;
  158. //因为要填充满足使其位长对512求余的结果等于448(56位)
  159. //所以当其所剩余的数小于56字节,则填充56-index字节,
  160. //否则填充120-index字节
  161. //这里padlen代表其所需填充的字节
  162. padlen = (index < 56)?(56-index):(120-index);
  163. //然后,在这个结果后面附加一个以64位二进制表示的填充前数据长度。
  164. //把填充前数据数据长度转换后放到bit字符数组中
  165. MD5Encode(bits,context->count,8);
  166. //根据已经存储好的数组PADDING,在信息的后面填充一个1和无数个0,
  167. //直到满足上面的条件时才停止用0对信息的填充
  168. //其填充后进行了一系列的加密操作,其定剩余48个字节
  169. MD5Update(context,PADDING,padlen);
  170. //在最后添加进8个字节的数据长度信息,最后凑成一组,进行一次加密处理
  171. MD5Update(context,bits,8);
  172. //把最终得到的加密信息变成字符输出,共16字节
  173. MD5Encode(digest,context->state,16);
  174. }
  175. /**********************************************************
  176. * 函数功能:利用位操作,按1->4方式把数字分解成字符
  177. *
  178. * 参数分析:
  179. * unsigned char *output :输出的字符的数组
  180. * unsigned int *input :输入数字的数组
  181. * unsigned int len : 输入数字数组的长度(单位:位)
  182. * *********************************************************/
  183. void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
  184. {
  185. unsigned int i = 0,j = 0;
  186. while(j < len)
  187. {
  188. //这里& 0xFF为取后8位
  189. //i代表数字数组下标
  190. //j代表字符数组下标
  191. //把数字的8、8-16、16-24、24-32分别赋值给字符
  192. output[j] = input[i] & 0xFF;
  193. output[j+1] = (input[i] >> 8) & 0xFF;
  194. output[j+2] = (input[i] >> 16) & 0xFF;
  195. output[j+3] = (input[i] >> 24) & 0xFF;
  196. i++;
  197. j+=4;
  198. }
  199. }
  200. /**********************************************************
  201. * 函数功能:利用位操作,按4->1方式把字符合成数字
  202. *
  203. * 参数分析:
  204. * unsigned int *output :输出的数字的数组
  205. * unsigned char *input :输入字符的数组
  206. * unsigned int len : 输入字符的长度 (单位:位)
  207. * *********************************************************/
  208. void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
  209. {
  210. unsigned int i = 0,j = 0;
  211. while(j < len)
  212. {
  213. //利用位操作,把四个单位为1字节的字符,合成一个单位为4字节的数字
  214. //因为FF GG HH II和非线性函数都只能对数字进行处理
  215. //第一个字符占前8位,第二个占8-16位,第三个占16-24位,第四个占
  216. //24-32位。
  217. //i代表数字数组下标
  218. //j代表字符数组下标
  219. output[i] = (input[j]) |
  220. (input[j+1] << 8) |
  221. (input[j+2] << 16) |
  222. (input[j+3] << 24);
  223. i++;
  224. j+=4;
  225. }
  226. }
  227. /**************************************************************
  228. * 函数功能:对512位的block数据进行加密,并把加密结果存入state数组中
  229. * 对512位信息(即block字符数组)进行一次处理,每次处理包括四轮
  230. *state[4]:md5结构中的state[4],用于保存对512bits信息加密的中间结果或者最终结果
  231. * block[64]:欲加密的512bits信息或其中间数据
  232. ***************************************************************/
  233. void MD5Transform(unsigned int state[4],unsigned char block[64])
  234. {
  235. //a b c d继承上一个加密的结果,所以其具有继承性
  236. unsigned int a = state[0];
  237. unsigned int b = state[1];
  238. unsigned int c = state[2];
  239. unsigned int d = state[3];
  240. //这里只需用到16个,我把原来的unsiged int x[64] 改为了 x[16]
  241. unsigned int x[16];
  242. //把字符转化成数字,便于运算
  243. MD5Decode(x,block,64);
  244. //具体函数方式固定,不再赘述
  245. /*************第一轮******************/
  246. FF(a, b, c, d, x[ 0], 7, 0xd76aa478);
  247. FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);
  248. FF(c, d, a, b, x[ 2], 17, 0x242070db);
  249. FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);
  250. FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);
  251. FF(d, a, b, c, x[ 5], 12, 0x4787c62a);
  252. FF(c, d, a, b, x[ 6], 17, 0xa8304613);
  253. FF(b, c, d, a, x[ 7], 22, 0xfd469501);
  254. FF(a, b, c, d, x[ 8], 7, 0x698098d8);
  255. FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);
  256. FF(c, d, a, b, x[10], 17, 0xffff5bb1);
  257. FF(b, c, d, a, x[11], 22, 0x895cd7be);
  258. FF(a, b, c, d, x[12], 7, 0x6b901122);
  259. FF(d, a, b, c, x[13], 12, 0xfd987193);
  260. FF(c, d, a, b, x[14], 17, 0xa679438e);
  261. FF(b, c, d, a, x[15], 22, 0x49b40821);
  262. /*************第二轮*****************/
  263. GG(a, b, c, d, x[ 1], 5, 0xf61e2562);
  264. GG(d, a, b, c, x[ 6], 9, 0xc040b340);
  265. GG(c, d, a, b, x[11], 14, 0x265e5a51);
  266. GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);
  267. GG(a, b, c, d, x[ 5], 5, 0xd62f105d);
  268. GG(d, a, b, c, x[10], 9, 0x2441453);
  269. GG(c, d, a, b, x[15], 14, 0xd8a1e681);
  270. GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);
  271. GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);
  272. GG(d, a, b, c, x[14], 9, 0xc33707d6);
  273. GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);
  274. GG(b, c, d, a, x[ 8], 20, 0x455a14ed);
  275. GG(a, b, c, d, x[13], 5, 0xa9e3e905);
  276. GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);
  277. GG(c, d, a, b, x[ 7], 14, 0x676f02d9);
  278. GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
  279. /*************第三轮*****************/
  280. HH(a, b, c, d, x[ 5], 4, 0xfffa3942);
  281. HH(d, a, b, c, x[ 8], 11, 0x8771f681);
  282. HH(c, d, a, b, x[11], 16, 0x6d9d6122);
  283. HH(b, c, d, a, x[14], 23, 0xfde5380c);
  284. HH(a, b, c, d, x[ 1], 4, 0xa4beea44);
  285. HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);
  286. HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);
  287. HH(b, c, d, a, x[10], 23, 0xbebfbc70);
  288. HH(a, b, c, d, x[13], 4, 0x289b7ec6);
  289. HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);
  290. HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);
  291. HH(b, c, d, a, x[ 6], 23, 0x4881d05);
  292. HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);
  293. HH(d, a, b, c, x[12], 11, 0xe6db99e5);
  294. HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
  295. HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);
  296. /*************第四轮******************/
  297. II(a, b, c, d, x[ 0], 6, 0xf4292244);
  298. II(d, a, b, c, x[ 7], 10, 0x432aff97);
  299. II(c, d, a, b, x[14], 15, 0xab9423a7);
  300. II(b, c, d, a, x[ 5], 21, 0xfc93a039);
  301. II(a, b, c, d, x[12], 6, 0x655b59c3);
  302. II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);
  303. II(c, d, a, b, x[10], 15, 0xffeff47d);
  304. II(b, c, d, a, x[ 1], 21, 0x85845dd1);
  305. II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);
  306. II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
  307. II(c, d, a, b, x[ 6], 15, 0xa3014314);
  308. II(b, c, d, a, x[13], 21, 0x4e0811a1);
  309. II(a, b, c, d, x[ 4], 6, 0xf7537e82);
  310. II(d, a, b, c, x[11], 10, 0xbd3af235);
  311. II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);
  312. II(b, c, d, a, x[ 9], 21, 0xeb86d391);
  313. //更换原来的结果
  314. state[0] += a;
  315. state[1] += b;
  316. state[2] += c;
  317. state[3] += d;
  318. }
  319. int main(int argc, char *argv[])
  320. {
  321. MD5_CTX md5; //定义一个MD5 text
  322. MD5Init(&md5);//初始化
  323. int i;
  324. unsigned char encrypt[] ="admin";//要加密内容
  325. //21232f297a57a5a743894a0e4a801fc3
  326. unsigned char decrypt[16]; //加密结果
  327. MD5Update(&md5,encrypt,strlen((char *)encrypt));//进行初步分组加密
  328. MD5Final(&md5,decrypt); //进行后序的补足,并加密
  329. printf("加密前:%s\n加密后16位:",encrypt);
  330. for(i=4;i<12;i++)
  331. {
  332. printf("%02x",decrypt[i]); //02x前需要加上 %
  333. }
  334. printf("\n加密前:%s\n加密后32位:",encrypt);
  335. for(i=0;i<16;i++)
  336. {
  337. printf("%02x",decrypt[i]); //02x前需要加上 %
  338. }
  339. return 0;
  340. }



看过代码,相信已经有了初步的理解。这里我再根据我的理解来总结一下:

1.首先其核心部分为这九个函数:

  1. #define F(x,y,z) ((x & y) | (~x & z))
  2. #define G(x,y,z) ((x & z) | (y & ~z))
  3. #define H(x,y,z) (x^y^z)
  4. #define I(x,y,z) (y ^ (x | ~z))


  1. #define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  2. #define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  3. #define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  4. #define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }

#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))  

可以说这九个函数和一些固定参数组成了加密核心,我们把它视为一个模块,

这个模块输入为4个unsigned int 类型数字和x[16]   经过一系列位运算,输出 4个unsinged int 类型数字

而且其有连续性特点 ,即其上一次的加密结果会在本此加密中作为初始值进行加密,

外加了由 输入数据 转化成的x[]数组,参入到其中。

实现这个功能是由其实现:

<pre name="code" class="cpp" style="font-size: 18px;">void MD5Transform(unsigned int state[4],unsigned char block[64]);  

 



2.我们现在只需对数据进行处理,处理成与其所输入类型相同的数据。

根据其输入特征,我们需要512位 (64个字节)的数据,所以我们以512位为一组,


下面需要做的就是进行分组和最后加入长度信息的补全,

分组由其实现:

void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);  
加入长度信息的补全由其实现:

void MD5Final(MD5_CTX *context,unsigned char digest[16]);  


由于数据为unsinged char类型,所以在这个过程中肯定会涉及到unsinged char 和 unsinged int 类型的转化。


unsinged char ->unsinged int由其实现:

void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)  


unsinged int ->unsinged char由其实现:

void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)  


下面我自己改了一下,改成一个MD5加密器,原理完全一样,只是做了小小的修改:


  1. /*************************************************************************
  2. > File Name: md5_1.cpp
  3. > Author:chudongfang
  4. > Mail:1149669942@qq.com
  5. > Created Time: 2016年06月22日 星期三 16时12分30秒
  6. ************************************************************************/
  7. #include <string.h>
  8. #include <math.h>
  9. #include <stdio.h>
  10. /***********************************
  11. * 非线性函数
  12. * (&是与,|是或,~是非,^是异或)
  13. *
  14. * 这些函数是这样设计的:
  15. * 如果X、Y和Z的对应位是独立和均匀的,
  16. * 那么结果的每一位也应是独立和均匀的。
  17. *
  18. * 函数F是按逐位方式操作:如果X,那么Y,否则Z。
  19. * 函数H是逐位奇偶操作符
  20. **********************************/
  21. #define F(x,y,z) ((x & y) | (~x & z))
  22. #define G(x,y,z) ((x & z) | (y & ~z))
  23. #define H(x,y,z) (x^y^z)
  24. #define I(x,y,z) (y ^ (x | ~z))
  25. /**************************************
  26. *向右环移n个单位
  27. * ************************************/
  28. #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
  29. /****************************************************
  30. * 每次操作对a,b,c和d中的其中三个作一次非线性函数运算
  31. * F(b,c,d) G(b,c,d) H(b,c,d) I(b,c,d)
  32. *
  33. * 然后将所得结果加上 第四个变量(a),
  34. * F(b,c,d)+a
  35. *
  36. * 文本的一个子分组(x)
  37. * F(b,c,d)+a+x
  38. *
  39. * 和一个常数(ac)。
  40. * F(b,c,d)+a+x+ac
  41. *
  42. * 再将所得结果向右环移一个不定的数(s),
  43. * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )
  44. *
  45. * 并加上a,b,c或d中之一(b)。
  46. * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
  47. *
  48. * 最后用该结果取代a,b,c或d中之一(a)。
  49. * a=ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
  50. *
  51. * ***************************************************/
  52. #define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  53. #define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  54. #define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  55. #define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }
  56. //储存一个MD5 text信息
  57. typedef struct
  58. {
  59. unsigned int count[2];
  60. //记录当前状态,其数据位数
  61. unsigned int state[4];
  62. //4个数,一共32位 记录用于保存对512bits信息加密的中间结果或者最终结果
  63. unsigned char buffer[64];
  64. //一共64字节,512位
  65. }MD5_CTX;
  66. //第一位1 其后若干个0,用于MD5Final函数时的补足
  67. unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  68. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  69. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  70. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  71. //函数声明区,每个函数在下面都有较详细说明,这里不再赘述
  72. void MD5Init(MD5_CTX *context);
  73. void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
  74. void MD5Final(MD5_CTX *context,unsigned char digest[16]);
  75. void MD5Transform(unsigned int state[4],unsigned char block[64]);
  76. void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
  77. void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
  78. void MD5();
  79. /************************
  80. * 函数功能:初始化一个MD5 text
  81. * 函数参数:MD5 text 指针
  82. * ***********************/
  83. //初始化
  84. void MD5Init(MD5_CTX *context)
  85. {
  86. context->count[0] = 0;
  87. context->count[1] = 0;
  88. //分别赋固定值
  89. context->state[0] = 0x67452301;
  90. context->state[1] = 0xEFCDAB89;
  91. context->state[2] = 0x98BADCFE;
  92. context->state[3] = 0x10325476;
  93. }
  94. /************************************************
  95. * 函数功能:对一个MD5 text,把输入的数据进行分组,并进行加密
  96. * 未用到的数据把其储存在MD5 text中。
  97. *
  98. * 参数分析:
  99. * MD5_CTX *context :一个MD5 text
  100. * unsigned char *input :新添加的数据
  101. * unsigned int inputlen :新添加数据的长度(字节)
  102. *
  103. ***********************************************/
  104. void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
  105. {
  106. unsigned int i = 0,index = 0,partlen = 0;
  107. //index:当前状态的位数对64取余,其单位是字节
  108. //也可以写作: index=(context->count[0]/8)%64
  109. index = (context->count[0] >> 3) & 0x3F;
  110. //partlen:可以补齐64字节的字节数
  111. partlen = 64 - index;
  112. //下面代码是解决一个unsignde int 无法储存极大数据导致溢出的问题
  113. //当前位数加上新添加的位数,由于inputlen是以字节为单位,所以其转换为位数
  114. //相当于context->count[0] += inputlen*8;
  115. context->count[0] += inputlen << 3;
  116. //当其出现溢出的情况时,通过以下操作把两个16位的数连在一块,生成一个
  117. //32位的二进制数串,从而扩大其储存范围
  118. if(context->count[0] < (inputlen << 3))
  119. context->count[1]++;
  120. //该语句可替换为 context->count[1]+=(inputlen<<3)>>32;
  121. //便于理解
  122. context->count[1] += inputlen >> 29;
  123. //当其输入字节数的大于其可以补足64字节的字节数,进行补足
  124. if(inputlen >= partlen)
  125. {
  126. //向buffer中补足partlen个字节,使其到达64字节
  127. memcpy(&context->buffer[index],input,partlen);
  128. //buffer达到64字节512位,则把其作为一组进行运算
  129. MD5Transform(context->state,context->buffer);
  130. //如果输入的数据还可以组成多个64字节,则把其可以组成
  131. //的作为若干组进行运算
  132. for(i = partlen;i+64 <= inputlen;i+=64)
  133. MD5Transform(context->state,&input[i]);
  134. //恢复0值,照应 下面 把输入 剩余字节(不能组成64字节组) 储存的操作
  135. index = 0;
  136. }
  137. //否则,把输入的数据按顺序放在原来数据后面
  138. else
  139. {
  140. i = 0;
  141. }
  142. //放置剩余数据
  143. memcpy(&context->buffer[index],&input[i],inputlen-i);
  144. }
  145. /*************************************************
  146. * 函数功能:对数据进行补足,并加入数据位数信息,并进一步加密
  147. *
  148. * 参数分析:
  149. * MD5_CTX *context :一个MD5 text
  150. * unsigned char digest[16] :储存加密结果的数组
  151. *************************************************/
  152. void MD5Final(MD5_CTX *context,unsigned char digest[16])
  153. {
  154. unsigned int index = 0,padlen = 0;
  155. //bits: 8个字节,64位
  156. unsigned char bits[8];
  157. //index:对64取余结果
  158. index = (context->count[0] >> 3) & 0x3F;
  159. //因为要填充满足使其位长对512求余的结果等于448(56位)
  160. //所以当其所剩余的数小于56字节,则填充56-index字节,
  161. //否则填充120-index字节
  162. //这里padlen代表其所需填充的字节
  163. padlen = (index < 56)?(56-index):(120-index);
  164. //然后,在这个结果后面附加一个以64位二进制表示的填充前数据长度。
  165. //把填充前数据数据长度转换后放到bit字符数组中
  166. MD5Encode(bits,context->count,8);
  167. //根据已经存储好的数组PADDING,在信息的后面填充一个1和无数个0,
  168. //直到满足上面的条件时才停止用0对信息的填充
  169. //其填充后进行了一系列的加密操作,其定剩余48个字节
  170. MD5Update(context,PADDING,padlen);
  171. //在最后添加进8个字节的数据长度信息,最后凑成一组,进行一次加密处理
  172. MD5Update(context,bits,8);
  173. //把最终得到的加密信息变成字符输出,共16字节
  174. MD5Encode(digest,context->state,16);
  175. }
  176. /**********************************************************
  177. * 函数功能:利用位操作,按1->4方式把数字分解成字符
  178. *
  179. * 参数分析:
  180. * unsigned char *output :输出的字符的数组
  181. * unsigned int *input :输入数字的数组
  182. * unsigned int len : 输入数字数组的长度(单位:位)
  183. * *********************************************************/
  184. void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
  185. {
  186. unsigned int i = 0,j = 0;
  187. while(j < len)
  188. {
  189. //这里& 0xFF为取后8位
  190. //i代表数字数组下标
  191. //j代表字符数组下标
  192. //把数字的8、8-16、16-24、24-32分别赋值给字符
  193. output[j] = input[i] & 0xFF;
  194. output[j+1] = (input[i] >> 8) & 0xFF;
  195. output[j+2] = (input[i] >> 16) & 0xFF;
  196. output[j+3] = (input[i] >> 24) & 0xFF;
  197. i++;
  198. j+=4;
  199. }
  200. }
  201. /**********************************************************
  202. * 函数功能:利用位操作,按4->1方式把字符合成数字
  203. *
  204. * 参数分析:
  205. * unsigned int *output :输出的数字的数组
  206. * unsigned char *input :输入字符的数组
  207. * unsigned int len : 输入字符的长度 (单位:位)
  208. * *********************************************************/
  209. void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
  210. {
  211. unsigned int i = 0,j = 0;
  212. while(j < len)
  213. {
  214. //利用位操作,把四个单位为1字节的字符,合成一个单位为4字节的数字
  215. //因为FF GG HH II和非线性函数都只能对数字进行处理
  216. //第一个字符占前8位,第二个占8-16位,第三个占16-24位,第四个占
  217. //24-32位。
  218. //i代表数字数组下标
  219. //j代表字符数组下标
  220. output[i] = (input[j]) |
  221. (input[j+1] << 8) |
  222. (input[j+2] << 16) |
  223. (input[j+3] << 24);
  224. i++;
  225. j+=4;
  226. }
  227. }
  228. /**************************************************************
  229. * 函数功能:对512位的block数据进行加密,并把加密结果存入state数组中
  230. * 对512位信息(即block字符数组)进行一次处理,每次处理包括四轮
  231. *state[4]:md5结构中的state[4],用于保存对512bits信息加密的中间结果或者最终结果
  232. * block[64]:欲加密的512bits信息或其中间数据
  233. ***************************************************************/
  234. void MD5Transform(unsigned int state[4],unsigned char block[64])
  235. {
  236. //a b c d继承上一个加密的结果,所以其具有继承性
  237. unsigned int a = state[0];
  238. unsigned int b = state[1];
  239. unsigned int c = state[2];
  240. unsigned int d = state[3];
  241. //这里只需用到16个,我把原来的unsiged int x[64] 改为了 x[16]
  242. unsigned int x[16];
  243. //把字符转化成数字,便于运算
  244. MD5Decode(x,block,64);
  245. //具体函数方式固定,不再赘述
  246. /*************第一轮******************/
  247. FF(a, b, c, d, x[ 0], 7, 0xd76aa478);
  248. FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);
  249. FF(c, d, a, b, x[ 2], 17, 0x242070db);
  250. FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);
  251. FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);
  252. FF(d, a, b, c, x[ 5], 12, 0x4787c62a);
  253. FF(c, d, a, b, x[ 6], 17, 0xa8304613);
  254. FF(b, c, d, a, x[ 7], 22, 0xfd469501);
  255. FF(a, b, c, d, x[ 8], 7, 0x698098d8);
  256. FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);
  257. FF(c, d, a, b, x[10], 17, 0xffff5bb1);
  258. FF(b, c, d, a, x[11], 22, 0x895cd7be);
  259. FF(a, b, c, d, x[12], 7, 0x6b901122);
  260. FF(d, a, b, c, x[13], 12, 0xfd987193);
  261. FF(c, d, a, b, x[14], 17, 0xa679438e);
  262. FF(b, c, d, a, x[15], 22, 0x49b40821);
  263. /*************第二轮*****************/
  264. GG(a, b, c, d, x[ 1], 5, 0xf61e2562);
  265. GG(d, a, b, c, x[ 6], 9, 0xc040b340);
  266. GG(c, d, a, b, x[11], 14, 0x265e5a51);
  267. GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);
  268. GG(a, b, c, d, x[ 5], 5, 0xd62f105d);
  269. GG(d, a, b, c, x[10], 9, 0x2441453);
  270. GG(c, d, a, b, x[15], 14, 0xd8a1e681);
  271. GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);
  272. GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);
  273. GG(d, a, b, c, x[14], 9, 0xc33707d6);
  274. GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);
  275. GG(b, c, d, a, x[ 8], 20, 0x455a14ed);
  276. GG(a, b, c, d, x[13], 5, 0xa9e3e905);
  277. GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);
  278. GG(c, d, a, b, x[ 7], 14, 0x676f02d9);
  279. GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
  280. /*************第三轮*****************/
  281. HH(a, b, c, d, x[ 5], 4, 0xfffa3942);
  282. HH(d, a, b, c, x[ 8], 11, 0x8771f681);
  283. HH(c, d, a, b, x[11], 16, 0x6d9d6122);
  284. HH(b, c, d, a, x[14], 23, 0xfde5380c);
  285. HH(a, b, c, d, x[ 1], 4, 0xa4beea44);
  286. HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);
  287. HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);
  288. HH(b, c, d, a, x[10], 23, 0xbebfbc70);
  289. HH(a, b, c, d, x[13], 4, 0x289b7ec6);
  290. HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);
  291. HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);
  292. HH(b, c, d, a, x[ 6], 23, 0x4881d05);
  293. HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);
  294. HH(d, a, b, c, x[12], 11, 0xe6db99e5);
  295. HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
  296. HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);
  297. /*************第四轮******************/
  298. II(a, b, c, d, x[ 0], 6, 0xf4292244);
  299. II(d, a, b, c, x[ 7], 10, 0x432aff97);
  300. II(c, d, a, b, x[14], 15, 0xab9423a7);
  301. II(b, c, d, a, x[ 5], 21, 0xfc93a039);
  302. II(a, b, c, d, x[12], 6, 0x655b59c3);
  303. II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);
  304. II(c, d, a, b, x[10], 15, 0xffeff47d);
  305. II(b, c, d, a, x[ 1], 21, 0x85845dd1);
  306. II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);
  307. II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
  308. II(c, d, a, b, x[ 6], 15, 0xa3014314);
  309. II(b, c, d, a, x[13], 21, 0x4e0811a1);
  310. II(a, b, c, d, x[ 4], 6, 0xf7537e82);
  311. II(d, a, b, c, x[11], 10, 0xbd3af235);
  312. II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);
  313. II(b, c, d, a, x[ 9], 21, 0xeb86d391);
  314. //更换原来的结果
  315. state[0] += a;
  316. state[1] += b;
  317. state[2] += c;
  318. state[3] += d;
  319. }
  320. void MD5()
  321. {
  322. MD5_CTX md5; //定义一个MD5 text
  323. MD5Init(&md5);//初始化
  324. unsigned char encrypt[1000];//要加密内容
  325. unsigned char decrypt[16]; //加密结果
  326. printf("请输入要加密的信息:");
  327. scanf("%s",encrypt);
  328. MD5Update(&md5,encrypt,strlen((char *)encrypt));//进行初步分组加密
  329. MD5Final(&md5,decrypt); //进行后序的补足,并加密
  330. printf("\n加密前:%s\n加密后16位:",encrypt);
  331. for(int i=4;i<12;i++)
  332. printf("%02x",decrypt[i]); //02x前需要加上 %
  333. printf("\n加密后:%s\n加密后32位:",encrypt);
  334. for(int i=0;i<16;i++)
  335. printf("%02x",decrypt[i]); //02x前需要加上 %
  336. }
  337. int main(int argc, char *argv[])
  338. {
  339. int chioce;
  340. do
  341. {
  342. printf("\n\t\t**********MD5加密器××********\n");
  343. printf("\t\t* 1.数据加密 *\n");
  344. printf("\t\t* 0.退出 *\n");
  345. printf("\t\t*******************************\n");
  346. printf("\t\t请选择:");
  347. scanf("%d",&chioce);
  348. switch(chioce)
  349. {
  350. case 1:
  351. MD5();
  352. break;
  353. default:
  354. break;
  355. }
  356. }while(chioce!=0);
  357. return 0;
  358. }


本文为原创作品,转载请注明出处:http://write.blog.csdn.net/postedit/51736426


附MD5加密网站:http://tool.chinaz.com/tools/md5.aspx





声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/515633
推荐阅读
相关标签
  

闽ICP备14008679号