MD5散列算法原理及实现

目录

 

一、什么是MD5

二、MD5的功能

三、抗膨胀性

四、可逆性

五、MD5是 加密算法吗?

六、MD5用途

1、防止被篡改

2、防止明文读取。

3、防止抵赖

七、MD5算法过程

主要过程描述

第一步:填充

第二步:记录信息长度

第三步:装入标准的幻数(四个整数)

第四步:四轮循环运算

八、java代码如下


 

一、什么是MD5

MD5信息简要是一种散列算法,可以对任意长度的输入,产生128位的输出,是一种被广泛应用的密码杂凑算法。

二、MD5的功能

输入任意长度的信息,输出128位的散列值(数字密码)

唯一性:不同的输入产生不同的输出

三、抗膨胀性

理论上MD5是不具备抗碰撞性的,因为输入是任意的 , 是无限的 , 但是输出是128位 , 也就是2^128,是有限的。以有限映射无限,必然出现碰撞。但是在实际应用中,我们的输入也是有限的,所以出现碰撞的几率是非常小的。

四、可逆性

MD5是散列哈希,是一种有损强压缩算法,在压缩过程中是会丢失信息,所以是不可逆的。

五、MD5是 加密算法吗?

一部分人认为MD5,是不可逆的,只有加密过程,没有解密过程,不能通过密文还原明文,不属于加密算法。

而另一部分人认为原明文信息经过MD5后生成了密文,已经进行了加密,MD5应该是归于加密算法。

个人观点:MD5用作生成数字码,和BASE64一样,属于是编码算法范畴。

六、MD5用途

1、防止被篡改

比如发送一个电子邮件,我们先生成MD5密文 a , 等对方收到邮件后,根据邮件内容生成密文b , 如果a=b ,则邮件未被篡改。

比如网站提供下载功能,可以预先生成MD5散列,并公布在网站上,客户下载后可以通过验证MD5散列,查看下载的数据是否被更改过。

2、防止明文读取。

比如我们系统里关于用户密码的问题 , 一般是不允许被明文入库的,这样会有丢失风险。常规的做法是MD5(password + salt) 生成密文后入库存档,这样即使数据被泄露 , 也无法通过密文反推用户密码。

3、防止抵赖

比如我做出了某项承诺:“过年给大家发3个月的年终奖” ,并预先生成MD5数字指纹 , 到了年底上,我不能私自将3个月改称1个月,因为MD5输入的微小变化,将引起MD5数字指纹的巨大变化。

七、MD5算法过程

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

主要过程描述

第一步:填充

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

第二步:记录信息长度

用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N512+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);
    }
}
​
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
散列算法是一种将任意长度的输入转换为固定长度输出的算法。它的基本原理是通过对输入数据进行一系列的处理和变换,最终生成一个唯一的散列值。这个散列值是根据输入数据计算得出的,并且具有以下几个特点: 1. 唯一性:对于不同的输入数据,散列算法应该生成不同的散列值。 2. 固定长度:无论输入数据的长度是多少,散列算法都会生成固定长度的输出。 3. 不可逆性:根据散列值无法推导出原始输入数据的内容。 4. 散列值的变化:即使输入数据的细微变化,也会导致生成的散列值完全不同。 实现散列算法的方法有很多种,其中常见的一种是MD5(Message Digest Algorithm 5)算法MD5算法是MD家族中最常用的一种加密算法。它基于一系列的位操作、位移操作和逻辑运算,对输入数据进行分组处理,并通过多次迭代和轮函数的运算来生成最终的散列值。 MD5算法实现过程可以分为以下几个步骤: 1. 填充信息:将输入数据进行填充,使得数据长度满足算法要求。 2. 初始值设置:设置初始的散列值,即A、B、C、D。 3. 真正的计算:通过多次迭代和轮函数的运算,对输入数据进行处理,最终生成散列值。 散列算法实现还可以通过其他算法,如SHA-1、SHA-256等。在实际应用中,散列算法常用于校验文件的完整性和存储用户密码等场景。通过对文件进行散列计算,可以判断文件是否被篡改;而存储用户密码时,通常将密码进行散列处理,并将散列值存储在数据库中,以增加密码的安全性。 在Java中,可以使用java.security.MessageDigest类来实现并使用散列算法。使用该类可以对输入数据进行散列计算,并生成散列值。具体的实现和使用方法可以参考Java文档和相关教程。 综上所述,散列算法的基本原理是将任意长度的输入转换为固定长度的输出,通过一系列的处理和变换来生成唯一的散列值。常见的实现方法包括MD5算法和SHA系列算法。使用Java的MessageDigest类可以方便地实现和使用散列算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值