Chinaunix首页 | 论坛 | 博客
  • 博客访问: 439336
  • 博文数量: 184
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 594
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-17 16:24
个人简介

我是一只小小鸟

文章分类

全部博文(184)

文章存档

2016年(1)

2015年(55)

2014年(127)

2013年(1)

分类: C/C++

2014-05-27 15:49:32

原文地址:md5算法实现 作者:vermiliontear

md5算法简要叙述:
        
md564Bytes分组来处理输入的信息,而每一分组又被划分为164Bytes子分组,经过一系列处理后,算法的输出为四个4Bytes分组的级联。
        
拿到待加密信息后,首先需要在末尾填充一个0x80,以及无数个0x00,使得填充后信息的总长度(Bytes64求余的结果等于56。之后再填充一个以8Bytes表示的填充前信息长度(bits)。
        
接下来进入主循环的四轮运算。每一轮都会调用16次相同的函数,但参数不同。每一轮所掉用的函数均不相同。下面直接来看看主循环所掉用的所有函数:
        
第一轮:
        
FF(a, b, c, d, *M[0], 7, 0xd76aa478);
        
FF(d, a, b, c, *M[1], 12, 0xe8c7b756);
        
FF(c, d, a, b, *M[2], 17, 0x242070db);
        
FF(b, c, d, a, *M[3], 22, 0xc1bdceee);
        
FF(a, b, c, d, *M[4], 7, 0xf57c0faf);
        
FF(d, a, b, c, *M[5], 12, 0x4787c62a);
        
FF(c, d, a, b, *M[6], 17, 0xa8304613);
        
FF(b, c, d, a, *M[7], 22, 0xfd469501);
        
FF(a, b, c, d, *M[8], 7, 0x698098d8);
        
FF(d, a, b, c, *M[9], 12, 0x8b44f7af);
        
FF(c, d, a, b, *M[10], 17, 0xffff5bb1);
        
FF(b, c, d, a, *M[11], 22, 0x895cd7be);
        
FF(a, b, c, d, *M[12], 7, 0x6b901122);
        
FF(d, a, b, c, *M[13], 12, 0xfd987193);
        
FF(c, d, a, b, *M[14], 17, 0xa679438e);
        
FF(b, c, d, a, *M[15], 22, 0x49b40821);
        
第二轮:
        
GG(a, b, c, d, *M[1], 5, 0xf61e2562);
        
GG(d, a, b, c, *M[6], 9, 0xc040b340);
        
GG(c, d, a, b, *M[11], 14, 0x265e5a51);
        
GG(b, c, d, a, *M[0], 20, 0xe9b6c7aa);
        
GG(a, b, c, d, *M[5], 5, 0xd62f105d);
        
GG(d, a, b, c, *M[10], 9, 0x02441453);
        
GG(c, d, a, b, *M[15], 14, 0xd8a1e681);
        
GG(b, c, d, a, *M[4], 20, 0xe7d3fbc8);
        
GG(a, b, c, d, *M[9], 5, 0x21e1cde6);
        
GG(d, a, b, c, *M[14], 9, 0xc33707d6);
        
GG(c, d, a, b, *M[3], 14, 0xf4d50d87);
        
GG(b, c, d, a, *M[8], 20, 0x455a14ed);
        
GG(a, b, c, d, *M[13], 5, 0xa9e3e905);
        
GG(d, a, b, c, *M[2], 9, 0xfcefa3f8);
        
GG(c, d, a, b, *M[7], 14, 0x676f02d9);
        
GG(b, c, d, a, *M[12], 20, 0x8d2a4c8a);
        
第三轮:
        
HH(a, b, c, d, *M[5], 4, 0xfffa3942);
        
HH(d, a, b, c, *M[8], 11, 0x8771f681);
        
HH(c, d, a, b, *M[11], 16, 0x6d9d6122);
        
HH(b, c, d, a, *M[14], 23, 0xfde5380c);
        
HH(a, b, c, d, *M[1], 4, 0xa4beea44);
        
HH(d, a, b, c, *M[4], 11, 0x4bdecfa9);
        
HH(c, d, a, b, *M[7], 16, 0xf6bb4b60);
        
HH(b, c, d, a, *M[10], 23, 0xbebfbc70);
        
HH(a, b, c, d, *M[13], 4, 0x289b7ec6);
        
HH(d, a, b, c, *M[0], 11, 0xeaa127fa);
        
HH(c, d, a, b, *M[3], 16, 0xd4ef3085);
        
HH(b, c, d, a, *M[6], 23, 0x04881d05);
        
HH(a, b, c, d, *M[9], 4, 0xd9d4d039);
        
HH(d, a, b, c, *M[12], 11, 0xe6db99e5);
        
HH(c, d, a, b, *M[15], 16, 0x1fa27cf8);
        
HH(b, c, d, a, *M[2], 23, 0xc4ac5665);
        
第四轮:
        
II(a, b, c, d, *M[0], 6, 0xf4292244);
        
II(d, a, b, c, *M[7], 10, 0x432aff97);
        
II(c, d, a, b, *M[14], 15, 0xab9423a7);
        
II(b, c, d, a, *M[5], 21, 0xfc93a039);
        
II(a, b, c, d, *M[12], 6, 0x655b59c3);
        
II(d, a, b, c, *M[3], 10, 0x8f0ccc92);
        
II(c, d, a, b, *M[10], 15, 0xffeff47d);
        
II(b, c, d, a, *M[1], 21, 0x85845dd1);
        
II(a, b, c, d, *M[8], 6, 0x6fa87e4f);
        
II(d, a, b, c, *M[15], 10, 0xfe2ce6e0);
        
II(c, d, a, b, *M[6], 15, 0xa3014314);
        
II(b, c, d, a, *M[13], 21, 0x4e0811a1);
        
II(a, b, c, d, *M[4], 6, 0xf7537e82);
        
II(d, a, b, c, *M[11], 10, 0xbd3af235);
        
II(c, d, a, b, *M[2], 15, 0x2ad7d2bb);
        
II(b, c, d, a, *M[9], 21, 0xeb86d391);
        
可以看到,虽然有4种函数,但参数的种类均一致。下面就来说说每个参数的来历。
        
· 前四个参数abcd。它们第一次进入主循环的初始值分别为(注意:这里为大端字节序表示,通常我们用的计算机为小端系统):
         
a = 0x1234567
         
b = 0x89ABCDEF 
          c = 0xFEDCBA98
          
d = 0x7654321
        
之后再进入主循环时,它们的值为上一个64Bytes分组的计算结果。
        
· 第五个参数*M[j]。它分别代表了每个子分组(这里我使用了数组指针 unsigned int *M[16])。
        
· 第六个参数是个常数,函数中控制数据循环左移多少位。
        
· 第七个参数也是常数,不过他有来历。它是2^32*abs(sin(i))的整数部分,i1~64
        
接下来再来看看每个函数是如何实现的。
        
#define FF(arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
                          
arg1 = arg2 + ((arg1 + F(arg2, arg3, arg4) + arg5 + arg7) << arg6)
        
#define GG(arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
                            
arg1 = arg2 + ((arg1 + G(arg2, arg3, arg4) + arg5 + arg7) << arg6)
        
#define HH(arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
                        
    arg1 = arg2 + ((arg1 + H(arg2, arg3, arg4) + arg5 + arg7) << arg6)
        
#define II(arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
                         
arg1 = arg2 + ((arg1 + I(arg2, arg3, arg4) + arg5 + arg7) << arg6)
        
大部分都是一样的,区别只在于内部调用的F()G()H()I()
        
最后再来看看F()G()H()I()这四个函数是如何实现的。
        
#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)))
        
经过主循环的这四轮运算,将abcd加回到上一个64Bytes分组的运算结果上(第一次进主循环的话,上一次的运算结果就是初始值),就得到了当前64Bytes分组的运算结果。
        
所有分组计算完之后,最后的运算结果如果为大端系统,直接将abcd连在一起输出就好了;如果为小端系统,需要进行转换。


算法实现步骤:
1、初始化abcd
2、从标准输入读取待加密字符串,每次读取64Bytes
3、因为填充的信息在待加密信息的末尾,所以只要还没读到待加密信息末尾,前面的64Bytes分组(零个或多个)正常进行主循环运算。
4、当读到的待加密信息不足64Bytes时,需要对待加密信息进行填充。填充后继续进行主循环运算。
5、小端系统将运算结果转换后输出;大端系统直接级联输出。


算法实现注意事项:
1、对于小端系统,abcd的初始值为:
     
a = 0x67452301
      
b = 0xEFCDAB89
      
c = 0x98BADCFE
      
d = 0x10325476
2、填充到待加密信息末尾的填充前长度是以bit为单位的。比如填充前信息长度(也就是待加密信息的长度)为1Bytes,那么需要填充的值是8,而不是1。我在编码过程中就在这里摔过大马爬,-,.-
3、在进行主循环的运算时,如果上一次的计算结果为ABCD。那么运算的前后应该如下这样:
     
unsigned int a = 0, b = 0, c = 0, d = 0;

     
a = A; b = B; c = C; d = D;
     
abcd进入主循环进行运算,在运算过程中它们的值会改变。
     
A += a; B += b; C += c; D += d;
     
新的ABCD才是这次的运算结果,而不是abcd-,.-。(好吧,我承认,当时我还把abcd保存起来作为下一次的运算结果,-,.-
4、在主循环中,FF()GG()HH()II()函数中的移位操作是循环左移!而不是左移,-,.-
5、如果填充前的待加密信息总长度(Bytes)对64求余的结果正好是56,那还需不需要填充那个0x80和无数个0x00?回答是肯定的。实际情况是,填充一个0x80,然后填充630x00,最后再填充8Bytes的填充前长度(bits)。看起来像没事儿找事儿,不过这确实是必要的,-,.-
md5算法实现源码.rar
阅读(877) | 评论(0) | 转发(0) |
0

上一篇:Linux下Socket编程

下一篇:typedef和enum

给主人留下些什么吧!~~