MD5 加密算法详解

MD5 加密算法详解

  • 一、概念
  • 二、算法原理
    • 2.1、MD5(32)、MD5(16)
    • 2.2、算法 4步流程
  • 三、 其他
    • 3.1、作用
    • 3.2、MD5可以作为数据库主键,唯一吗?
    • 3.3、测试源码

一、概念

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

你可能感兴趣的:(系统安全,加密)