《Java知识应用》Java加密方式(MD5)详解

1. 应用

使用MD5加密

因为:因为MD5的不可逆性,也可以保证你的key 是安全的,黑客无法通过原文和密文知晓你的key。

案例:

import java.math.BigInteger;
import java.security.MessageDigest;

public class MD5Util{

    /**
     * 使用MD5加密
     * @param plainText
     * @return
     */
    public static String encryptionByMD5(String plainText) {
        String md5code;
        try {
            byte[] secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
            md5code = new BigInteger(1, secretBytes).toString(16);
            for (int i = 0; i < 32 - md5code.length(); i++) {
                md5code = "0" + md5code;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return md5code;
    }
}
import java.io.IOException;

public class Md5Test {

    public static void main(String[] args) throws IOException {
        //网络传输过程中被篡改很容易。
        //可逆加密也一样,只要让别人知道加密方式,就可以先还原再修改,再以同样的方式加密,一样篡改了传输信息。
        // 根据几种加密方式就可以推出你使用的加密方式。
        String str = "姓名=蕾蕾,支付=1000,账户:123";

        /**
         * 使用MD5是不可逆加密方式。
         * 就算黑客知道我是MD5加密的,他也不能通过密文反推我的原文是啥。
         */
        String result = MD5Util.encryptionByMD5(str);
        System.out.println(result);
        System.out.println("-----------实际使用场景------------");
        //先准备一个key 只有传输方和接收方知道。
        String key = "AHINTGJLL&&HH$%^";
        // 将原文和key拼接和一起使用MD5加密
        String resultMd5 = MD5Util.encryptionByMD5(str+key);
        //然后将原文和密文再拼接,然后传输。
        System.out.println(resultMd5);
        /**
         * 这样操作后,黑客拿到这个传输数据,改的了你的账户,金额,无法修改你的加密串,因为他无法知道你们约定的key:AHINTGJLL&&HH$%^.
         * 因为MD5的不可逆性,也可以保证你的key 是安全的,不能被黑客知晓。
         */
        System.out.println("传输:" +str+":"+resultMd5);
    }
}

运行结果:

2 MD5 加密原理(如何做到不可逆)

MD5算法特点:

1. 加密结果:固定长度。

2. 加密速度快。

3. 细微:原文改变一点,加密结果变化巨大。

4. 不可逆:

最简单的不可逆加密:比如 原文是“57” ,加密方式是相加得到“12”。 这个时候你发现你知道加密结果是12,但是你无法知道原文是什么,因为“57”,“66”,“48” 等都有可能。

当然按照上面的加密方法,虽然不知道原文,但是破解太简单了。

算法:

1、数据填充

对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。

填充方法:在消息后面进行填充,填充第一位为1,其余为0。

2、添加消息长度

在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。

在此步骤进行完毕后,最终消息长度就是512的整数倍。

3、数据处理

准备需要用到的数据:

4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
4个函数: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));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。

实现:

import java.math.BigInteger;
import java.util.Arrays;

public class MD5 {
    //标准的幻数
    private static final int A=0x67452301;
    private static final int B=0xefcdab89;
    private static final int C=0x98badcfe;
    private static final int D=0x10325476;

    //下面这些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 int [] result={A,B,C,D};//存储hash结果,共4×32=128位,初始化值为(幻数的级联)
    //此重载方法是将传入的字符串加点特色(比如密码加密可以连同将用户名加入一起加密,这样就算密码和别人一样加密后的结果也不一样)
    private byte[] digest(String inputStr ,String salt) {
        return digest(salt+inputStr);
    }
    private byte[] digest(String inputStr){
        byte [] inputBytes=inputStr.getBytes();//将字符串转化成字节数组
        int byteLen=inputBytes.length;//长度(字节)
        int groupCount=0;//完整分组的个数
        groupCount=byteLen/64;//每组512位(64字节)
        int []groups=null;//每个小组(64字节)再细分后的16个小组(4字节)

        //处理每一个完整分组
        for(int step=0;step<groupCount;step++){
            groups=divGroup(inputBytes,step*64);//处理分组,将每一个完整分组16个小组(16×4)
            trans(groups);//处理分组,核心算法
        }

        //处理完整分组后的尾巴
        int rest=byteLen%64;//512位分组后的余数
        byte [] tempBytes=new byte[64];
        //余数小于<=56,先填充1,0数据,然后最后64位(8字节)储存长度
        if(rest<=56){
            //将尾巴先存储在临时数组中
            for(int i=0;i<rest;i++)
                tempBytes[i]=inputBytes[byteLen-rest+i];
            if(rest<56){
                //填充数据(448位之前第一个填充1,后面填充0)
                tempBytes[rest]=(byte)(1<<7);
                for(int i=1;i<56-rest;i++)
                    tempBytes[rest+i]=0;
            }
            //后64位储存原文实际长度(448-512),即8字节
            long len=byteLen<<3;//长度单位是bit 即原文字节数组长度×8
            for(int i=0;i<8;i++){
                tempBytes[56+i]=(byte)(len&0xFF);//每次取长度的低八位存入字节数组
                len=len>>8;//取完移除低八位
            }
            groups=divGroup(tempBytes,0);
            trans(groups);//处理分组
        }else{//余数大于56,需增加一组主循环,目的保证处理后的原文最后64位(8字节)储存长度
            for(int i=0;i<rest;i++)
                //将尾巴先存储在临时数组中
                tempBytes[i]=inputBytes[byteLen-rest+i];
            //填充数据(第一个填充1,后面填充0)
            tempBytes[rest]=(byte)(1<<7);
            for(int i=rest+1;i<64;i++)
                tempBytes[i]=0;
            groups=divGroup(tempBytes,0);
            trans(groups);//处理分组
            //前56元素填充0
            for(int i=0;i<56;i++)
                tempBytes[i]=0;
            //后64位储存原文实际长度(448-512),即8字节
            long len=(long)(byteLen<<3);
            for(int i=0;i<8;i++){
                tempBytes[56+i]=(byte)(len&0xFF);
                len=len>>8;
            }
            groups=divGroup(tempBytes,0);
            trans(groups);//处理分组
        }
        //将hash值转换成字节数组
        //int[] result={A,B,C,D};共4×32=128位,每八位转换一个byte类型,储存在一个结果集
        byte[] resultByte = new byte[16];
        for(int i = 0;i<4;i++) {
            for(int j = 0; j < 4; j++) {
                //每次取低八位
                resultByte[i*4+j] = (byte) (result[i] & 0xff);
                //取完移除
                result[i]=result[i]>>8;
            }
        }

        return resultByte;
    }

    /**
     * 从inputBytes的index开始取512位,作为新的分组
     * 将每一个512位的分组再细分成16个小组,每个小组64位(8个字节)
     * @param inputBytes
     * @param index
     * @return
     */
    private static int[] divGroup(byte[] inputBytes,int index){
        int [] temp=new int[16];
        for(int i=0;i<16;i++){
            //从byte数组中取四个元素组成一个int类型保存在数组中
            //b2iu方法是将byte的最高位符号位转化为代表数值位
            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 int b2iu(byte b){//0x7F + 128=0xff
        return b < 0 ? b & 0x7F + 128 : b;
    }

    /**
     * 主要的操作,四轮循环
     * @param groups--每一个分组512位(64字节)
     */
    private void trans(int[] groups) {
        int a = result[0], b = result[1], c = result[2], d = result[3];
        /*第一轮*/
        a = FF(a, b, c, d, groups[0], S11, 0xd76aa478); /* 1 */
        d = FF(d, a, b, c, groups[1], S12, 0xe8c7b756); /* 2 */
        c = FF(c, d, a, b, groups[2], S13, 0x242070db); /* 3 */
        b = FF(b, c, d, a, groups[3], S14, 0xc1bdceee); /* 4 */
        a = FF(a, b, c, d, groups[4], S11, 0xf57c0faf); /* 5 */
        d = FF(d, a, b, c, groups[5], S12, 0x4787c62a); /* 6 */
        c = FF(c, d, a, b, groups[6], S13, 0xa8304613); /* 7 */
        b = FF(b, c, d, a, groups[7], S14, 0xfd469501); /* 8 */
        a = FF(a, b, c, d, groups[8], S11, 0x698098d8); /* 9 */
        d = FF(d, a, b, c, groups[9], S12, 0x8b44f7af); /* 10 */
        c = FF(c, d, a, b, groups[10], S13, 0xffff5bb1); /* 11 */
        b = FF(b, c, d, a, groups[11], S14, 0x895cd7be); /* 12 */
        a = FF(a, b, c, d, groups[12], S11, 0x6b901122); /* 13 */
        d = FF(d, a, b, c, groups[13], S12, 0xfd987193); /* 14 */
        c = FF(c, d, a, b, groups[14], S13, 0xa679438e); /* 15 */
        b = FF(b, c, d, a, groups[15], S14, 0x49b40821); /* 16 */

        /*第二轮*/
        a = GG(a, b, c, d, groups[1], S21, 0xf61e2562); /* 17 */
        d = GG(d, a, b, c, groups[6], S22, 0xc040b340); /* 18 */
        c = GG(c, d, a, b, groups[11], S23, 0x265e5a51); /* 19 */
        b = GG(b, c, d, a, groups[0], S24, 0xe9b6c7aa); /* 20 */
        a = GG(a, b, c, d, groups[5], S21, 0xd62f105d); /* 21 */
        d = GG(d, a, b, c, groups[10], S22, 0x2441453); /* 22 */
        c = GG(c, d, a, b, groups[15], S23, 0xd8a1e681); /* 23 */
        b = GG(b, c, d, a, groups[4], S24, 0xe7d3fbc8); /* 24 */
        a = GG(a, b, c, d, groups[9], S21, 0x21e1cde6); /* 25 */
        d = GG(d, a, b, c, groups[14], S22, 0xc33707d6); /* 26 */
        c = GG(c, d, a, b, groups[3], S23, 0xf4d50d87); /* 27 */
        b = GG(b, c, d, a, groups[8], S24, 0x455a14ed); /* 28 */
        a = GG(a, b, c, d, groups[13], S21, 0xa9e3e905); /* 29 */
        d = GG(d, a, b, c, groups[2], S22, 0xfcefa3f8); /* 30 */
        c = GG(c, d, a, b, groups[7], S23, 0x676f02d9); /* 31 */
        b = GG(b, c, d, a, groups[12], S24, 0x8d2a4c8a); /* 32 */

        /*第三轮*/
        a = HH(a, b, c, d, groups[5], S31, 0xfffa3942); /* 33 */
        d = HH(d, a, b, c, groups[8], S32, 0x8771f681); /* 34 */
        c = HH(c, d, a, b, groups[11], S33, 0x6d9d6122); /* 35 */
        b = HH(b, c, d, a, groups[14], S34, 0xfde5380c); /* 36 */
        a = HH(a, b, c, d, groups[1], S31, 0xa4beea44); /* 37 */
        d = HH(d, a, b, c, groups[4], S32, 0x4bdecfa9); /* 38 */
        c = HH(c, d, a, b, groups[7], S33, 0xf6bb4b60); /* 39 */
        b = HH(b, c, d, a, groups[10], S34, 0xbebfbc70); /* 40 */
        a = HH(a, b, c, d, groups[13], S31, 0x289b7ec6); /* 41 */
        d = HH(d, a, b, c, groups[0], S32, 0xeaa127fa); /* 42 */
        c = HH(c, d, a, b, groups[3], S33, 0xd4ef3085); /* 43 */
        b = HH(b, c, d, a, groups[6], S34, 0x4881d05); /* 44 */
        a = HH(a, b, c, d, groups[9], S31, 0xd9d4d039); /* 45 */
        d = HH(d, a, b, c, groups[12], S32, 0xe6db99e5); /* 46 */
        c = HH(c, d, a, b, groups[15], S33, 0x1fa27cf8); /* 47 */
        b = HH(b, c, d, a, groups[2], S34, 0xc4ac5665); /* 48 */

        /*第四轮*/
        a = II(a, b, c, d, groups[0], S41, 0xf4292244); /* 49 */
        d = II(d, a, b, c, groups[7], S42, 0x432aff97); /* 50 */
        c = II(c, d, a, b, groups[14], S43, 0xab9423a7); /* 51 */
        b = II(b, c, d, a, groups[5], S44, 0xfc93a039); /* 52 */
        a = II(a, b, c, d, groups[12], S41, 0x655b59c3); /* 53 */
        d = II(d, a, b, c, groups[3], S42, 0x8f0ccc92); /* 54 */
        c = II(c, d, a, b, groups[10], S43, 0xffeff47d); /* 55 */
        b = II(b, c, d, a, groups[1], S44, 0x85845dd1); /* 56 */
        a = II(a, b, c, d, groups[8], S41, 0x6fa87e4f); /* 57 */
        d = II(d, a, b, c, groups[15], S42, 0xfe2ce6e0); /* 58 */
        c = II(c, d, a, b, groups[6], S43, 0xa3014314); /* 59 */
        b = II(b, c, d, a, groups[13], S44, 0x4e0811a1); /* 60 */
        a = II(a, b, c, d, groups[4], S41, 0xf7537e82); /* 61 */
        d = II(d, a, b, c, groups[11], S42, 0xbd3af235); /* 62 */
        c = II(c, d, a, b, groups[2], S43, 0x2ad7d2bb); /* 63 */
        b = II(b, c, d, a, groups[9], S44, 0xeb86d391); /* 64 */

        /*加入到之前计算的结果当中*/
        result[0] += a;
        result[1] += b;
        result[2] += c;
        result[3] += d;
        result[0]=result[0]&0xFFFFFFFF;
        result[1]=result[1]&0xFFFFFFFF;
        result[2]=result[2]&0xFFFFFFFF;
        result[3]=result[3]&0xFFFFFFFF;
    }

    /**
     * 下面是处理要用到的线性函数
     */
    private static int F(int x, int y, int z) {
        return (x & y) | ((~x) & z);
    }

    private static int G(int x, int y, int z) {
        return (x & z) | (y & (~z));
    }

    private static int H(int x, int y, int z) {
        return x ^ y ^ z;
    }

    private static int I(int x, int y, int z) {
        return y ^ (x | (~z));
    }

    private static int FF(int a, int b, int c, int d, int x, int s,
                          int ac) {
        a += (F(b, c, d)&0xFFFFFFFF) + x + ac;
        //<<<s表示循环左移s位,解决Java中无该运算符
        a = ((a&0xFFFFFFFF)<< s) | ((a&0xFFFFFFFF) >>> (32 - s));
        a += b;
        return (a&0xFFFFFFFF);
    }

    private static int GG(int a, int b, int c, int d, int x, int s,
                          int ac) {
        a += (G(b, c, d)&0xFFFFFFFF) + x + ac;
        a = ((a&0xFFFFFFFF) << s) | ((a&0xFFFFFFFF) >>> (32 - s));
        a += b;
        return (a&0xFFFFFFFF);
    }

    private static int HH(int a, int b, int c, int d, int x, int s,
                          long ac) {
        a += (H(b, c, d)&0xFFFFFFFF) + x + ac;
        a = ((a&0xFFFFFFFF) << s) | ((a&0xFFFFFFFF) >>> (32 - s));
        a += b;
        return (a&0xFFFFFFFF);
    }

    private static int II(int a, int b, int c, int d, int x, int s,
                          long ac) {
        a += (I(b, c, d)&0xFFFFFFFF) + x + ac;
        a = ((a&0xFFFFFFFF) << s) | ((a&0xFFFFFFFF) >>> (32 - s));
        a += b;
        return (a&0xFFFFFFFF);
    }
    //清除缓存,将int[] result={A,B,C,D},还原为初始状态
    public  void reset() {
        result[0] = A;
        result[1] = B;
        result[2] = C;
        result[3] = D;
    }

    public static void main(String []args){
        MD5 md=new MD5();
        byte[] digest = md.digest("姓名=蕾蕾,支付=1000,账户:123");
        String md5code = new BigInteger(1, digest).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        System.out.println(md5code);
    }
}

运行结果:

对比Java 自带的MD5 加密结果一样。

破解MD5方法有:王小云破解。

参考:https://blog.csdn.net/JunhuiXie/article/details/100630841#%E7%AC%AC%E4%B8%80%E6%AD%A5%3A%E5%A4%84%E7%90%86%E5%8E%9F%E6%96%87%C2%A0

原文地址:https://www.cnblogs.com/jssj/p/12001431.html

时间: 2024-09-28 22:43:23

《Java知识应用》Java加密方式(MD5)详解的相关文章

java笔记--反射进阶之总结与详解

一.反射进阶之动态设置类的私有域 "封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的, 然后通过提供相对应的set和get方法来操作这个域.但是我们仍然可以用java的反射机制来 修改类的私有域,由于修改类的私有域会破坏Java"封装"的特性,故请慎重操作. 主要技术:     Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限.     访问的字段可能是一个类(静态)字段或实例字段.             常

Java开源生鲜电商平台-Java后端生成Token架构与设计详解(源码可下载)

Java开源生鲜电商平台-Java后端生成Token架构与设计详解(源码可下载) 目的:Java开源生鲜电商平台-Java后端生成Token目的是为了用于校验客户端,防止重复提交. 技术选型:用开源的JWT架构. 1.概述:在web项目中,服务端和前端经常需要交互数据,有的时候由于网络相应慢,客户端在提交某些敏感数据(比如按照正常的业务逻辑,此份数据只能保存一份)时,如果前端多次点击提交按钮会导致提交多份数据,这种情况我们是要防止发生的. 2.解决方法: ①前端处理:在提交之后通过js立即将按钮

Java知多少(54)断言详解

断言的概念 断言用于证明和测试程序的假设,比如“这里的值大于 5”.断言可以在运行时从代码中完全删除,所以对代码的运行速度没有影响. 断言的使用 断言有两种方法: 一种是 assert<<布尔表达式>> : 另一种是 assert<<布尔表达式>> :<<细节描述>>. 如果布尔表达式的值为false , 将抛出AssertionError 异常: 细节描述是AssertionError异常的描述文本使用 javac –source

Java的String和StringBuffer和StringBuilder详解

Java的String和StringBuffer和StringBuilder详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 前言 最近发现团队成员在Java代码方面的质量不够高,准备写一些基础的文章,供大家参考. 一.定义 String是不可变字符序列. StringBuffer是可变的字符序列. StringBuilder也是可变的字符序列. 1.StringBuffer和StringBuilder的唯一区别 StringBuffer对象是线

Java研究之文件路径的读取详解

 记得在操作系统中了解到文件读取有两种方式,当然这在各编程语言中也是通用的,所以java路径也分,相对和绝对路径.上章我们分享了Java研究之学习设计模式-组合模式详解有兴趣的朋友可以去看下. 绝对路径 绝对路径URI ,听着和URL很相似,那我们就来看看吧. URI(Uniformresource Identifier)统一资源标示符.URL统一资源定位符,是一个定位器,还说明了具体如何找到资源.所以他们就有一种抽象和继承的关系.URI抽象的说明了统一资源表示符号,而URL是具体的标识符的

Java进阶(三十二) HttpClient使用详解

Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性.因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入. 一.简介 HttpClient是A

“全栈2019”Java异常第九章:throws关键字详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异常第九章:throws关键字详解 下一章 "全栈2019"Java异常第十章:throw与throws区别详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Ja

Java基础13:反射与注解详解

Java基础13:反射与注解详解 什么是反射? 反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性. Oracle官方对反射的解释是 Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fi

“全栈2019”Java异常第十八章:Exception详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异常第十八章:Exception详解 下一章 "全栈2019"Java异常第十九章:RuntimeException详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复&quo

Java深入学习11:Lock锁详解

Java深入学习11:Lock锁详解 一.Lock锁是什么 java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) Lock 接口支持那些语义不同(重入.公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则.主要的实现是 ReentrantLock. Lock 实现提供了比 synchronized 关键字 更广泛的锁操作,它能以更优雅的方式处理线程同步问题.也就是说,Lock提供了比s