md5_md5加密 - CSDN
md5 订阅
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。 展开全文
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
信息
别    称
摘要算法
提出者
罗纳德·李维斯特
应用学科
信息技术,计算机科学
中文名
消息摘要算法
外文名
Message Digest Algorithm MD5
提出时间
1992年
MD5发展历史
1992年8月,罗纳德·李维斯特向互联网工程任务组(IETF)提交了一份重要文件,描述了这种算法的原理。由于这种算法的公开性和安全性,在90年代被广泛使用在各种程序语言中,用以确保资料传递无误等 [1]  。MD5由MD4、MD3、MD2改进而来,主要增强算法复杂度和不可逆性。MD5算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的加密保护领域 [2]  。Rivest在1989年开发出MD2算法 [3]  。在这个算法中,首先对信息进行数据补位,使信息的字节长度是16的倍数。然后,以一个16位的校验和追加到信息末尾,并且根据这个新产生的信息计算出散列值。后来,Rogier和Chauvaud发现如果忽略了校验和MD2将产生冲突。MD2算法加密后结果是唯一的(即不同信息加密后的结果不同) [4]  。为了加强算法的安全性,Rivest在1990年又开发出MD4算法 [3]  。MD4算法同样需要填补信息以确保信息的比特位长度减去448后能被512整除(信息比特位长度mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damgard/merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。Den boer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到MD4完整版本中的冲突(这个冲突实际上是一种漏洞,它将导致对不同的内容进行加密却可能得到相同的加密后结果) [5]  。1991年,Rivest开发出技术上更为趋近成熟的MD5算法。它在MD4的基础上增加了"安全带"(safety-belts)的概念。虽然MD5比MD4复杂度大一些,但却更为安全。这个算法很明显的由四个和MD4设计有少许不同的步骤组成。在MD5算法中,信息-摘要的大小和填充的必要条件与MD4完全相同。Den boer和Bosselaers曾发现MD5算法中的假冲突(pseudo-collisions),但除此之外就没有其他被发现的加密后结果了 [3]  。
收起全文
  • MD5算法原理及其实现

    万次阅读 多人点赞 2017-01-16 00:44:05
    MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值(hash value),用于确保信息传输完整一致。

    什么是MD5算法

    MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值(hash value),用于确保信息传输完整一致。

    MD5功能

    输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
    不同的输入得到的不同的结果(唯一性);

    MD5属不属于加密算法

    认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有解密算法,所以这部分人认为MD5只能属于算法,不能称为加密算法;
    认为属于的人是因为他们觉得经过MD5处理后看不到原文,即已经将原文加密,所以认为MD5属于加密算法;我个人支持前者,正如认为BASE64算法只能算编码一样。

    MD5算法是否可逆?

    MD5不可逆的原因是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

    不过有个地方值得指出的是,一个MD5理论上的确是可能对应无数多个原文的,因为MD5是有限多个的而原文可以是无数多个。比如主流使用的MD5将任意长度的“字节串映射为一个128bit的大整数。也就是一共有2^128种可能,大概是3.4*10^38,这个数字是有限多个的,而但是世界上可以被用来加密的原文则会有无数的可能性。

    不过需要注意的一点是,尽量这是一个理论上的有限对无限,不过问题是这个无限在现实生活中并不完全成立,因为一方面现实中原文的长度往往是有限的(以常用的密码为例,一般人都在20位以内),另一方面目前想要发现两段原文对应同一个MD5(专业的说这叫杂凑冲撞)值非常困难,因此某种意义上来说,在一定范围内想构建MD5值与原文的一一对应关系是完全有可能的。所以对于MD5目前最有效的攻击方式就是彩虹表,具体详情你可以通过谷歌了解。

    MD5相当于超损压缩。

    MD5用途

    1.防止被篡改:
    1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。
    2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。
    3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

    2.防止直接看到明文:
    现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

    3.防止抵赖(数字签名):
    这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。

    MD5安全性

    普遍认为MD5是很安全,因为暴力破解的时间是一般人无法接受的。实际上如果把用户的密码MD5处理后再存储到数据库,其实是很不安全的。因为用户的密码是比较短的,而且很多用户的密码都使用生日,手机号码,身份证号码,电话号码等等。或者使用常用的一些吉利的数字,或者某个英文单词。如果我把常用的密码先MD5处理,把数据存储起来,然后再跟你的MD5结果匹配,这时我就有可能得到明文。比如某个MD5破解网站http://www.cmd5.com/default.aspx,所以现在大多数网站密码的策略是强制要求用户使用数字大小写字母的组合的方式提高用户密码的安全度。

    MD5算法过程

    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

    第一步、填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);

    第二步、记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N*512+448+64=(N+1)*512位。

    第三步、装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567)16,B=(89ABCDEF)16,C=(FEDCBA98)16,D=(76543210)16)。如果在程序中定义应该是:
    (A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点晕哈,其实想一想就明白了。

    第四步、四轮循环运算:循环的次数是分组的个数(N+1)

    1)将每一512字节细分成16个小组,每个小组64位(8个字节)

    2)先认识四个线性函数(&是与,|是或,~是非,^是异或)

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

    3)设Mj表示消息的第j个子分组(从0到15),<<

    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)

    4)四轮运算

     第一轮
    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)

    5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环。

    如果上面的过程用JAVA代码来实现的话,代码如下:

    public class MD5 {
    
        static final String hexs[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
        //标准的幻数
        private static final long A=0x67452301L;
        private static final long B=0xefcdab89L;
        private static final long C=0x98badcfeL;
        private static final long D=0x10325476L;
    
    
        //下面这些S11-S44实际上是一个4*4的矩阵,在四轮循环运算中用到
        static final int S11 = 7;
        static final int S12 = 12;
        static final int S13 = 17;
        static final int S14 = 22;
    
        static final int S21 = 5;
        static final int S22 = 9;
        static final int S23 = 14;
        static final int S24 = 20;
    
        static final int S31 = 4;
        static final int S32 = 11;
        static final int S33 = 16;
        static final int S34 = 23;
    
        static final int S41 = 6;
        static final int S42 = 10;
        static final int S43 = 15;
        static final int S44 = 21;
    
        //java不支持无符号的基本数据(unsigned)
        private long [] result={A,B,C,D};//存储hash结果,共4×32=128位,初始化值为(幻数的级联)
    
        public static void main(String []args){
            MD5 md=new MD5();
            System.out.println("md5(abc)="+md.digest("abc"));
        }
    
        private String digest(String inputStr){
            byte [] inputBytes=inputStr.getBytes();
            int byteLen=inputBytes.length;//长度(字节)
            int groupCount=0;//完整分组的个数
            groupCount=byteLen/64;//每组512位(64字节)
            long []groups=null;//每个小组(64字节)再细分后的16个小组(4字节)
    
            //处理每一个完整 分组
            for(int step=0;step<groupCount;step++){
                groups=divGroup(inputBytes,step*64);
                trans(groups);//处理分组,核心算法
            }
    
            //处理完整分组后的尾巴
            int rest=byteLen%64;//512位分组后的余数
            byte [] tempBytes=new byte[64];
            if(rest<=56){
                for(int i=0;i<rest;i++)
                    tempBytes[i]=inputBytes[byteLen-rest+i];
                if(rest<56){
                    tempBytes[rest]=(byte)(1<<7);
                    for(int i=1;i<56-rest;i++)
                        tempBytes[rest+i]=0;
                }
                long len=(long)(byteLen<<3);
                for(int i=0;i<8;i++){
                    tempBytes[56+i]=(byte)(len&0xFFL);
                    len=len>>8;
                }
                groups=divGroup(tempBytes,0);
                trans(groups);//处理分组
            }else{
                for(int i=0;i<rest;i++)
                    tempBytes[i]=inputBytes[byteLen-rest+i];
                tempBytes[rest]=(byte)(1<<7);
                for(int i=rest+1;i<64;i++)
                    tempBytes[i]=0;
                groups=divGroup(tempBytes,0);
                trans(groups);//处理分组
    
                for(int i=0;i<56;i++)
                    tempBytes[i]=0;
                long len=(long)(byteLen<<3);
                for(int i=0;i<8;i++){
                    tempBytes[56+i]=(byte)(len&0xFFL);
                    len=len>>8;
                }
                groups=divGroup(tempBytes,0);
                trans(groups);//处理分组
            }
    
            //将Hash值转换成十六进制的字符串
            String resStr="";
            long temp=0;
            for(int i=0;i<4;i++){
                for(int j=0;j<4;j++){
                    temp=result[i]&0x0FL;
                    String a=hexs[(int)(temp)];
                    result[i]=result[i]>>4;
                    temp=result[i]&0x0FL;
                    resStr+=hexs[(int)(temp)]+a;
                    result[i]=result[i]>>4;
                }
            }
            return resStr;
        }
    
        /**
         * 从inputBytes的index开始取512位,作为新的分组
         * 将每一个512位的分组再细分成16个小组,每个小组64位(8个字节)
         * @param inputBytes
         * @param index
         * @return
         */
        private static long[] divGroup(byte[] inputBytes,int index){
            long [] temp=new long[16];
            for(int i=0;i<16;i++){
                temp[i]=b2iu(inputBytes[4*i+index])|
                    (b2iu(inputBytes[4*i+1+index]))<<8|
                    (b2iu(inputBytes[4*i+2+index]))<<16|
                    (b2iu(inputBytes[4*i+3+index]))<<24;
            }
            return temp;
        }
    
        /**
         * 这时不存在符号位(符号位存储不再是代表正负),所以需要处理一下
         * @param b
         * @return
         */
        public static long b2iu(byte b){
            return b < 0 ? b & 0x7F + 128 : b;
         }
    
        /**
         * 主要的操作,四轮循环
         * @param groups[]--每一个分组512位(64字节)
         */
        private void trans(long[] groups) {
            long a = result[0], b = result[1], c = result[2], d = result[3];
            /*第一轮*/
            a = FF(a, b, c, d, groups[0], S11, 0xd76aa478L); /* 1 */
            d = FF(d, a, b, c, groups[1], S12, 0xe8c7b756L); /* 2 */
            c = FF(c, d, a, b, groups[2], S13, 0x242070dbL); /* 3 */
            b = FF(b, c, d, a, groups[3], S14, 0xc1bdceeeL); /* 4 */
            a = FF(a, b, c, d, groups[4], S11, 0xf57c0fafL); /* 5 */
            d = FF(d, a, b, c, groups[5], S12, 0x4787c62aL); /* 6 */
            c = FF(c, d, a, b, groups[6], S13, 0xa8304613L); /* 7 */
            b = FF(b, c, d, a, groups[7], S14, 0xfd469501L); /* 8 */
            a = FF(a, b, c, d, groups[8], S11, 0x698098d8L); /* 9 */
            d = FF(d, a, b, c, groups[9], S12, 0x8b44f7afL); /* 10 */
            c = FF(c, d, a, b, groups[10], S13, 0xffff5bb1L); /* 11 */
            b = FF(b, c, d, a, groups[11], S14, 0x895cd7beL); /* 12 */
            a = FF(a, b, c, d, groups[12], S11, 0x6b901122L); /* 13 */
            d = FF(d, a, b, c, groups[13], S12, 0xfd987193L); /* 14 */
            c = FF(c, d, a, b, groups[14], S13, 0xa679438eL); /* 15 */
            b = FF(b, c, d, a, groups[15], S14, 0x49b40821L); /* 16 */
    
            /*第二轮*/
            a = GG(a, b, c, d, groups[1], S21, 0xf61e2562L); /* 17 */
            d = GG(d, a, b, c, groups[6], S22, 0xc040b340L); /* 18 */
            c = GG(c, d, a, b, groups[11], S23, 0x265e5a51L); /* 19 */
            b = GG(b, c, d, a, groups[0], S24, 0xe9b6c7aaL); /* 20 */
            a = GG(a, b, c, d, groups[5], S21, 0xd62f105dL); /* 21 */
            d = GG(d, a, b, c, groups[10], S22, 0x2441453L); /* 22 */
            c = GG(c, d, a, b, groups[15], S23, 0xd8a1e681L); /* 23 */
            b = GG(b, c, d, a, groups[4], S24, 0xe7d3fbc8L); /* 24 */
            a = GG(a, b, c, d, groups[9], S21, 0x21e1cde6L); /* 25 */
            d = GG(d, a, b, c, groups[14], S22, 0xc33707d6L); /* 26 */
            c = GG(c, d, a, b, groups[3], S23, 0xf4d50d87L); /* 27 */
            b = GG(b, c, d, a, groups[8], S24, 0x455a14edL); /* 28 */
            a = GG(a, b, c, d, groups[13], S21, 0xa9e3e905L); /* 29 */
            d = GG(d, a, b, c, groups[2], S22, 0xfcefa3f8L); /* 30 */
            c = GG(c, d, a, b, groups[7], S23, 0x676f02d9L); /* 31 */
            b = GG(b, c, d, a, groups[12], S24, 0x8d2a4c8aL); /* 32 */
    
            /*第三轮*/
            a = HH(a, b, c, d, groups[5], S31, 0xfffa3942L); /* 33 */
            d = HH(d, a, b, c, groups[8], S32, 0x8771f681L); /* 34 */
            c = HH(c, d, a, b, groups[11], S33, 0x6d9d6122L); /* 35 */
            b = HH(b, c, d, a, groups[14], S34, 0xfde5380cL); /* 36 */
            a = HH(a, b, c, d, groups[1], S31, 0xa4beea44L); /* 37 */
            d = HH(d, a, b, c, groups[4], S32, 0x4bdecfa9L); /* 38 */
            c = HH(c, d, a, b, groups[7], S33, 0xf6bb4b60L); /* 39 */
            b = HH(b, c, d, a, groups[10], S34, 0xbebfbc70L); /* 40 */
            a = HH(a, b, c, d, groups[13], S31, 0x289b7ec6L); /* 41 */
            d = HH(d, a, b, c, groups[0], S32, 0xeaa127faL); /* 42 */
            c = HH(c, d, a, b, groups[3], S33, 0xd4ef3085L); /* 43 */
            b = HH(b, c, d, a, groups[6], S34, 0x4881d05L); /* 44 */
            a = HH(a, b, c, d, groups[9], S31, 0xd9d4d039L); /* 45 */
            d = HH(d, a, b, c, groups[12], S32, 0xe6db99e5L); /* 46 */
            c = HH(c, d, a, b, groups[15], S33, 0x1fa27cf8L); /* 47 */
            b = HH(b, c, d, a, groups[2], S34, 0xc4ac5665L); /* 48 */
    
            /*第四轮*/
            a = II(a, b, c, d, groups[0], S41, 0xf4292244L); /* 49 */
            d = II(d, a, b, c, groups[7], S42, 0x432aff97L); /* 50 */
            c = II(c, d, a, b, groups[14], S43, 0xab9423a7L); /* 51 */
            b = II(b, c, d, a, groups[5], S44, 0xfc93a039L); /* 52 */
            a = II(a, b, c, d, groups[12], S41, 0x655b59c3L); /* 53 */
            d = II(d, a, b, c, groups[3], S42, 0x8f0ccc92L); /* 54 */
            c = II(c, d, a, b, groups[10], S43, 0xffeff47dL); /* 55 */
            b = II(b, c, d, a, groups[1], S44, 0x85845dd1L); /* 56 */
            a = II(a, b, c, d, groups[8], S41, 0x6fa87e4fL); /* 57 */
            d = II(d, a, b, c, groups[15], S42, 0xfe2ce6e0L); /* 58 */
            c = II(c, d, a, b, groups[6], S43, 0xa3014314L); /* 59 */
            b = II(b, c, d, a, groups[13], S44, 0x4e0811a1L); /* 60 */
            a = II(a, b, c, d, groups[4], S41, 0xf7537e82L); /* 61 */
            d = II(d, a, b, c, groups[11], S42, 0xbd3af235L); /* 62 */
            c = II(c, d, a, b, groups[2], S43, 0x2ad7d2bbL); /* 63 */
            b = II(b, c, d, a, groups[9], S44, 0xeb86d391L); /* 64 */
    
            /*加入到之前计算的结果当中*/
            result[0] += a;
            result[1] += b;
            result[2] += c;
            result[3] += d;
            result[0]=result[0]&0xFFFFFFFFL;
            result[1]=result[1]&0xFFFFFFFFL;
            result[2]=result[2]&0xFFFFFFFFL;
            result[3]=result[3]&0xFFFFFFFFL;
        }
    
        /**
         * 下面是处理要用到的线性函数
         */
        private static long F(long x, long y, long z) {
            return (x & y) | ((~x) & z);
        }
    
        private static long G(long x, long y, long z) {
            return (x & z) | (y & (~z));
        }
    
        private static long H(long x, long y, long z) {
            return x ^ y ^ z;
        }
    
        private static long I(long x, long y, long z) {
            return y ^ (x | (~z));
        }
    
        private static long FF(long a, long b, long c, long d, long x, long s,
                long ac) {
            a += (F(b, c, d)&0xFFFFFFFFL) + x + ac;
            a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >>> (32 - s));
            a += b;
            return (a&0xFFFFFFFFL);
        }
    
        private static long GG(long a, long b, long c, long d, long x, long s,
                long ac) {
            a += (G(b, c, d)&0xFFFFFFFFL) + x + ac;
            a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
            a += b;
            return (a&0xFFFFFFFFL);
        }
    
        private static long HH(long a, long b, long c, long d, long x, long s,
                long ac) {
            a += (H(b, c, d)&0xFFFFFFFFL) + x + ac;
            a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
            a += b;
            return (a&0xFFFFFFFFL);
        }
    
        private static long II(long a, long b, long c, long d, long x, long s,
                long ac) {
            a += (I(b, c, d)&0xFFFFFFFFL) + x + ac;
            a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
            a += b;
            return (a&0xFFFFFFFFL);
        }
    }
    

    JAVA实现MD5

    在java中实现MD5是很简单的,在包java.security有个类MessageDigest。官方文档如下
    MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。

    MessageDigest 对象开始被初始化。该对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算。

    对于给定数量的更新数据,digest 方法只能被调用一次。digest 被调用后,MessageDigest 对象被重新设置成其初始状态。

    JAVA代码如下:

    import java.security.MessageDigest;
    
    public class MyMD5 {
    
        static char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
        public static void main(String[] args) {
            try{
                MessageDigest md5 = MessageDigest.getInstance("MD5");//申明使用MD5算法
                md5.update("a".getBytes());//
                System.out.println("md5(a)="+byte2str(md5.digest()));
                md5.update("a".getBytes());
                md5.update("bc".getBytes());
                System.out.println("md5(abc)="+byte2str(md5.digest()));
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 将字节数组转换成十六进制字符串
         * @param bytes
         * @return
         */
        private static String byte2str(byte []bytes){
            int len = bytes.length;   
            StringBuffer result = new StringBuffer();    
            for (int i = 0; i < len; i++) {   
                byte byte0 = bytes[i];   
                result.append(hex[byte0 >>> 4 & 0xf]);   
                result.append(hex[byte0 & 0xf]);   
            }
            return result.toString();
        }
    } 
    展开全文
  • MD5是什么?及其它的特点

    千次阅读 2018-11-23 22:19:43
    比较官方的解释大家可以百度,这里我说一下自己的理解。 一.作用:一般用于互联网一种数据的唯一性标识。 二....一般被表示为16进制的字符串。4个比特位组成一个16进制字符,因此常常能见到的是(128/4=)32个16进制...

    比较官方的解释大家可以百度,这里我说一下自己的理解。

    一.作用:一般用于互联网一种数据的唯一性标识。

    二.实体:本身是一个128位的0/1比特。一般被表示为16进制的字符串。4个比特位组成一个16进制字符,因此常常能见到的是(128/4=)32个16进制字符组成的字符串   4951 dd1c bff8 cbbe 4cd4 475c a939 fc8b,当然它实质是一种消息摘要算法

    三.特点:

    1.完全相同的一段数据,不论时间地点(加密算法相同的条件)加密出的32位的字符串完全相同。

    2.加密过程本身就是一个有损的加密过程。因此几乎不能还原出原始数据。安全

    补充:一般还原出原始数据都是使用碰撞的方式来还原。意思大概和穷举的意思一样,有足够多的原始数据量,世界上的所有数据用 1~n来表示,把1~n都用MD5计算一遍得1x~nx(x表示计算后得的数)。把1~n当成键值对,把1x~nx放入键值对当得到一个MD5字符串,通过值去找键,就知道是什么数据了。因为你本来就拥有数据,因此才能找到数据。但当你没有原始数据时,想通过128比特的玩意去恢复大于128比特的数据本身就是玩笑,数据量都不一样,至于原理有兴趣的朋友可以研究研究信息论。不想研究的可以回想回想之前学的香农公式。再从这一点去研究。又有大的世界。

    3.散列能力巨强。安全

    MD5工具类代码:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.MessageDigest;
    
    public class MD5Utils {
    	public static String getStringMD5(String str) {
    		try {
    			MessageDigest digest = MessageDigest.getInstance("MD5");
    			return new String(encode(digest.digest(str.getBytes())));
    		} catch (Exception e) {
    			throw new RuntimeException("md5 digest fail:", e);
    		}
    	}
    
    	public static String getFileMD5(File file) {
    		FileInputStream in = null;
    		try {
    			MessageDigest digest = MessageDigest.getInstance("MD5");
    			if (!file.isFile()) {
    				throw new RuntimeException("md5 digest fail: file not exists!");
    			}
    			byte buffer[] = new byte[1024];
    			int len;
    			in = new FileInputStream(file);
    			while ((len = in.read(buffer, 0, 1024)) != -1) {
    				digest.update(buffer, 0, len);
    			}
    			return new String(encode(digest.digest()));
    		} catch (Exception e) {
    			throw new RuntimeException("md5 digest fail:", e);
    		} finally {
    			if (in != null) {
    				try {
    					in.close();
    				} catch (IOException e) {
    				}
    			}
    		}
    	}
    
    	private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    
    	public static char[] encode(byte[] bytes) {
    		final int nBytes = bytes.length;
    		char[] result = new char[2 * nBytes];
    
    		int j = 0;
    		for (int i = 0; i < nBytes; i++) {
    			// Char for top 4 bits
    			result[j++] = HEX[(0xF0 & bytes[i]) >>> 4];
    			// Bottom 4
    			result[j++] = HEX[(0x0F & bytes[i])];
    		}
    
    		return result;
    	}
    
    }
    

    当以上代码加密文件时。即使文件名不同,只要文件内容相同,加密出的MD5完全一致:

    测试:

    加密一个图片:

     

    import java.io.File;
    import utils.MD5Utils;
    
    public class MD5Test {
    	public static void main(String[] args) {
    		//MD5第一次运算 4951dd1cbff8cbbe4cd4475ca939fc8b
    		System.out.println(
    				MD5Utils.getFileMD5(
    						new File("C:\\Users\\anxiaopei\\Desktop\\index.jpg")
    						)
    				);
    		//MD5第二次运算
    		System.out.println(
    				MD5Utils.getFileMD5(
    						new File("C:\\Users\\anxiaopei\\Desktop\\index.jpg")
    						)
    				);
    		//MD5第三次运算
    		System.out.println(
    				MD5Utils.getFileMD5(
    						new File("C:\\Users\\anxiaopei\\Desktop\\index文件名真的不同!.jpg")
    						)
    				);
    		//MD5第四次运算
    		System.out.println(
    				MD5Utils.getFileMD5(
    						new File("C:\\Users\\anxiaopei\\Desktop\\index文件名真的不同!.jpg")
    						)
    				);
    		//1的MD5数为
    		System.out.println(
    				MD5Utils.getStringMD5(
    						"1"
    						)
    				);
    	}
    }
    

    结果:

    对字符串1的加密,大家使用我的代码在你的机器上做了MD5运算后一定也能得到 c4ca4238a0b923820dcc509a6f75849b

    第一次与第二次加密证明了,对相同的文件 不同的时间加密的出相同的MD5

    第三四次加密与前两次相同证明在,文件的副本,哪怕文件名不一样,只要文件内容相同,加密的MD5也是相同的。

    现在我们来做一个有趣的事。

    把图片以记事本打开 更改其中一个字符。再次加密图片(改了一个字符,图片也会打不开,不要使用重要图片做测试),这次我们更改文件的副本里的一个字符。删掉添加更改都可以,随意。这次我就只改一个字符,把最后一个字符“ ? ”删掉。

    删掉“?”后我的副本都不能预览了。

     用MD5运算后,可以看到第二行与第三行结果完全不一样,证明了它散列的能力。巨强!可以说百分之零点几的的差别也会导致MD5完全不一样。因此再不考虑碰撞的可能性下,只要两个数据的MD5相同,那我们可以认为这两个数据完全相同。

    补充:什么是碰撞?:https://blog.csdn.net/qq_29519041/article/details/84404223

     

    说了这么多知道了它唯一标识的能力很强。

    既然他的唯一标识能力这么强。

    那有个使用场景就是,用来作为文件的唯一标识。比如文件上传时,哪怕你的文件名不一样,经过MD5一下,服务器对比一下存在服务器的文件的MD5码表。就知道你上传的这个文件是否服务器已经存在,qq的秒传也是使用这个方法(我猜的)发现服务器有这个文件后,直接把服务器那个文件传给你要传的人。如果你是上传文件给服务器,那你也得到一个访问服务器那个文件的权限。因为服务器知道你上传的文件就是服务器存着的那个文件,这就是秒传的精髓。当然具体实现消息摘要算法的方式也许会不一样。也许是md其他什么的。

     

    展开全文
  • MD5是什么, MD5怎么用

    千次阅读 2019-06-10 22:19:32
    MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. ...

    Message Digest Algorithm

    MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)。

    MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。

    MD5算法具有以下特点:

    1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
    2、容易计算:从原数据计算出MD5值很容易。
    3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
    4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

    一个工具类

    package com.huaidan.utils;
    
    
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.util.Arrays;
    
    public class MyMD5Util {
    
    
    
        private static final String HEX_NUMS_STR="0123456789ABCDEF";
    
        private static final Integer SALT_LENGTH = 12;
    
    
    
        /**
    
         * 将16进制字符串转换成字节数组
    
         * @param hex
    
         * @return
    
         */
    
        public static byte[] hexStringToByte(String hex) {
    
            int len = (hex.length() / 2);
    
            byte[] result = new byte[len];
    
            char[] hexChars = hex.toCharArray();
    
            for (int i = 0; i < len; i++) {
    
                int pos = i * 2;
    
                result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4
    
                        | HEX_NUMS_STR.indexOf(hexChars[pos + 1]));
    
            }
    
            return result;
    
        }
    
    
    
    
    
        /**
    
         * 将指定byte数组转换成16进制字符串
    
         * @param b
    
         * @return
    
         */
    
        public static String byteToHexString(byte[] b) {
    
            StringBuffer hexString = new StringBuffer();
    
            for (int i = 0; i < b.length; i++) {
    
                String hex = Integer.toHexString(b[i] & 0xFF);
    
                if (hex.length() == 1) {
    
                    hex = '0' + hex;
    
                }
    
                hexString.append(hex.toUpperCase());
    
            }
    
            return hexString.toString();
    
        }
    
    
    
        /**
    
         * 验证口令是否合法
    
         * @param password
    
         * @param passwordInDb
    
         * @return
    
         * @throws NoSuchAlgorithmException
    
         * @throws UnsupportedEncodingException
    
         */
    
        public static boolean validPassword(String password, String passwordInDb)
    
                throws NoSuchAlgorithmException, UnsupportedEncodingException {
    
            //将16进制字符串格式口令转换成字节数组
    
            byte[] pwdInDb = hexStringToByte(passwordInDb);
    
            //声明盐变量
    
            byte[] salt = new byte[SALT_LENGTH];
    
            //将盐从数据库中保存的口令字节数组中提取出来
    
            System.arraycopy(pwdInDb, 0, salt, 0, SALT_LENGTH);
    
            //创建消息摘要对象
    
          
            MessageDigest md = MessageDigest.getInstance("MD5");
    
            //将盐数据传入消息摘要对象
    
            md.update(salt);
    
            //将口令的数据传给消息摘要对象
    
            md.update(password.getBytes("UTF-8"));
    
            //生成输入口令的消息摘要
    
            byte[] digest = md.digest();
    
            //声明一个保存数据库中口令消息摘要的变量
    
            byte[] digestInDb = new byte[pwdInDb.length - SALT_LENGTH];
    
            //取得数据库中口令的消息摘要
    
            System.arraycopy(pwdInDb, SALT_LENGTH, digestInDb, 0, digestInDb.length);
    
            //比较根据输入口令生成的消息摘要和数据库中消息摘要是否相同
    
            if (Arrays.equals(digest, digestInDb)) {
    
                //口令正确返回口令匹配消息
    
                return true;
    
            } else {
    
                //口令不正确返回口令不匹配消息
    
                return false;
    
            }
    
        }
    
    
    
    
    
        /**
    
         * 获得加密后的16进制形式口令
    
         * @param password
    
         * @return
    
         * @throws NoSuchAlgorithmException
    
         * @throws UnsupportedEncodingException
    
         */
    
        public static String getEncryptedPwd(String password)
    
                throws NoSuchAlgorithmException, UnsupportedEncodingException {
    
            //声明加密后的口令数组变量
    
            byte[] pwd = null;
    
            //随机数生成器
    
            SecureRandom random = new SecureRandom();
    
            //声明盐数组变量
    
            byte[] salt = new byte[SALT_LENGTH];
    
            //将随机数放入盐变量中
    
            random.nextBytes(salt);
    
    
    
            //声明消息摘要对象
    
            MessageDigest md = null;
    
            //创建消息摘要
    
            md = MessageDigest.getInstance("MD5");
    
            //将盐数据传入消息摘要对象
    
            md.update(salt);
    
            //将口令的数据传给消息摘要对象
    
            md.update(password.getBytes("UTF-8"));
    
            //获得消息摘要的字节数组
    
            byte[] digest = md.digest();
    
    
    
            //因为要在口令的字节数组中存放盐,所以加上盐的字节长度
    
            pwd = new byte[digest.length + SALT_LENGTH];
    
            //将盐的字节拷贝到生成的加密口令字节数组的前12个字节,以便在验证口令时取出盐
    
            System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH);
    
            //将消息摘要拷贝到加密口令字节数组从第13个字节开始的字节
    
            System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length);
    
            //将字节数组格式加密后的口令转化为16进制字符串格式的口令
    
            return byteToHexString(pwd);
    
        }
    
    }
    
    

    测试类客户端

    package com.huaidan.test;
    
    
    import com.huaidan.utils.MyMD5Util;
    
    import java.io.UnsupportedEncodingException;
    import java.security.NoSuchAlgorithmException;
    import java.util.HashMap;
    import java.util.Map;
    
    public class Client {
    
        private static Map users = new HashMap();
    
    
    
        public static void main(String[] args){
    
    
    
            String userName = "zyg";
    
            String password = "123";
    
            registerUser(userName,password);
    
    
    
            userName = "changong";
    
            password = "456";
    
            registerUser(userName,password);
    
    
    
            String loginUserId = "zyg";
    
            String pwd = "123";
    
            try {
    
                if(loginValid(loginUserId,pwd)){
    
                    System.out.println("欢迎登陆!!!");
    
                }else{
    
                    System.out.println("口令错误,请重新输入!!!");
    
                }
    
            } catch (NoSuchAlgorithmException e) {
    
                // TODO Auto-generated catch block
    
                e.printStackTrace();
    
            } catch (UnsupportedEncodingException e) {
    
                // TODO Auto-generated catch block
    
                e.printStackTrace();
    
            }
    
        }
    
    
    
        /**
    
         * 注册用户
    
         *
    
         * @param userName
    
         * @param password
    
         */
    
        public static void registerUser(String userName,String password){
    
            String encryptedPwd = null;
    
            try {
    
                encryptedPwd = MyMD5Util.getEncryptedPwd(password);
    
    
    
                System.out.println("加密后的用户密码"+encryptedPwd);
    
                users.put(userName, encryptedPwd);
    
    
    
            } catch (NoSuchAlgorithmException e) {
    
                // TODO Auto-generated catch block
    
                e.printStackTrace();
    
            } catch (UnsupportedEncodingException e) {
    
                // TODO Auto-generated catch block
    
                e.printStackTrace();
    
            }
    
        }
    
    
    
        /**
    
         * 验证登陆
    
         *
    
         * @param userName
    
         * @param password
    
         * @return
    
         * @throws UnsupportedEncodingException
    
         * @throws NoSuchAlgorithmException
    
         */
    
        public static boolean loginValid(String userName,String password)
    
                throws NoSuchAlgorithmException, UnsupportedEncodingException{
    
            String pwdInDb = (String)users.get(userName);
    
            System.out.println(pwdInDb);
    
            if(null!=pwdInDb){ // 该用户存在
    
                return MyMD5Util.validPassword(password, pwdInDb);
    
            }else{
    
                System.out.println("不存在该用户!!!");
    
                return false;
    
            }
    
        }
    
    }
    
    
    展开全文
  • MD5算法详解

    万次阅读 多人点赞 2020-05-04 21:37:51
    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值。

    展开全文
  • MD5即Message-Digest Algorithm 5(信息-摘要算法5)。用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之中的一个(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。 ——百度百科  当然。这篇...
  • 对于加密来说,MD5和SHA都比较流行。所谓加盐,无非是生成一个随机的salt在数据存储时提高数据的安全性,防止不法分子盗取用户个人信息。虽然MD5进行加密是不可逆的,但还是有弊端的。 举个简单的例子来说:如果...
  • 之前我们都是使用MD5 Md5PasswordEncoder 或者SHA ShaPasswordEncoder 的哈希算法进行密码加密,在spring security中依然使用只要指定使用自定义加密算法就行,现在推荐spring使用的BCrypt BCryptPasswordEncoder,...
  • MD5加盐

    2019-04-08 08:48:28
    转自:吃螺丝粉 这段时间诸多爆库的新闻,里面有许多饶有趣味的事情。...(这篇文章是给IT界的人看的,如果你看不懂,我会准备一个简单的“如何辨别密码安全糟糕的网站”的方法给你,另文描述。) ...
  • bcryptjs密码加密

    2020-04-02 12:05:36
    bcryptjs密码加密 一.背景 ...二.md5加密 ​ md5是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。通常将128位MD5哈希表示为32位...
  • 用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。 特定字符串是程序代码中固定的,salt是每个密码单独随机,...
  • MD5算法原理

    万次阅读 多人点赞 2012-02-14 14:45:41
    MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2、MD3和MD4发展而来。MD5算法的使用不需要支付任何版权费用。  MD5功能:  输入任意长度的信息,经过处理,输出为128位的...
  • MD5是什么?如何使用MD5

    万次阅读 2018-01-26 16:44:24
    MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc....
  • MD5

    2019-08-15 14:27:05
    MD5消息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。 MD5可以将任意长度的字节串变换成一个128位...
  • 什么叫MD5MD5通常做什么用处

    千次阅读 2019-05-20 10:36:53
    这里是修真院后端小课堂,每篇分享文从 【背景介绍】【知识剖析】【常见问题】【解决方案】...【修真院java小课堂】什么叫MD5MD5通常做什么用处,为什么MD5不可逆,用做密码加密的时候仍然可能会被解密? 大家...
  • MD5.js,前端MD5加密

    万次阅读 2017-10-26 13:26:07
    MD5.js 共有6中加密方法:hex_md5(s), b64_md5(s) ,str_md5(s) ,hex_hmac_md5(key, data), b64_hmac_md5(key, data) ,str_hmac_md5(key, data).根据需求选择. js加密的好处: 1,用js对私密信息加密可避免在网络中...
  • Java实现MD5加密的方式

    万次阅读 多人点赞 2018-07-02 00:08:15
     MD5加密全程是Message-Digest Algoorithm 5(信息-摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串。  例如我们要加密一篇文章,那么我们会随机从每段话或者每行中获取...
  • JAVA核心知识点--MD5加密

    万次阅读 2019-03-27 20:31:27
    MD5简介 在java中使用MD5 MD5简介 MD5即Message-Digest Algorithm 5(信息-摘要算法第五版),是一种在计算机安全领域被广泛使用的散列函数(又译杂凑算法、摘要算法、哈希算法),用于确保所加密的数据的信息...
  • MD5算法如何被破解

    万次阅读 多人点赞 2020-03-12 07:27:45
    小明:老师,上次您讲了MD5算法。用它生成的信息摘要,真的可以被破解吗? 老师:有很多种方法可以破解,不过需要明确一点,这里所谓的破解,并非把摘要还原成原文。为什么呢?因为固定128位的摘要是有穷的,而原文...
  • Python3中的md5加密

    万次阅读 2018-09-10 19:37:18
    Python3中的md5加密 md5介绍 在写网络爬虫模拟登陆时,遇到提交表单时是一串数字和字母,才知道经过了md5加密。 md5加密的密码 MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的...
  • python3中的md5加密

    万次阅读 多人点赞 2017-12-19 11:56:17
    在python3的标准库中,已经移除了md5,而关于hash加密算法都放在hashlib这个标准库中,如SHA1、SHA224、SHA256、SHA384、SHA512和MD5算法等。另:在网上找关于python的md5加密,发现要不是比较旧的不适用当前py版本...
1 2 3 4 5 ... 20
收藏数 587,764
精华内容 235,105
关键字:

md5