[JAVA][MD5]关于MD5的实现和JAVA生成MD5

本文介绍了JAVA中MD5加密的实现,详细讲解了MD5算法的补位、分组和循环步骤。并探讨了MD5的盐运算以增强密码安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

>获取完整摘要算法工具类代码:

[JAVA]数字摘要算法工具类——(Hamc)MD5/SHA1/SHA256/SHA512/PBKDF2

 

>Java实现MD5

JAVA JDK中的security包自带了MD5的加密算法,经过加密后,会生成一个128位的二进制序列。

首先导入这个包:

 

import java.security.MessageDigest;


然后编写主要的业务逻辑:

        try {
            //1.获取算法MD5实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            //2.MD5加密
            byte[] buff = md.digest("message digest".getBytes());// "message digest" 就是密文
            //3.将128位的二进制编码转为32位的16进制编码
            String md5str = toHex(buff);
            //4.打印
            System.out.println(md5str);//输出 f96b697d7cb7938d525a2f31aaf161d0
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

其中,128bin -> 32hex的方法如下:

    //将128位的二进制序列转为32位的16进制编码
    public static String toHex(byte[] bytes) {
        String md5str = "";
        for (int i = 0; i < bytes.length; i++) {
            int temp = bytes[i];
            if (temp < 0) temp += 256; // 0x8* 在经过toHexString转化时,会被转为ffffff8*,需要+256保证其正值
            if (temp < 16) md5str += "0"; // 0x05 转化会变成 5,缺少一位0
            md5str+=Integer.toHexString(temp);
        }
        return md5str;
    }

 

>MD5的算法

MD5的主要算法思路有三步:

 

  1. 补位
  2. 分组(小端排序)
  3. 循环

>补位是什么呢?

 

MD5将密文按512分一个大组,然后每32位分一个小组,共16小组。(也就是64字节一个大组,4个字节一个小组)。

密文总长度为len,每条密文最后要留出64位(8个字节)的长度,用于保存原数据的长度len;因此,最后一个大分组必须有448位(56字节)的补位;

举个例子,假如一个密文长度为127位(字节),由于(127%64=63)!= 48,因此,必须补到其刚好余48字节为止,也就是(64-63)+48=49个字节。

补位时,第一个字节必为0x80,其后都是0x00。(也就是第一位为1,其他位都为0)。

补位完成后,再往后面写上八个字节的长度。比如对于上面那个例子,最后8个字节要写的是7fe 00 00 00 00 00 00 00。(127 = 0x7f)

     public static BYTES fill(BYTES s){
        int length=s.getLength();
        if(s.getLength()%64!=56){
            s.add((byte)0x80);
            if(length%64<56){
                for(int i=0;i<56-length%64-1;i++){
                    s.add((byte)0x00);
                }
            }
            else {
                for(int i=0;i<56+64-length%64-1;i++){
                    s.add((byte)0x00);
                }
            }
        }
        String hex = Integer.toHexString(length);
        int begin=-2,end=begin+2,remain=8;
        while(end<hex.length()){
            remain--;
            begin=end;
            end+=2;
            if (end>hex.length())end=hex.length();
            byte b = (byte)(int)Integer.valueOf(hex.substring(begin,end),16);
            s.add(b);
        }
        for (int i=0;i<remain;i++){
            s.add((byte)0x00);
        }
        //System.out.println(s.getLength());
        return s;
    }

 

>如何分组?

共分为16组,第j组记为M[j]。每4个字节(32位)一组,组内按小端排序。

比如M[1]对应的数据是12 13 14 15,那么M[1]=0x15141312。>

    public static int[] divide(BYTES s){
        int[] M = new int[16];
        for(int i=0;i<16;i++){
            int a = s.getI(i*4);if(a<0)a+=256;
            int b = s.getI(i*4+1);if(b<0)b+=256;
            int c = s.getI(i*4+2);if(c<0)c+=256;
            int d = s.getI(i*4+3);if(d<0)d+=256;
            String ta="";if(a<16)ta+="0";ta+=Integer.toHexString(a);
            String tb="";if(b<16)tb+="0";tb+=Integer.toHexString(b);
            String tc="";if(c<16)tc+="0";tc+=Integer.toHexString(c);
            String td="";if(d<16)td+="0";td+=Integer.toHexString(d);
            /*
            System.out.println("a="+a+" "+i+" "+Integer.toHexString(a));
            System.out.println("b="+b+" "+i+" "+Integer.toHexString(b));
            System.out.println("c="+c+" "+i+" "+Integer.toHexString(c));
            System.out.println("d="+d+" "+i+" "+Integer.toHexString(d));
            */
            String temp=td+tc+tb+ta;
            M[i]=Integer.valueOf(temp,16);
        }
        return M;
    }

 

>循环

这是MD5的核心算法,涉及到数学,就不推导了。

总之,需要定义四个链接常量:

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

四个非线性函数:

    public static int F(int X,int Y,int Z){
        return (X&Y)|((~X)&Z);
    }
    public static int G(int X,int Y,int Z){
        return (X&Z)|(Y&(~Z));
    }
    public static int H(int X,int Y,int Z){
        return X^Y^Z;
    }
    public static int I(int X,int Y,int Z){
        return Y^(X|(~Z));
    }

四个循环函数:

    public static int FF(int a,int b,int c,int d,int Mj,int s,int ti){
        return b + ((a + F(b, c, d) + Mj + ti) << s);
    }
    public static int GG(int a,int b,int c,int d,int Mj,int s,int ti){
        return b + ((a + G(b, c, d) + Mj + ti) << s);
    }
    public static int HH(int a,int b,int c,int d,int Mj,int s,int ti){
        return b + ((a + H(b, c, d) + Mj + ti) << s);
    }
    public static int II(int a,int b,int c,int d,int Mj,int s,int ti){
        return b + ((a + I(b, c, d) + Mj + ti) << s);
    }

 

接着,进行N=sLength/64次大循环,其中sLength是补位后的数据长度(这里的单位是字节,所以除了64)。每次循环结束后,要做一个A+=a的操作。(这部分直接结合代码看吧)

整个循环的代码为:

    public static String getMD5(BYTES s){
        //链接常量
        int A=0x67452301;
        int B=0xefcdab89;
        int C=0x98badcfe;
        int D=0x10325476;
        int a=A,b=B,c=C,d=D;
        int N=s.getLength()/64;
        for(int i=0;i<N;i++){
            int[] M = divide(s);
            //对M[j]的第一轮循环
            a=FF(a,b,c,d,M[0],7,0xd76aa478);
            b=FF(d,a,b,c,M[1],12,0xe8c7b756);
            c=FF(c,d,a,b,M[2],17,0x242070db);
            d=FF(b,c,d,a,M[3],22,0xc1bdceee);
            a=FF(a,b,c,d,M[4],7,0xf57c0faf);
            b=FF(d,a,b,c,M[5],12,0x4787c62a);
            c=FF(c,d,a,b,M[6],17,0xa8304613);
            d=FF(b,c,d,a,M[7],22,0xfd469501) ;
            a=FF(a,b,c,d,M[8],7,0x698098d8) ;
            b=FF(d,a,b,c,M[9],12,0x8b44f7af) ;
            c=FF(c,d,a,b,M[10],17,0xffff5bb1) ;
            d=FF(b,c,d,a,M[11],22,0x895cd7be) ;
            a=FF(a,b,c,d,M[12],7,0x6b901122) ;
            b=FF(d,a,b,c,M[13],12,0xfd987193) ;
            c=FF(c,d,a,b,M[14],17,0xa679438e) ;
            d=FF(b,c,d,a,M[15],22,0x49b40821);

            //对M[j]的第二轮循环
            a=GG(a,b,c,d,M[1],5,0xf61e2562);
            b=GG(d,a,b,c,M[6],9,0xc040b340);
            c=GG(c,d,a,b,M[11],14,0x265e5a51);
            d=GG(b,c,d,a,M[0],20,0xe9b6c7aa) ;
            a=GG(a,b,c,d,M[5],5,0xd62f105d) ;
            b=GG(d,a,b,c,M[10],9,0x02441453) ;
            c=GG(c,d,a,b,M[15],14,0xd8a1e681);
            d=GG(b,c,d,a,M[4],20,0xe7d3fbc8) ;
            a=GG(a,b,c,d,M[9],5,0x21e1cde6) ;
            b=GG(d,a,b,c,M[14],9,0xc33707d6) ;
            c=GG(c,d,a,b,M[3],14,0xf4d50d87) ;
            d=GG(b,c,d,a,M[8],20,0x455a14ed);
            a=GG(a,b,c,d,M[13],5,0xa9e3e905);
            b=GG(d,a,b,c,M[2],9,0xfcefa3f8) ;
            c=GG(c,d,a,b,M[7],14,0x676f02d9) ;
            d=GG(b,c,d,a,M[12],20,0x8d2a4c8a);

            //对M[j]的第三轮循环
            a=HH(a,b,c,d,M[5],4,0xfffa3942);
            b=HH(d,a,b,c,M[8],11,0x8771f681);
            c=HH(c,d,a,b,M[11],16,0x6d9d6122);
            d=HH(b,c,d,a,M[14],23,0xfde5380c) ;
            a=HH(a,b,c,d,M[1],4,0xa4beea44) ;
            b=HH(d,a,b,c,M[4],11,0x4bdecfa9) ;
            c=HH(c,d,a,b,M[7],16,0xf6bb4b60) ;
            d=HH(b,c,d,a,M[10],23,0xbebfbc70);
            a=HH(a,b,c,d,M[13],4,0x289b7ec6);
            b=HH(d,a,b,c,M[0],11,0xeaa127fa);
            c=HH(c,d,a,b,M[3],16,0xd4ef3085);
            d=HH(b,c,d,a,M[6],23,0x04881d05);
            a=HH(a,b,c,d,M[9],4,0xd9d4d039);
            b=HH(d,a,b,c,M[12],11,0xe6db99e5);
            c=HH(c,d,a,b,M[15],16,0x1fa27cf8) ;
            d=HH(b,c,d,a,M[2],23,0xc4ac5665);

            //对M[j]的第四轮循环
            a=II(a,b,c,d,M[0],6,0xf4292244) ;
            b=II(d,a,b,c,M[7],10,0x432aff97) ;
            c=II(c,d,a,b,M[14],15,0xab9423a7);
            d=II(b,c,d,a,M[5],21,0xfc93a039) ;
            a=II(a,b,c,d,M[12],6,0x655b59c3) ;
            b=II(d,a,b,c,M[3],10,0x8f0ccc92) ;
            c=II(c,d,a,b,M[10],15,0xffeff47d);
            d=II(b,c,d,a,M[1],21,0x85845dd1) ;
            a=II(a,b,c,d,M[8],6,0x6fa87e4f) ;
            b=II(d,a,b,c,M[15],10,0xfe2ce6e0);
            c=II(c,d,a,b,M[6],15,0xa3014314) ;
            d=II(b,c,d,a,M[13],21,0x4e0811a1);
            a=II(a,b,c,d,M[4],6,0xf7537e82) ;
            b=II(d,a,b,c,M[11],10,0xbd3af235);
            c=II(c,d,a,b,M[2],15,0x2ad7d2bb);
            d=II(b,c,d,a,M[9],21,0xeb86d391);

            A += a;
            B += b;
            C += c;
            D += d;
        }
        if(A<0)A+=256;
        if(B<0)B+=256;
        if(C<0)C+=256;
        if(D<0)D+=256;
        return Integer.toHexString(A)+" "+Integer.toHexString(B)+" "+Integer.toHexString(C)+" "+Integer.toHexString(D);
    }

 

>MD5的盐运算

MD5理论上不可能求逆,但仍经不住黑客通过暴力解法去求解。例如黑客从数据库里得到一个序列为M的经过MD5加密的密码序列,那么他可以在他本机上经过大量摘要运算,当某个字符串S的MD5计算结果等于M时,就可以知道S就是原密码了。

盐运算可以较好得预防这个情况:

 

  1. 每次随机一个16位的Hex序列,如0x254f58d21a,把这个序列R作为字符串,直接加到密码序列S之后,也就是S+="254f58d21a";
  2. 对这个序列做MD5,并对结果M,把后16位或你指定的某16位用序列R替换;(当然更好的方法是按某种只有你知道的方式,插入到MD5序列中,可充分保证唯一性);
  3. 验证用户密码S时,从数据库存储的MD5码提取R(你已知其如何插入或替换也就知道如何提取),然后重复1、2两步,最后与数据库中的MD5校对即可。

 

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值