精华内容
下载资源
问答
  • 用matlab实现的一个md5算法,有详细的注释说明
  • MD5算法详解

    万次阅读 多人点赞 2018-07-16 17:30:26
    MD5是一种哈希算法,用来保证信息的完整性。 就一段信息对应一个哈希值,且不能通过哈希值推出这段信息,而且还需要保证不存在任意两段不相同的信息对应同一个哈希值。不过MD5算法算出来的值也就16个字节(16*8=...

    0x00 前言

    MD5是一种哈希算法,用来保证信息的完整性。
    就一段信息对应一个哈希值,且不能通过哈希值推出这段信息,而且还需要保证不存在任意两段不相同的信息对应同一个哈希值。不过MD5算法算出来的值也就16个字节(16*8=128,即128位),肯定存在相同的,找到另一个所花时间长短而已。

    0x01 填充信息

    在计算机中,数据存储都是二进制存储的,所以任意一个文件都是些二进制。
    每个文件(消息)的大小(长短)都不一样,所以在计算MD5值之前,要将这些文件(消息)用特定内容填充到指定的情况为止。(这里的大小长度是指字节数)

    填充的过程如下:
    1.先判断文件(消息)的大小(长度) mod 512 == 448 mod 512 ,就是大小(长度)对512求余等于448。(这里的512、448是“位”为单位,转成“字节”就是64、56,即mod 64 == 56 mod 64)

    2.如果大小(长度)满足 mod 512 == 448 mod 512,就在文件(消息)的末尾处添加64位(8字节)的值,值的内容是原消息的长度(以位为单位)

    3.如果大小(长度)不满足要求,就执行以下操作:
    (1)填充1个1
    (2)填充0,直到满足满足过程的第一步。
    注意:这里是以位为单位,假如是以字节为单位,第一个填充的是0x80(1000 0000),然后就填0x0
    举例:消息内容为“gnubd”,就能得到以下内容

    67 6E 75 62 64 80 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 
    

    注意到最后面这里有个0x28,数8个字节,就是0x0000000000000028,刚刚好是十进制的40,消息的内容是5个字节,也就是40位,还要注意到这里是小端字节序存储

    0x02 数据说明

    填充信息满足要求后就要开始计算MD5值了,首先先把需要的东西先列出来:

    DWORD md5::A = 0x67452301;
    DWORD md5::B = 0xEFCDAB89;
    DWORD md5::C = 0x98BADCFE;
    DWORD md5::D = 0x10325476;
    
    DWORD md5::T[64] = {
    	0xD76AA478,0xE8C7B756,0x242070DB,0xC1BDCEEE,0xF57C0FAF,0x4787C62A,0xA8304613,0xFD469501,
    	0x698098D8,0x8B44F7AF,0xFFFF5BB1,0x895CD7BE,0x6B901122,0xFD987193,0xA679438E,0x49B40821,
    	0xF61E2562,0xC040B340,0x265E5A51,0xE9B6C7AA,0xD62F105D,0x02441453,0xD8A1E681,0xE7D3FBC8,
    	0x21E1CDE6,0xC33707D6,0xF4D50D87,0x455A14ED,0xA9E3E905,0xFCEFA3F8,0x676F02D9,0x8D2A4C8A,
    	0xFFFA3942,0x8771F681,0x6D9D6122,0xFDE5380C,0xA4BEEA44,0x4BDECFA9,0xF6BB4B60,0xBEBFBC70,
    	0x289B7EC6,0xEAA127FA,0xD4EF3085,0x04881D05,0xD9D4D039,0xE6DB99E5,0x1FA27CF8,0xC4AC5665,
    	0xF4292244,0x432AFF97,0xAB9423A7,0xFC93A039,0x655B59C3,0x8F0CCC92,0xFFEFF47D,0x85845DD1,
    	0x6FA87E4F,0xFE2CE6E0,0xA3014314,0x4E0811A1,0xF7537E82,0xBD3AF235,0x2AD7D2BB,0xEB86D391
    	};
    
    DWORD md5::s[64]={
    	7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
    	5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
    	4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
    	6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
    };
    
    DWORD md5::m[64]={
    	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
    	1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
    	5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
    	0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
    };
    

    看上去很复杂,其实不是。
    首先前4个数值A、B、C、D,它们原本的值是这样的:

    A = 0x01234567
    B = 0x89ABCDEF
    C = 0xFEDCBA89
    D = 0x76543210
    

    为了保证ABCD4个值在内存中的显示情况为上面情况,所以要调整数字的位置,故实际情况是:

    A = 0x67452301;
    B = 0xEFCDAB89;
    C = 0x98BADCFE;
    D = 0x10325476;
    

    0x03 处理信息

    经过信息填充后,填充后的信息长度肯定是512位(64字节)的倍数,也就是说每512位(64字节)为1段可以分成n段,(n大于等于1),对于每一段信息(512位,64字节)又划分成16小段(每段32位,4个字节,用M表示)

    对于每一段信息,都会经过下列的运算处理,共4种函数
    FF(a,b,c,d,mj,s,ti)
    GG(a,b,c,d,mj,s,ti)
    HH(a,b,c,d,mj,s,ti)
    II(a,b,c,d,mj,s,ti)

    对于每种函数都会执行64次
    执行顺序是:
    先执行16次FF,再执行16次GG,再执行16次HH,最后执行16次II,可以把这个4种函数各执行16次看作一次周期,那么这样的周期有4个,可以简单理解为:

    for(i = 0; i < 4; ++i) {
     	FF(16); //括号内为执行次数
     	GG(16);
     	HH(16);
     	II(16);
    }
    

    函数的内容分别是:

    void md5::FF(DWORD &a,DWORD &b,DWORD &c,DWORD &d,DWORD mj,DWORD s,DWORD ti){
    	DWORD temp = F(b,c,d) + a + mj + ti;
    	temp = (temp<<s)|(temp>>(32-s));
    	a = b + temp; 
    }
    
    void md5::GG(DWORD &a,DWORD &b,DWORD &c,DWORD &d,DWORD mj,DWORD s,DWORD ti){
    	DWORD temp = G(b,c,d) + a + mj + ti;
    	temp = (temp<<s)|(temp>>(32-s));
    	a = b + temp; 
    }
    
    void md5::HH(DWORD &a,DWORD &b,DWORD &c,DWORD &d,DWORD mj,DWORD s,DWORD ti){
    	DWORD temp = H(b,c,d) + a + mj + ti;
    	temp = (temp<<s)|(temp>>(32-s));
    	a = b + temp; 
    }
    
    void md5::II(DWORD &a,DWORD &b,DWORD &c,DWORD &d,DWORD mj,DWORD s,DWORD ti){
    	DWORD temp = I(b,c,d) + a + mj + ti;
    	temp = (temp<<s)|(temp>>(32-s));
    	a = b + temp; 
    }
    

    其中,F,G,H,I分别是:

    DWORD md5::F(DWORD X,DWORD Y,DWORD Z){
    	return (X&Y)|((~X)&Z);
    }
    
    DWORD md5::G(DWORD X,DWORD Y,DWORD Z){
    	return (X&Z)|(Y&(~Z));
    }
    
    DWORD md5::H(DWORD X,DWORD Y,DWORD Z){
    	return X^Y^Z;
    }
    
    DWORD md5::I(DWORD X,DWORD Y,DWORD Z){
    	return Y^(X|(~Z));
    }
    

    接下来是参数说明,由于每个函数的参数具有相同的意义,所以只说一个就行了。
    a,b,c,d 4个参数对应的值是周期变化的,周期长度为4,所以是:

    for(int j=0;j<16;j+=4){ //每次增加4
    	FF(A,B,C,D,M[m[j]],s[j],T[j]);
    	FF(D,A,B,C,M[m[j+1]],s[j+1],T[j+1]);
    	FF(C,D,A,B,M[m[j+2]],s[j+2],T[j+2]);
    	FF(B,C,D,A,M[m[j+3]],s[j+3],T[j+3]);
    }
    
    //GG、HH、II也类似,每种函数执行16次(1轮4次,4轮16次)后再执行下一种函数
    for(int j=0;j<16;j+=4){
    	GG(A,B,C,D,M[m[j]],s[j],T[j]);
    	GG(D,A,B,C,M[m[j+1]],s[j+1],T[j+1]);
    	GG(C,D,A,B,M[m[j+2]],s[j+2],T[j+2]);
    	GG(B,C,D,A,M[m[j+3]],s[j+3],T[j+3]);
    }
    

    M是上面每一段的16个小段了,其中m[j]表示每次函数处理的小段都不同,按照一定的顺序来处理每个小段,其中的顺序就在m中保存了。
    s是循环左移的位数,也有一定顺序。
    T是常数,共64个,意味着64次的函数调用都是用不同的数值
    对于第一段消息(前512位(64个字节))传入的a,b,c,d的值是上面的ABCD4个有规律的值。
    第一段消息处理完后(即4个函数各执行了16次之后),得到新的a,b,c,d的值,将它们分别加上原来a,b,c,d的值(即计算前的值),作为下一段消息(第2个512位(64个字节))的初始a,b,c,d的值。

    当每段消息(512位,64个字节)都处理完之后,得到的a,b,c,d的值,按照地址的顺序从低到高打印对应的值,就是所求的MD5值。

    展开全文
  • matlab实现md5算法,md5算法详解,matlab源码.zip
  • MD5 算法详解以及代码

    2011-05-03 21:04:57
    MD5算法的详细介绍,以及相应的java代码实现
  • MD5 标准算法详解

    千次阅读 2020-07-25 12:00:23
    1)不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样 2)抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。 3)弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的...

    1 简介

    视频参考(本文参考视频中的例子)
    目的:
    将任意长度的明文,转化为128位的哈希值
    例如将 123456 转化成 e10adc3949ba59abbe56e057f20f883e (16进制)

    特点:
    1)不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样
    2)抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
    3)弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
    4)强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。

    2 计算准备——数据填充

    假设明文为China,my great country
    2.1 转化成16进制
    首先将明文按码表(ASCII)转换成16进制码,得到16进制格式的字符串 H1

    China,my
    4368696E612C6D79
    greatc
    2067726561742063
    ountry
    6F756E747279

    即H1 = 4368696E612C6D7920677265617420636F756E747279

    2.2 补充至448位
    先将 H1 转化为二进制码 B1 ,B1 得数据长度为 n1
    若n1 对 512 取模不得 448,需要填充数据。在 B1 后面填充:填充的第一位为1,其余为0。由此得到 B2 。B2 的数据长度对 512 取模得 448

    2.3 补充至512位
    将 n1 转化为二进制码 Bn1 ,合并B2 与 Bn1 ,如果还不足 512位,则补0
    设处理结果为 B3 ,将 B3 转化为16进制码得到 H2 ,我们将用 H2 进行计算
    例子中的H2由三部分组成
    H2= 4368696E612C6D7920677265617420636F756E747279

    80……0(用于补充至448位,该部分补充了二进制数272位)

    B000……0(用于补充到512位,该部分补充了64位)

    3 计算准备——循环次数、初始参数和函数

    计算过程分为主循环和子循环
    3.1 循环的次数
    假设处理后的原文长度是 M,即 H2 的数据长度
    主循环的次数 = M/512 次(在例子中为一次)
    自循环的次数 = 512 / 32 * 4 = 64 次

    3.2 计算中要使用的函数
    1)MD5中使用的四种函数

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

    2)MD5中使用的四种操作

    FF(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(F(b,c,d)+Mi+tj)<<< s)
    GG(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(G(b,c,d)+Mi+tj)<<< s)
    HH(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(H(b,c,d)+Mi+tj)<<< s)
    II(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(I(b,c,d)+Mi+tj)<<< s)
    
    1)其中Mi表示消息的第i个子分组(从015,共16个),详情看3.3
    2<<< s表示循环左移s位
    3)常数tj为:在第j步中,tj是4294967296*abs(sin(j))的整数部分,i的单位是弧度。
    (4294967296232次方)
    亦可用 0x100000000UL * abs(sin((double)j)) 计算
    4)i和j 都是针对一次主循环而言的,下一个主循环开始后,i和j将重置。
    5)a,b,c,d 就是每一步中的 A,B,C,D,详情见3.4
    

    3.3 将 H2 分成 16 组
    在例子中,H2 = 4368696E612C6D7920677265617420636F756E747279 80000……000B000000000000000
    则每 8 个16进制码为一组,并且将顺序反转
    例如M[0]= 43 68 69 6E的反转 = 6E 69 68 43
    根据这种方式将 H2 分成16组,分别是M[0]-M[15]

    3.4 四个初始值
    四个初始值是MD5这个算法提前定义好的,分别是4个32位的值,总共刚好128位。

    A=0x01234567
    B=0x89ABCDEF
    C=0xFEDCBA98
    D=0x76543210
    

    4 一次主循环的计算过程

    在子循环中,将 F、G、H、I 交替使用,第一个 16 次使用 F,第二个 16 次使用 G,第三个 16 次使用 H,第四个 16 次使用 I。

    第一轮
    a=FF(a,b,c,d,M0,7,0xd76aa478)
    b=FF(d,a,b,c,M1,12,0xe8c7b756)
    c=FF(c,d,a,b,M2,17,0x242070db)
    d=FF(b,c,d,a,M3,22,0xc1bdceee)
    a=FF(a,b,c,d,M4,7,0xf57c0faf)
    b=FF(d,a,b,c,M5,12,0x4787c62a)
    c=FF(c,d,a,b,M6,17,0xa8304613)
    d=FF(b,c,d,a,M7,22,0xfd469501)
    a=FF(a,b,c,d,M8,7,0x698098d8)
    b=FF(d,a,b,c,M9,12,0x8b44f7af)
    c=FF(c,d,a,b,M10,17,0xffff5bb1)
    d=FF(b,c,d,a,M11,22,0x895cd7be)
    a=FF(a,b,c,d,M12,7,0x6b901122)
    b=FF(d,a,b,c,M13,12,0xfd987193)
    c=FF(c,d,a,b,M14,17,0xa679438e)
    d=FF(b,c,d,a,M15,22,0x49b40821)
    
    
    第二轮
    a=GG(a,b,c,d,M1,5,0xf61e2562)
    b=GG(d,a,b,c,M6,9,0xc040b340)
    c=GG(c,d,a,b,M11,14,0x265e5a51)
    d=GG(b,c,d,a,M0,20,0xe9b6c7aa)
    a=GG(a,b,c,d,M5,5,0xd62f105d)
    b=GG(d,a,b,c,M10,9,0x02441453)
    c=GG(c,d,a,b,M15,14,0xd8a1e681)
    d=GG(b,c,d,a,M4,20,0xe7d3fbc8)
    a=GG(a,b,c,d,M9,5,0x21e1cde6)
    b=GG(d,a,b,c,M14,9,0xc33707d6)
    c=GG(c,d,a,b,M3,14,0xf4d50d87)
    d=GG(b,c,d,a,M8,20,0x455a14ed)
    a=GG(a,b,c,d,M13,5,0xa9e3e905)
    b=GG(d,a,b,c,M2,9,0xfcefa3f8)
    c=GG(c,d,a,b,M7,14,0x676f02d9)
    d=GG(b,c,d,a,M12,20,0x8d2a4c8a)
    
    
    第三轮
    a=HH(a,b,c,d,M5,4,0xfffa3942)
    b=HH(d,a,b,c,M8,11,0x8771f681)
    c=HH(c,d,a,b,M11,16,0x6d9d6122)
    d=HH(b,c,d,a,M14,23,0xfde5380c)
    a=HH(a,b,c,d,M1,4,0xa4beea44)
    b=HH(d,a,b,c,M4,11,0x4bdecfa9)
    c=HH(c,d,a,b,M7,16,0xf6bb4b60)
    d=HH(b,c,d,a,M10,23,0xbebfbc70)
    a=HH(a,b,c,d,M13,4,0x289b7ec6)
    b=HH(d,a,b,c,M0,11,0xeaa127fa)
    c=HH(c,d,a,b,M3,16,0xd4ef3085)
    d=HH(b,c,d,a,M6,23,0x04881d05)
    a=HH(a,b,c,d,M9,4,0xd9d4d039)
    b=HH(d,a,b,c,M12,11,0xe6db99e5)
    c=HH(c,d,a,b,M15,16,0x1fa27cf8)
    d=HH(b,c,d,a,M2,23,0xc4ac5665)
    
    
    第四轮
    a=II(a,b,c,d,M0,6,0xf4292244)
    b=II(d,a,b,c,M7,10,0x432aff97)
    c=II(c,d,a,b,M14,15,0xab9423a7)
    d=II(b,c,d,a,M5,21,0xfc93a039)
    a=II(a,b,c,d,M12,6,0x655b59c3)
    b=II(d,a,b,c,M3,10,0x8f0ccc92)
    c=II(c,d,a,b,M10,15,0xffeff47d)
    d=II(b,c,d,a,M1,21,0x85845dd1)
    a=II(a,b,c,d,M8,6,0x6fa87e4f)
    b=II(d,a,b,c,M15,10,0xfe2ce6e0)
    c=II(c,d,a,b,M6,15,0xa3014314)
    d=II(b,c,d,a,M13,21,0x4e0811a1)
    a=II(a,b,c,d,M4,6,0xf7537e82)
    b=II(d,a,b,c,M11,10,0xbd3af235)
    c=II(c,d,a,b,M2,15,0x2ad7d2bb)
    d=II(b,c,d,a,M9,21,0xeb86d391)
    

    一次主循环后,将获取到的A,B,C,D当作下一轮的A,B,C,D的初始值,进行下一轮的计算。
    主循环的计算次数在3.1节中已给出。

    5 获取摘要

    5.1 计算A’,B’,C’,D’
    经过指定轮的主循环的计算,可以获得最终结果A,B,C,D(16进制格式)
    假设A=3A 6F 62 0E
    则A’= A 的反转 = 0E 62 6F 3A
    将A,B,C,D 按上面的方式转化成A’,B’,C’,D’

    5.2 获取最终结果
    将A’,B’,C’,D’ 合并后,就是MD5标准加密后的16进制结果。
    例子中的结果为:0E626F3A097331FE132F59DF4091F775

    6 md5盐值加密

    md5盐值加密基于md5标准加密算法
    6.1 盐值
    1)盐值为一个字符串salt,是程序员定义的
    2)可以是定值,也可以是通过明文构造出来的字符串

    6.2 加密过程
    原明文为 M,盐值为salt,MD5标准加密算法函数为md5(x)
    则加密对象为M+ salt
    加密结果 = md5(M+salt),这就是盐值加密结果

    展开全文
  • MD5 加密算法详解

    千次阅读 2019-07-02 18:08:15
    MD5 加密算法详解一、概念二、算法原理2.1、MD5(32)、MD5(16)2.2、MD5算法 4步流程三、 其他3.1、MD5的作用3.2、MD5可以作为数据库主键,唯一吗? 一、概念 MD5算法是单向散列算法的一种。单向散列算法也称为HASH...

    一、概念

    MD5算法是单向散列算法的一种。单向散列算法也称为HASH算法,是一种将任意长度的信息压缩至某一固定长度(称之为消息摘要)的函数(该压缩过程不可逆)。在MD5算法中,这个摘要是指将任意数据映射成一个128位长的摘要信息。并且其是不可逆的,即从摘要信息无法反向推演中原文,在演算过程中,原文的内容也是有丢失的。

    并非说md5不能破解。MD5破解可以参考王晓云教授的《MD5破解》论文

    因为MD5算法最终生成的是一个128位长的数据,从原理上说,有2^128种可能,这是一个非常巨大的数据,约等于3.4乘10的38次方,虽然这个是个天文数字,但是世界上可以进行加密的数据原则上说是无限的,因此是可能存在不同的内容经过MD5加密后得到同样的摘要信息,但这个碰中的概率非常小。可用于数字签名、信息完整性检查等用途;

    1、对于需要高度安全性的数据,一般改用其他算法,如SHA-2。MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是高要求的数字签名等用途。
    2、常见的散列算法还有SHA、RIPE-MD、HAVAL、N-Hash等

    二、算法原理

    2.1、MD5(32)、MD5(16)

    MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
    将这128 位用十六进制表示便是常见的32 字符的MD5 码,而所谓的16 字符的MD5 码,其实是这32 字符中间的16 个字符。

    MD5(123456,32) = e10adc3949ba59abbe56e057f20f883e
    MD5(123456,16) = 49ba59abbe56e057
    

    2.2、算法 4步流程

    MD5 算法将输入的信息进行分组,每组512 位(64个 字节),顺序处理完所有分组后输出128 位结果。
    在每一组消息的处理中,都要进行4 轮、每轮16 步、总计64 步的处理。其中每步计算中含一次左循环移位,每一步结束时将计算结果进行一次右循环移位。详见下方流程。

    第1步:进行数据填充整理

    这一步是对要加密的数据进行填充和整理,将要加密的二进制数据对512取模,得到的结果如果不够448位,则进行补足,补足的方式是第1位填充1,后面全部填充0。

    为啥是448位呢? 在数据读取时,会有一个结束标识位。如同c中的字符串,有一个\0的结束标识;
    将需加密的信件信息(如一份文件)分次读取到缓冲区中,一次最好读取64*n 个字节,这样就是n 组,方便处理。那么此时,对信息进行填充,使其字节数除以64 时余数为56,其一个结束标识符就是8字节;如,一个消息为64n 倍数字节,则最后一次读取0 字节(结束标识),据本规则将填充56 字节(448bit)。

    第2步:记录数据长度

    经过第一步整理完成后的数据的位数可以表示为N*512+448,再向其后追加64位用来存储数据的长度,比如数据的长度为16字节,则用10000来填充后64位。这一步做完后,数据的位数将变成(N+1)*512。

    第3步:以标准的幻数作为输入

    MD5的实现需要每512个字节进行一次处理,后一次处理的输入为前一次处理的输出,因此,在循环处理开始之前,需要拿4个标准数作为输入,它们分别是:

    unsigned int A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476;
    

    第4步:进行N轮循环处理,将最后的结果输出

    这一步重要的是每一轮的处理算法,每一轮处理也要循环64次,这64次循环被分为4各组,每16次循环为一组,每组循环使用不同的逻辑处理函数,处理完成后,将输出作为输入进入下一轮循环。

    通过上面的标准128bit 输入,参与每组512bit 计算,得到一个新的128值,接着参与下一轮循环运算,最终得到一个128位的值;
    具体运算:
    这里用到4 个逻辑函数F,G,H,I,分别对应4 轮运算,它们将参与运算。(4轮16步)
    第一轮逻辑函数:F(b,c,d)=(b&c)|((~b)&d) 参与第一轮的16 步运算 (b,c,d均为32位数)
    第二轮逻辑函数:G(b,c,d)=(b&d)|(c&(~d)) 参与第二轮的16 步运算
    第三轮逻辑函数:H(b,c,d)= bcd 参与第三轮的16 步运算
    第四轮逻辑函数:I(b,c,d)= c^(b|(~d)) 参与第四轮的16 步运算
    再引入一个移位函数MOVE(X,n),它将整型变量X 左循环移n 位,如变量X 为32 位,则MOVE(X,n)= (X
    << n) | (X >> (32 - n))。

    三、 其他

    3.1、作用

    • 一致性检验,如大文件上传时,需要对文件一致性校验;
    • 数字签名,还是最上面那个例子。只是把md5看出了一个指纹,按了个手印说明独一无二了;
    • 安全访问认证等;

    如:在用户注册时,会将密码进行md5加密,存到数据库中。这样可以防止那些可以看到数据库数据的人,恶意操作了。

    3.2、MD5可以作为数据库主键,唯一吗?

    前面也讲了md5无法避免碰撞,那么md5值就不唯一。
    也就是一个原始数据,只对应一个md5值;但是一个md5值,可能对应多个原始数据。

    3.3、测试源码

    package com.xuecheng.test.md5test;
    
    import org.springframework.util.DigestUtils;
    
    public class MD5 {
        /*
         *四个链接变量
         */
        private final int A=0x67452301;
        private final int B=0xefcdab89;
        private final int C=0x98badcfe;
        private final int D=0x10325476;
        /*
         *ABCD的临时变量
         */
        private int Atemp,Btemp,Ctemp,Dtemp;
    
        /*
         *常量ti
         *公式:floor(abs(sin(i+1))×(2pow32)
         */
        private final int K[]={
                0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
                0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
                0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
                0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
                0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
                0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
                0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
                0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
                0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
                0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
                0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
                0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
                0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
        /*
         *向左位移数,计算方法未知
         */
        private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
                12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
                4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
                15,21,6,10,15,21,6,10,15,21,6,10,15,21};
    
    
        /*
         *初始化函数
         */
        private void init(){
            Atemp=A;
            Btemp=B;
            Ctemp=C;
            Dtemp=D;
        }
        /*
         *移动一定位数
         */
        private int  shift(int a,int s){
            return(a<<s)|(a>>>(32-s));//右移的时候,高位一定要补零,而不是补充符号位
        }
        /*
         *主循环
         */
        private void MainLoop(int M[]){
            int F,g;
            int a=Atemp;
            int b=Btemp;
            int c=Ctemp;
            int d=Dtemp;
            for(int i = 0; i < 64; i ++){
                if(i<16){
                    F=(b&c)|((~b)&d);
                    g=i;
                }else if(i<32){
                    F=(d&b)|((~d)&c);
                    g=(5*i+1)%16;
                }else if(i<48){
                    F=b^c^d;
                    g=(3*i+5)%16;
                }else{
                    F=c^(b|(~d));
                    g=(7*i)%16;
                }
                int tmp=d;
                d=c;
                c=b;
                b=b+shift(a+F+K[i]+M[g],s[i]);
                a=tmp;
            }
            Atemp=a+Atemp;
            Btemp=b+Btemp;
            Ctemp=c+Ctemp;
            Dtemp=d+Dtemp;
    
        }
        /*
         *填充函数
         *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
         *填充方式为先加一个0,其它位补零
         *最后加上64位的原来长度
         */
        private int[] add(String str){
            int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
            int strByte[]=new int[num*16];//64/4=16,所以有16个整数
            for(int i=0;i<num*16;i++){//全部初始化0
                strByte[i]=0;
            }
            int    i;
            for(i=0;i<str.length();i++){
                strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一个整数存储四个字节,小端序
            }
            strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
            /*
             *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位
             */
            strByte[num*16-2]=str.length()*8;
            return strByte;
        }
        /*
         *调用函数
         */
        public String getMD5(String source){
            init();
            int strByte[]=add(source);
            for(int i=0;i<strByte.length/16;i++){
                int num[]=new int[16];
                for(int j=0;j<16;j++){
                    num[j]=strByte[i*16+j];
                }
                MainLoop(num);
            }
            return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
    
        }
        /*
         *整数变成16进制字符串
         */
        private String changeHex(int a){
            String str="";
            for(int i=0;i<4;i++){
                str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
    
            }
            return str;
        }
        /*
         *单例
         */
        private static MD5 instance;
        public static MD5 getInstance(){
            if(instance==null){
                instance=new MD5();
            }
            return instance;
        }
    
        private MD5(){};
    
        public static void main(String[] args){
            String str=MD5.getInstance().getMD5("123");
            System.out.println(str);
    
            String s1 = DigestUtils.md5DigestAsHex("123".getBytes());
            System.out.println(s1);
        }
    
    }
    
    

    参考资料

    https://cloud.tencent.com/developer/article/1402024
    https://my.oschina.net/u/3352105/blog/875927
    https://www.cnblogs.com/orangebook/p/3558334.html

    展开全文
  • md5加密算法详解md5加密算法详解md5加密算法详解md5加密算法详解md5加密算法详解md5加密算法详解md5加密算法详解md5加密算法详解
  • SHA1和MD5算法详解和源码

    千次阅读 2018-06-03 23:57:21
    1. SHA1和MD5算法说明 2. 源码 转载自http://www.cnblogs.com/fullsail/ 1. SHA1和MD5算法说明 SHA1和MD5算法都是从MD4算法改进而来的2种算法,基本思路都是将信息分成N个分组,每组64个字节,每个...

    1. SHA1和MD5的算法说明

    SHA1和MD5的算法都是从MD4算法改进而来的2种算法,基本思路都是将信息分成N个分组,每组64个字节,每个分组都进行摘要运算。当一个分组的摘要运算完毕后,将上一个分组的结果也用于下一个分组的运算。
    信息的长度(注意是bit位长度,不是字节长度)用64位表示,也要参加信息摘要运算,而且是放在最后一个分组的末尾,所以长度信息要占据8个字节。
    如果信息数据最后一个分组长度小于64个字节,在后面添加0x80标志结束,如果此时数据+结束标志已经<=56个字节,还可以放入长度数据,就在结束标志到第56个字节补0,然后放入长度,如果此时信息数据+结束标志已经大于56字节,那么这个分组后面补0,进行一次摘要运算,然后再建立一个分组,前面全部补0,最后16个字节放长度,再进行一次摘要。
    需要注意的地方如下。
    MD5最后生成的摘要信息是16个字节,SHA1是20个字节。
    MD5和SHA1的分组信息运算,分组里面的的数据都会被视为16个DWORD,而MD5算法认为这些DWORD的字节序列是LITTLE-ENDIAN,而SHA1的算法认为DWORD是BIG-ENDIAN的。所以在不同字节序的主机上要进行转换。
    放入最后一个分组的长度信息,是原始数据长度,而且是BIT位长度,其是一个uint64_t,而MD5算法要求放入的长度是LITTLE-ENDIAN的,而SHA1算法则要求这个长度是BIG-ENDIAN的。不同的平台要进行转换。
    当然生成的结果,MD5也要求是LITTLE-ENDIAN,SHA1也要求结果是BIG-ENDIAN的,不同的平台还是要进行转换。
    我们贴几个摘要处理过程的分组信息,帮助大家理解。如果要处理的数据是3个字节字符串”abc”,其在MD5的算法中,只需要一个分组参加,数据是16进制,如下:
    61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00
    而SHA1算法中,也只有一个分组,如下,大家注意长度位置上的差别。十六进制的18标识24个bit3个字节。
    61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18
    如果要处理的数据是80个字节的”12345678901234567890123456789012345678901234567890123456789012345678901234567890”,其在MD5的算法会被分成2个分组,
    第一个分组如下,
    31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
    37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32
    33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38
    39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34
    第二个分组如下
    35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30
    80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 80 02 00 00 00 00 00 00

    2. 源码

    代码在VS2012编译测试通过,rhashlib采用的协议是MIT。

    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>
    #include <assert.h>
    
    //字节序的小头和大头的问题
    #define ZEN_LITTLE_ENDIAN  0x0123
    #define ZEN_BIG_ENDIAN     0x3210
    
    //目前所有的代码都是为了小头党服务的,不知道有生之年这套代码是否还会为大头党服务一次?
    #ifndef ZEN_BYTES_ORDER
    #define ZEN_BYTES_ORDER    ZEN_LITTLE_ENDIAN
    #endif
    
    #ifndef ZEN_SWAP_UINT16
    #define ZEN_SWAP_UINT16(x)  ((((x) & 0xff00) >>  8) | (((x) & 0x00ff) <<  8))
    #endif
    #ifndef ZEN_SWAP_UINT32
    #define ZEN_SWAP_UINT32(x)  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
        (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
    #endif
    #ifndef ZEN_SWAP_UINT64
    #define ZEN_SWAP_UINT64(x)  ((((x) & 0xff00000000000000) >> 56) | (((x) & 0x00ff000000000000) >>  40) | \
        (((x) & 0x0000ff0000000000) >> 24) | (((x) & 0x000000ff00000000) >>  8) | \
        (((x) & 0x00000000ff000000) << 8 ) | (((x) & 0x0000000000ff0000) <<  24) | \
        (((x) & 0x000000000000ff00) << 40 ) | (((x) & 0x00000000000000ff) <<  56))
    #endif
    
    //将一个(字符串)数组,拷贝到另外一个uint32_t数组,同时每个uint32_t反字节序
    void *swap_uint32_memcpy(void *to, const void *from, size_t length)
    {
        memcpy(to, from, length);
        size_t remain_len =  (4 - (length & 3)) & 3;
    
        //数据不是4字节的倍数,补充0
        if (remain_len)
        {
            for (size_t i = 0; i < remain_len; ++i)
            {
                *((char *)(to) + length + i) = 0;
            }
            //调整成4的倍数
            length += remain_len;
        }
    
        //所有的数据反转
        for (size_t i = 0; i < length / 4; ++i)
        {
            ((uint32_t *)to)[i] = ZEN_SWAP_UINT32(((uint32_t *)to)[i]);
        }
    
        return to;
    }
    
    ///MD5的结果数据长度
    static const size_t ZEN_MD5_HASH_SIZE   = 16;
    ///SHA1的结果数据长度
    static const size_t ZEN_SHA1_HASH_SIZE  = 20;
    
    
    
    namespace ZEN_LIB
    {
    
    
    /*!
    @brief      求某个内存块的MD5,
    @return     unsigned char* 返回的的结果,
    @param[in]  buf    求MD5的内存BUFFER指针
    @param[in]  size   BUFFER长度
    @param[out] result 结果
    */
    unsigned char *md5(const unsigned char *buf,
                       size_t size,
                       unsigned char result[ZEN_MD5_HASH_SIZE]);
    
    
    /*!
    @brief      求内存块BUFFER的SHA1值
    @return     unsigned char* 返回的的结果
    @param[in]  buf    求SHA1的内存BUFFER指针
    @param[in]  size   BUFFER长度
    @param[out] result 结果
    */
    unsigned char *sha1(const unsigned char *buf,
                        size_t size,
                        unsigned char result[ZEN_SHA1_HASH_SIZE]);
    };
    
    
    //================================================================================================
    //MD5的算法
    
    //每次处理的BLOCK的大小
    static const size_t ZEN_MD5_BLOCK_SIZE = 64;
    
    //md5算法的上下文,保存一些状态,中间数据,结果
    typedef struct md5_ctx
    {
        //处理的数据的长度
        uint64_t length_;
        //还没有处理的数据长度
        uint64_t unprocessed_;
        //取得的HASH结果(中间数据)
        uint32_t  hash_[4];
    } md5_ctx;
    
    
    #define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n))))
    #define ROTR32(dword, n) ((dword) >> (n) ^ ((dword) << (32 - (n))))
    #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n))))
    #define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n))))
    
    
    /*!
    @brief      内部函数,初始化MD5的context,内容
    @param      ctx
    */
    static void zen_md5_init(md5_ctx *ctx)
    {
        ctx->length_ = 0;
        ctx->unprocessed_ = 0;
    
        /* initialize state */
        ctx->hash_[0] = 0x67452301;
        ctx->hash_[1] = 0xefcdab89;
        ctx->hash_[2] = 0x98badcfe;
        ctx->hash_[3] = 0x10325476;
    }
    
    /* First, define four auxiliary functions that each take as input
     * three 32-bit words and returns a 32-bit word.*/
    
    /* F(x,y,z) = ((y XOR z) AND x) XOR z - is faster then original version */
    #define MD5_F(x, y, z) ((((y) ^ (z)) & (x)) ^ (z))
    #define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
    #define MD5_H(x, y, z) ((x) ^ (y) ^ (z))
    #define MD5_I(x, y, z) ((y) ^ ((x) | (~z)))
    
    /* transformations for rounds 1, 2, 3, and 4. */
    #define MD5_ROUND1(a, b, c, d, x, s, ac) { \
            (a) += MD5_F((b), (c), (d)) + (x) + (ac); \
            (a) = ROTL32((a), (s)); \
            (a) += (b); \
        }
    #define MD5_ROUND2(a, b, c, d, x, s, ac) { \
            (a) += MD5_G((b), (c), (d)) + (x) + (ac); \
            (a) = ROTL32((a), (s)); \
            (a) += (b); \
        }
    #define MD5_ROUND3(a, b, c, d, x, s, ac) { \
            (a) += MD5_H((b), (c), (d)) + (x) + (ac); \
            (a) = ROTL32((a), (s)); \
            (a) += (b); \
        }
    #define MD5_ROUND4(a, b, c, d, x, s, ac) { \
            (a) += MD5_I((b), (c), (d)) + (x) + (ac); \
            (a) = ROTL32((a), (s)); \
            (a) += (b); \
        }
    
    
    /*!
    @brief      内部函数,将64个字节,16个uint32_t的数组进行摘要(杂凑)处理,处理的数据自己序是小头数据
    @param      state 存放处理的hash数据结果
    @param      block 要处理的block,64个字节,16个uint32_t的数组
    */
    static void zen_md5_process_block(uint32_t state[4], const uint32_t block[ZEN_MD5_BLOCK_SIZE / 4])
    {
        register unsigned a, b, c, d;
        a = state[0];
        b = state[1];
        c = state[2];
        d = state[3];
    
        const uint32_t *x = NULL;
    
        //MD5里面计算的数据都是小头数据.大头党的数据要处理
    #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
        x = block;
    #else
        uint32_t swap_block[ZEN_MD5_BLOCK_SIZE / 4];
        swap_uint32_memcpy(swap_block, block, 64);
        x = swap_block;
    #endif
    
    
        MD5_ROUND1(a, b, c, d, x[ 0],  7, 0xd76aa478);
        MD5_ROUND1(d, a, b, c, x[ 1], 12, 0xe8c7b756);
        MD5_ROUND1(c, d, a, b, x[ 2], 17, 0x242070db);
        MD5_ROUND1(b, c, d, a, x[ 3], 22, 0xc1bdceee);
        MD5_ROUND1(a, b, c, d, x[ 4],  7, 0xf57c0faf);
        MD5_ROUND1(d, a, b, c, x[ 5], 12, 0x4787c62a);
        MD5_ROUND1(c, d, a, b, x[ 6], 17, 0xa8304613);
        MD5_ROUND1(b, c, d, a, x[ 7], 22, 0xfd469501);
        MD5_ROUND1(a, b, c, d, x[ 8],  7, 0x698098d8);
        MD5_ROUND1(d, a, b, c, x[ 9], 12, 0x8b44f7af);
        MD5_ROUND1(c, d, a, b, x[10], 17, 0xffff5bb1);
        MD5_ROUND1(b, c, d, a, x[11], 22, 0x895cd7be);
        MD5_ROUND1(a, b, c, d, x[12],  7, 0x6b901122);
        MD5_ROUND1(d, a, b, c, x[13], 12, 0xfd987193);
        MD5_ROUND1(c, d, a, b, x[14], 17, 0xa679438e);
        MD5_ROUND1(b, c, d, a, x[15], 22, 0x49b40821);
    
        MD5_ROUND2(a, b, c, d, x[ 1],  5, 0xf61e2562);
        MD5_ROUND2(d, a, b, c, x[ 6],  9, 0xc040b340);
        MD5_ROUND2(c, d, a, b, x[11], 14, 0x265e5a51);
        MD5_ROUND2(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);
        MD5_ROUND2(a, b, c, d, x[ 5],  5, 0xd62f105d);
        MD5_ROUND2(d, a, b, c, x[10],  9,  0x2441453);
        MD5_ROUND2(c, d, a, b, x[15], 14, 0xd8a1e681);
        MD5_ROUND2(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);
        MD5_ROUND2(a, b, c, d, x[ 9],  5, 0x21e1cde6);
        MD5_ROUND2(d, a, b, c, x[14],  9, 0xc33707d6);
        MD5_ROUND2(c, d, a, b, x[ 3], 14, 0xf4d50d87);
        MD5_ROUND2(b, c, d, a, x[ 8], 20, 0x455a14ed);
        MD5_ROUND2(a, b, c, d, x[13],  5, 0xa9e3e905);
        MD5_ROUND2(d, a, b, c, x[ 2],  9, 0xfcefa3f8);
        MD5_ROUND2(c, d, a, b, x[ 7], 14, 0x676f02d9);
        MD5_ROUND2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
    
        MD5_ROUND3(a, b, c, d, x[ 5],  4, 0xfffa3942);
        MD5_ROUND3(d, a, b, c, x[ 8], 11, 0x8771f681);
        MD5_ROUND3(c, d, a, b, x[11], 16, 0x6d9d6122);
        MD5_ROUND3(b, c, d, a, x[14], 23, 0xfde5380c);
        MD5_ROUND3(a, b, c, d, x[ 1],  4, 0xa4beea44);
        MD5_ROUND3(d, a, b, c, x[ 4], 11, 0x4bdecfa9);
        MD5_ROUND3(c, d, a, b, x[ 7], 16, 0xf6bb4b60);
        MD5_ROUND3(b, c, d, a, x[10], 23, 0xbebfbc70);
        MD5_ROUND3(a, b, c, d, x[13],  4, 0x289b7ec6);
        MD5_ROUND3(d, a, b, c, x[ 0], 11, 0xeaa127fa);
        MD5_ROUND3(c, d, a, b, x[ 3], 16, 0xd4ef3085);
        MD5_ROUND3(b, c, d, a, x[ 6], 23,  0x4881d05);
        MD5_ROUND3(a, b, c, d, x[ 9],  4, 0xd9d4d039);
        MD5_ROUND3(d, a, b, c, x[12], 11, 0xe6db99e5);
        MD5_ROUND3(c, d, a, b, x[15], 16, 0x1fa27cf8);
        MD5_ROUND3(b, c, d, a, x[ 2], 23, 0xc4ac5665);
    
        MD5_ROUND4(a, b, c, d, x[ 0],  6, 0xf4292244);
        MD5_ROUND4(d, a, b, c, x[ 7], 10, 0x432aff97);
        MD5_ROUND4(c, d, a, b, x[14], 15, 0xab9423a7);
        MD5_ROUND4(b, c, d, a, x[ 5], 21, 0xfc93a039);
        MD5_ROUND4(a, b, c, d, x[12],  6, 0x655b59c3);
        MD5_ROUND4(d, a, b, c, x[ 3], 10, 0x8f0ccc92);
        MD5_ROUND4(c, d, a, b, x[10], 15, 0xffeff47d);
        MD5_ROUND4(b, c, d, a, x[ 1], 21, 0x85845dd1);
        MD5_ROUND4(a, b, c, d, x[ 8],  6, 0x6fa87e4f);
        MD5_ROUND4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
        MD5_ROUND4(c, d, a, b, x[ 6], 15, 0xa3014314);
        MD5_ROUND4(b, c, d, a, x[13], 21, 0x4e0811a1);
        MD5_ROUND4(a, b, c, d, x[ 4],  6, 0xf7537e82);
        MD5_ROUND4(d, a, b, c, x[11], 10, 0xbd3af235);
        MD5_ROUND4(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);
        MD5_ROUND4(b, c, d, a, x[ 9], 21, 0xeb86d391);
    
        state[0] += a;
        state[1] += b;
        state[2] += c;
        state[3] += d;
    }
    
    
    /*!
    @brief      内部函数,处理数据的前面部分(>64字节的部分),每次组成一个64字节的block就进行杂凑处理
    @param[out] ctx  算法的context,用于记录一些处理的上下文和结果
    @param[in]  buf  处理的数据,
    @param[in]  size 处理的数据长度
    */
    static void zen_md5_update(md5_ctx *ctx, const unsigned char *buf, size_t size)
    {
        //为什么不是=,因为在某些环境下,可以多次调用zen_md5_update,但这种情况,必须保证前面的调用,每次都没有unprocessed_
        ctx->length_ += size;
    
        //每个处理的块都是64字节
        while (size >= ZEN_MD5_BLOCK_SIZE)
        {
            zen_md5_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf));
            buf  += ZEN_MD5_BLOCK_SIZE;
            size -= ZEN_MD5_BLOCK_SIZE;
        }
    
        ctx->unprocessed_ = size;
    }
    
    
    /*!
    @brief      内部函数,处理数据的末尾部分,我们要拼出最后1个(或者两个)要处理的BLOCK,加上0x80,加上长度进行处理
    @param[in]  ctx    算法的context,用于记录一些处理的上下文和结果
    @param[in]  buf    处理的数据
    @param[in]  size   处理buffer的长度
    @param[out] result 返回的结果,
    */
    static void zen_md5_final(md5_ctx *ctx, const unsigned char *buf, size_t size, unsigned char *result)
    {
        uint32_t message[ZEN_MD5_BLOCK_SIZE / 4];
    
        //保存剩余的数据,我们要拼出最后1个(或者两个)要处理的块,前面的算法保证了,最后一个块肯定小于64个字节
        if (ctx->unprocessed_)
        {
            memcpy(message, buf + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_));
        }
    
        //得到0x80要添加在的位置(在uint32_t 数组中),
        uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2;
        uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8;
    
        //添加0x80进去,并且把余下的空间补充0
        message[index]   &= ~(0xFFFFFFFF << shift);
        message[index++] ^= 0x80 << shift;
    
        //如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个block
        if (index > 14)
        {
            while (index < 16)
            {
                message[index++] = 0;
            }
    
            zen_md5_process_block(ctx->hash_, message);
            index = 0;
        }
    
        //补0
        while (index < 14)
        {
            message[index++] = 0;
        }
    
        //保存长度,注意是bit位的长度,这个问题让我看着郁闷了半天,
        uint64_t data_len = (ctx->length_) << 3;
    
        //注意MD5算法要求的64bit的长度是小头LITTLE-ENDIAN编码,注意下面的比较是!=
    #if ZEN_BYTES_ORDER != ZEN_LITTLE_ENDIAN
        data_len = ZEN_SWAP_UINT64(data_len);
    #endif
    
        message[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF);
        message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32);
    
        zen_md5_process_block(ctx->hash_, message);
    
        //注意结果是小头党的,在大头的世界要进行转换
    #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
        memcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE);
    #else
        swap_uint32_memcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE);
    #endif
    
    }
    
    
    //计算一个内存数据的MD5值
    unsigned char *ZEN_LIB::md5(const unsigned char *buf,
                                size_t size,
                                unsigned char result[ZEN_MD5_HASH_SIZE])
    {
        assert(result != NULL);
    
        md5_ctx ctx;
        zen_md5_init(&ctx);
        zen_md5_update(&ctx, buf, size);
        zen_md5_final(&ctx, buf, size, result);
        return result;
    }
    
    
    
    
    //================================================================================================
    //SHA1的算法
    
    //每次处理的BLOCK的大小
    static const size_t ZEN_SHA1_BLOCK_SIZE = 64;
    
    //SHA1算法的上下文,保存一些状态,中间数据,结果
    typedef struct sha1_ctx
    {
    
        //处理的数据的长度
        uint64_t length_;
        //还没有处理的数据长度
        uint64_t unprocessed_;
        /* 160-bit algorithm internal hashing state */
        uint32_t hash_[5];
    } sha1_ctx;
    
    //内部函数,SHA1算法的上下文的初始化
    static void zen_sha1_init(sha1_ctx *ctx)
    {
        ctx->length_ = 0;
        ctx->unprocessed_ = 0;
        // 初始化算法的几个常量,魔术数
        ctx->hash_[0] = 0x67452301;
        ctx->hash_[1] = 0xefcdab89;
        ctx->hash_[2] = 0x98badcfe;
        ctx->hash_[3] = 0x10325476;
        ctx->hash_[4] = 0xc3d2e1f0;
    }
    
    
    /*!
    @brief      内部函数,对一个64bit内存块进行摘要(杂凑)处理,
    @param      hash  存放计算hash结果的的数组
    @param      block 要计算的处理得内存块
    */
    static void zen_sha1_process_block(uint32_t hash[5],
                                       const uint32_t block[ZEN_SHA1_BLOCK_SIZE / 4])
    {
        size_t        t;
        uint32_t      wblock[80];
        register uint32_t      a, b, c, d, e, temp;
    
        //SHA1算法处理的内部数据要求是大头党的,在小头的环境转换
    #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
        swap_uint32_memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
    #else
        ::memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
    #endif
    
        //处理
        for (t = 16; t < 80; t++)
        {
            wblock[t] = ROTL32(wblock[t - 3] ^ wblock[t - 8] ^ wblock[t - 14] ^ wblock[t - 16], 1);
        }
    
        a = hash[0];
        b = hash[1];
        c = hash[2];
        d = hash[3];
        e = hash[4];
    
        for (t = 0; t < 20; t++)
        {
            /* the following is faster than ((B & C) | ((~B) & D)) */
            temp =  ROTL32(a, 5) + (((c ^ d) & b) ^ d)
                    + e + wblock[t] + 0x5A827999;
            e = d;
            d = c;
            c = ROTL32(b, 30);
            b = a;
            a = temp;
        }
    
        for (t = 20; t < 40; t++)
        {
            temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0x6ED9EBA1;
            e = d;
            d = c;
            c = ROTL32(b, 30);
            b = a;
            a = temp;
        }
    
        for (t = 40; t < 60; t++)
        {
            temp = ROTL32(a, 5) + ((b & c) | (b & d) | (c & d))
                   + e + wblock[t] + 0x8F1BBCDC;
            e = d;
            d = c;
            c = ROTL32(b, 30);
            b = a;
            a = temp;
        }
    
        for (t = 60; t < 80; t++)
        {
            temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0xCA62C1D6;
            e = d;
            d = c;
            c = ROTL32(b, 30);
            b = a;
            a = temp;
        }
    
        hash[0] += a;
        hash[1] += b;
        hash[2] += c;
        hash[3] += d;
        hash[4] += e;
    }
    
    
    /*!
    @brief      内部函数,处理数据的前面部分(>64字节的部分),每次组成一个64字节的block就进行杂凑处理
    @param      ctx  算法的上下文,记录中间数据,结果等
    @param      msg  要进行计算的数据buffer
    @param      size 长度
    */
    static void zen_sha1_update(sha1_ctx *ctx,
                                const unsigned char *buf, 
                                size_t size)
    {
        //为了让zen_sha1_update可以多次进入,长度可以累计
        ctx->length_ += size;
    
        //每个处理的块都是64字节
        while (size >= ZEN_SHA1_BLOCK_SIZE)
        {
            zen_sha1_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf));
            buf  += ZEN_SHA1_BLOCK_SIZE;
            size -= ZEN_SHA1_BLOCK_SIZE;
        }
    
        ctx->unprocessed_ = size;
    }
    
    
    /*!
    @brief      内部函数,处理数据的最后部分,添加0x80,补0,增加长度信息
    @param      ctx    算法的上下文,记录中间数据,结果等
    @param      msg    要进行计算的数据buffer
    @param      result 返回的结果
    */
    static void zen_sha1_final(sha1_ctx *ctx, 
                               const unsigned char *msg,
                               size_t size, 
                               unsigned char *result)
    {
    
        uint32_t message[ZEN_SHA1_BLOCK_SIZE / 4];
    
        //保存剩余的数据,我们要拼出最后1个(或者两个)要处理的块,前面的算法保证了,最后一个块肯定小于64个字节
        if (ctx->unprocessed_)
        {
            memcpy(message, msg + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_));
        }
    
        //得到0x80要添加在的位置(在uint32_t 数组中),
        uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2;
        uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8;
    
        //添加0x80进去,并且把余下的空间补充0
        message[index]   &= ~(0xFFFFFFFF << shift);
        message[index++] ^= 0x80 << shift;
    
        //如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个block
        if (index > 14)
        {
            while (index < 16)
            {
                message[index++] = 0;
            }
    
            zen_sha1_process_block(ctx->hash_, message);
            index = 0;
        }
    
        //补0
        while (index < 14)
        {
            message[index++] = 0;
        }
    
        //保存长度,注意是bit位的长度,这个问题让我看着郁闷了半天,
        uint64_t data_len = (ctx->length_) << 3;
    
        //注意SHA1算法要求的64bit的长度是大头BIG-ENDIAN,在小头的世界要进行转换
    #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
        data_len = ZEN_SWAP_UINT64(data_len);
    #endif
    
        message[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF);
        message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32);
    
        zen_sha1_process_block(ctx->hash_, message);
    
        //注意结果是大头党的,在小头的世界要进行转换
    #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
        swap_uint32_memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
    #else
        memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
    #endif
    }
    
    
    
    //计算一个内存数据的SHA1值
    unsigned char *ZEN_LIB::sha1(const unsigned char *msg,
                                 size_t size,
                                 unsigned char result[ZEN_SHA1_HASH_SIZE])
    {
        assert(result != NULL);
    
        sha1_ctx ctx;
        zen_sha1_init(&ctx);
        zen_sha1_update(&ctx, msg, size);
        zen_sha1_final(&ctx, msg, size, result);
        return result;
    }
    
    int main(int /*argc*/, char * /*argv*/[])
    {
    
        int ret = 0;
        static unsigned char test_buf[7][81] =
        {
            { "" }, 
            { "a" },
            { "abc" },
            { "message digest" },
            { "abcdefghijklmnopqrstuvwxyz" },
            { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" },
            { "12345678901234567890123456789012345678901234567890123456789012345678901234567890" }
        };
    
        static const size_t test_buflen[7] =
        {
            0, 1, 3, 14, 26, 62, 80
        };
    
        static const unsigned char md5_test_sum[7][16] =
        {
            { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,  0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E },
            { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8,  0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 },
            { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0,  0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 },
            { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D,  0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 },
            { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00,  0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B },
            { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5,  0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F },
            { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55,  0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A }
        };
        unsigned char result[32] ={0};
    
        for(size_t i=0;i<7;++i)
        {
            ZEN_LIB::md5(test_buf[i],test_buflen[i],result);
            ret = memcmp(result,md5_test_sum[i],16);
            if (ret != 0)
            {
                assert(false);
            }
        }
    
        static const unsigned char sha1_test_sum[7][20] =
        {
            { 0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09 },
            { 0x86,0xf7,0xe4,0x37,0xfa,0xa5,0xa7,0xfc,0xe1,0x5d,0x1d,0xdc,0xb9,0xea,0xea,0xea,0x37,0x76,0x67,0xb8 },
            { 0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d },
            { 0xc1,0x22,0x52,0xce,0xda,0x8b,0xe8,0x99,0x4d,0x5f,0xa0,0x29,0x0a,0x47,0x23,0x1c,0x1d,0x16,0xaa,0xe3 },
            { 0x32,0xd1,0x0c,0x7b,0x8c,0xf9,0x65,0x70,0xca,0x04,0xce,0x37,0xf2,0xa1,0x9d,0x84,0x24,0x0d,0x3a,0x89 },
            { 0x76,0x1c,0x45,0x7b,0xf7,0x3b,0x14,0xd2,0x7e,0x9e,0x92,0x65,0xc4,0x6f,0x4b,0x4d,0xda,0x11,0xf9,0x40 },
            { 0x50,0xab,0xf5,0x70,0x6a,0x15,0x09,0x90,0xa0,0x8b,0x2c,0x5e,0xa4,0x0f,0xa0,0xe5,0x85,0x55,0x47,0x32 },
        };
        for(size_t i=0;i<7;++i)
        {
            ZEN_LIB::sha1(test_buf[i],test_buflen[i],result);
            ret = memcmp(result,sha1_test_sum[i],20);
            if (ret != 0)
            {
                assert(false);
            }
        }
        return 0;
    }

    转载自http://www.cnblogs.com/fullsail/

    展开全文
  • 一、MD5算法的实现 MD5算法简述: MD5是输入不定長度信息,输出固定長度128-bits的演算法。 经过程序流程,生成四个32位数据,最后联合起来成为一个128-bits散列。 基本方式为,求余、取余、调整长度、...
  • 了解哪些常用的加密算法?能简单介绍一种吗? 内推军P186 P192 1.分类 加密算法首先分为两种:单向加密、双向加密。 单向加密是不可逆的,也就是只能加密,不能解密。通常用来传输类似用户名和密码,直接将加密...
  • MD5算法java篇

    2014-09-13 12:25:11
    MD5算法java篇
  • MD5算法原理的说明

    2019-03-01 16:47:28
    测试: zhong7758521 : 50945d41c5b4f365296a68ea416dc2c7 UJRdQcW082UpamjqQW3Cxw== 博文链接:https://graduate.iteye.com/blog/239817
  • MD5算法的JAVA实现

    2014-11-18 16:37:25
    这是一个MD5算法的JAVA实现,将对应的包名修改一下即可
  • MD5算法分析及逆向详解

    千次阅读 2016-10-08 09:16:24
    题外话:最近在看加密与解密,看到加密算法部分,感觉对于初次接触的新手还是有些难度的。故写下该篇文章,算作一个引导吧,新手飘过,老鸟勿笑。 基本原理:  MD5的典型应用是对一段信息(Message)产生信息...
  • 主要介绍了详解Java中实现SHA1与MD5加密算法的基本方法,安全哈希算法第一版和消息摘要算法第五版也是通常人们最常用的加密算法,需要的朋友可以参考下
  • MD5 摘要算法应用详解

    千次阅读 2020-06-23 23:37:32
    MD算法家族针对源数据会产生一个128位的消息摘要。在一般应用场景下,会将128二进制摘要信息转换为十六进制,可以得到一个32位的十六进制字符串。   MD5的发展经历了MD2、MD4系列算法:   · MD2:1989年,RSA的...
  • MD5算法的实现详解

    千次阅读 2017-07-28 18:47:37
    加密算法肯定有对应的解密算法,但MD5没有,所以不能称之为加密。接下来我们就来聊聊MD5的具体实现。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,285
精华内容 5,714
关键字:

md5算法详解