JAVA MD5加密

以下内容整理自网上:


简介


1、一些术语


明文:加密前的信息
密文:机密后的信息
算法:加密或解密的算法
密钥:算法使用的钥匙


2、一个简单的加密


将123456每位数字都加1后得到234567,
其中123456就是明文,234567就是密文,加密密钥就是1,加密算法是每位加


3、对称加密和非对称加密



123456-->234567的加密密钥就是1,加密算法是每位+

234567-->123456的解密密钥也是1,解密算法是每位-


其中加密算法(+)和解密算法(-)相对称,这种加密算法就称作对称加密,

同样,如果加密算法和解密算法不对称就称之为非对称加密。


4、常见算法


对称加密算法:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法,AES算法。

非对称加密算法:RSA、Elgamal、背包算法、Rabin、D-H、ECC。

经典的哈希算法:MD2、MD4、MD5 和 SHA-1(目的是将任意长输入通过算法变为固定长输出,且保证输入变化一点输出都不同,且不能反向解密)


5.经典的算法


AES(对称),RSA(非对称),MD5,SHA-1(哈希)


6、加密和摘要


加密和摘要,是不一样的,加密后的消息是完整的,具有解密算法,得到原始数据,摘要得到的消息是不完整的,通过摘要的数据,不能得到原始数据


7、MD5长度


有人说md5,128位,32位,16位,到底md5多长?

md5的长度,默认为128bit,也就是128个0和1的二进制串。

这样表达是很不友好的。

所以将二进制转成了16进制,每4个bit表示一个16进制,

所以128/4 = 32 换成16进制表示后,为32位了。


JAVA MD5加密_第1张图片



8、为什么网上还有md5是16位的呢?


网上有很多帖子,md5 32位 16位 加密 区别。

仔细观察admin生成的32位和16位的md5值


查询结果:

md5(admin,32) = 21232f297a57a5a743894a0e4a801fc3

md5(admin,16) = 7a57a5a743894a0e

其实16位的长度,是从32位md5值来的。是将32位md5去掉前八位,去掉后八位得到的。


9、MD5的作用


①一致性检验,最上面那个例子
②数字签名,还是最上面那个例子。只是把md5看出了一个指纹,按了个手印说明独一无二了。
③安全访问认证,这个就是平时系统设计的问题了。

在用户注册时,会将密码进行md5加密,存到数据库中。这样可以防止那些可以看到数据库数据的人,恶意操作了。

10、md5不能破解吗?


md5是不可逆的,也就是没有对应的算法,从生产的md5值逆向得到原始数据。

但是如果使用暴力破解,那就另说了。


11、md5是唯一的吗?


md5作为数据库中的主键可行吗?这就涉及到一个问题,md5值是唯一的吗?答案是,不唯一。

也就是一个原始数据,只对应一个md5值;

但是一个md5值,可能对应多个原始数据。


一、什么是MD5加密


MD5,全称为“Message Digest Algorithm 5”,中文名“消息摘要算法第五版”,它是计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。严格来说,它是一种摘要算法,是确保信息完整性的。不过,在某种意义上来说,也可以算作一种加密算法。

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

MD5 的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。

但实际上MD5不是一种加密算法,只是一种算法,因为它是一个不可逆的过程,即 MD5 生成消息摘要后是不能通过结果解析得到原来的内容。


二、MD5用途 


在上面也说到了

一、文件校验

MD5生成的消息摘要通常的用途是用来校验文件的完整性。 


JAVA MD5加密_第2张图片



通过接收到的文件进行MD5摘要校验对比,就能得出文件在传输过程中有没有被篡改。 


二、密码加密


由于MD5在生成后无法进行反向解析,加密后会像上面图中一样,只有一串16进制的字符数组,无法获取原本的内容是什么(其实上图中的MD5字符,是使用Java自带的MD5工具对“123456”进行处理后得到的);所以通常也可以用于传输一些私密信息,像用户密码。

以 Web 应用为例,前台页面输入密码后,可以通过MD5加密,然后直接将 MD5字符串 保存到数据库中,当用户登录时只需将数据库中的 MD5字符串获取并与之输入的密码MD5字符串比对即可通过验证。 


JAVA MD5加密_第3张图片


三、JAVA中实现MD5加密


在JAVA中有MD5的这个类,在java.security.MessageDigest包中,名字就是MessageDigest。此 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。


MessageDigest 通过其getInstance系列静态函数来进行实例化和初始化。MessageDigest 对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算并返回结果。

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


MessageDigest类:


  • 构造函数

  • protected MessageDigest(String algorithm) 创建具有指定算法名称的消息摘要。
    


  • 方法


  • Object clone() 如果实现是可克隆的,则返回一个克隆。
    byte[] digest(byte[] input) 使用指定的字节数对摘要执行最后更新,然后完成摘要计算。
    String getAlgorithm() 返回标识算法的字符串,与实现详细信息无关。
    int getDigestLength() 返回摘要的长度(以字节为单位),如果此操作不受提供程序支持并且实现不可克隆,则返回0。
    static MessageDigest getInstance(String algorithm) 返回实现指定摘要算法的MessageDigest对象。
    Provider getProvider() 返回此消息摘要对象的提供程序。
    static boolean isEqual(byte[] digesta, byte[] digestb) 比较两个摘要的平等性。
    void reset() 重置摘要以供进一步使用。
    String toString() 返回此消息摘要对象的字符串表示形式。
    void update(byte[] input) 使用指定的字节数更新摘要。

MessageDigest类使用步骤:

  1. 初始化MessageDigest对象
  2. 调用update方法传入要进行加密的byte数据
  3. (可选)调用reset方法重置要传入进行加密的数据
  4. 调用digest方法对传入的数据进行加密

具体步骤如下:


(1)生成MessageDigest对象

MessageDigest m=MessageDigest.getInstance("MD5");

分析:

MessageDigest类也是一个工厂类,其构造器是受保护的,不允许直接使用new MessageDigist( )来创建对象,而必须通过其静态方法getInstance( )生成MessageDigest对象。其中传入的参数指定计算消息摘要所使用的算法,常用的有"MD5","SHA"等。

(2)传入需要计算的字符串

m.update(x.getBytes("UTF8" ));

分析:

x为需要计算的字符串,update传入的参数是字节类型或字节类型数组,对于字符串,需要先使用getBytes( )方法生成字符串数组。


(3)计算消息摘要


byte s[ ]=m.digest( );

分析:

执行MessageDigest对象的digest( )方法完成计算,计算的结果通过字节类型的数组返回。

(4)处理计算结果

必要的话可以使用如下代码将计算结果s转换为字符串。

for (int i=0; i

完整程序如下:

public static void main(String[] args) {
  
		String x="pwd";
		MessageDigest m=MessageDigest.getInstance("MD5");
		m.update(x.getBytes("UTF8"));
		byte s[ ]=m.digest( );
		String result="";
		for (int i=0; i

普遍使用的三种加密方式


方式一:使用位运算符,将加密后的数据转换成16进制
方式二:使用格式化方式,将加密后的数据转换成16进制(推荐)
方式三:使用算法,将加密后的数据转换成16进制

   /** 
     * @param source 需要加密的字符串 
     * @param hashType 加密类型 (MD5 和 SHA) 
     * @return 
     */  
    public static String getHash(String source, String hashType) {
	
        // 用来将字节转换成 16 进制表示的字符  
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};  
  
        try {  
            MessageDigest md = MessageDigest.getInstance(hashType);  
            md.update(source.getBytes()); // 通过使用 update 方法处理数据,使指定的 byte数组更新摘要  
            byte[] encryptStr = md.digest(); // 获得密文完成哈希计算,产生128 位的长整数  
            char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符  
            int k = 0; // 表示转换结果中对应的字符位置  
            for (int i = 0; i < 16; i++) { // 从第一个字节开始,对每一个字节,转换成 16 进制字符的转换  
                byte byte0 = encryptStr[i]; // 取第 i 个字节  
                str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移  
                str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换  
            }  
            return new String(str); // 换后的结果转换为字符串  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
  
    /**@param source 需要加密的字符串 
     * @param hashType  加密类型 (MD5 和 SHA) 
     * @return 
     */  
    public static String getHash2(String source, String hashType) {  
        StringBuilder sb = new StringBuilder();  
        MessageDigest md5;  
        try {  
            md5 = MessageDigest.getInstance(hashType);  
            md5.update(source.getBytes());  
            for (byte b : md5.digest()) {  
                sb.append(String.format("%02X", b)); // 10进制转16进制,X 表示以十六进制形式输出,02 表示不足两位前面补0输出  
            }  
            return sb.toString();  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
  
    /**@param source 需要加密的字符串 
     * @param hashType  加密类型 (MD5 和 SHA) 
     * @return 
     */  
    public static String getHash3(String source, String hashType) { 
	
        // 用来将字节转换成 16 进制表示的字符  
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};  
          
        StringBuilder sb = new StringBuilder();  
        MessageDigest md5;  
        try {  
            md5 = MessageDigest.getInstance(hashType);  
            md5.update(source.getBytes());  
            byte[] encryptStr = md5.digest();  
            for (int i = 0; i < encryptStr.length; i++) {  
                int iRet = encryptStr[i];  
                if (iRet < 0) {  
                    iRet += 256;  
                }  
                int iD1 = iRet / 16;  
                int iD2 = iRet % 16;  
                sb.append(hexDigits[iD1] + "" + hexDigits[iD2]);  
            }  
            return sb.toString();  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  


四、MD5与Base64结合使用


Base64: 把任意序列的8为字节描述为一种不易为人识别的形式, 通常用于邮件、http加密. 登陆的用户名和密码字段通过它加密, 可以进行加密和解密.


package com.fendo.MD5;

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

import sun.misc.BASE64Encoder;

/**   
 * @Title: Temps.java 
 * @Package com.fendo.MD5 
 * @Description: TODO
 * @author fendo
 * @date 2017年9月11日 上午10:27:12 
 * @version V1.0   
*/
public class Temps {

   /** 
     * 使用md5的算法进行加密 
     * @param plainText 加密明文 
     * @return 加密密文 
     */  
    public static String getDigest(String plainText) {  
        byte[] secretBytes = null;  
        try {  
            secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());  
        } catch (NoSuchAlgorithmException e) {  
            throw new RuntimeException("error happens", e);  
        }  
        return new BigInteger(1, secretBytes).toString(16);  
    }  
    
    /** 
     * 使用Base64进行编码 
     * @param encodeContent 需要编码的内容 
     * @return 编码后的内容 
     */  
    public static  String encode(String encodeContent) {   
        if (encodeContent == null) {  
            return null;  
        }  
        BASE64Encoder encoder = new BASE64Encoder();   
        return encoder.encode(encodeContent.getBytes());  
    }
    
    /** 
     * 先使用MD5算法加密, 再使用base64算法进行编码 
     * @param args 
     */  
    public static void main(String[] args) {  
         String plainText = "pwd";    
         String encodedPassword = getDigest(encode(plainText));   
         System.out.println(encodedPassword);  
    }  
}

为什么使用MD5加密后还要使用Base64编码呢? 


用Base64算法编码后得到的是32位长度的字符串, 这样有利于在数据库中进行存储. 


五、MD5Utils帮助类


package com.fendo.MD5;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**   
 * @Title: MD5Utils.java 
 * @Package com.fendo.MD5 
 * @Description: TODO
 * @author fendo
 * @date 2017年9月11日 上午11:56:20 
 * @version V1.0   
*/
public class MD5Utils {

    /** 16进制的字符串数组 */
    private final static String[] hexDigitsStrings = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
            "e", "f" };
    
    /** 16进制的字符集 */
    private final static char [] hexDigitsChar = {'0', '1', '2', '3', '4', '5', '6', '7', '8',  
            '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 
    
    /** 
     * MD5加密字符串
     * 
     * @param source 源字符串 
     * 
     * @return 加密后的字符串 
     * 
     */  
    public static String getMD5(String source) {  
        String mdString = null;  
        if (source != null) {  
            try {  
                mdString = getMD5(source.getBytes("UTF-8"));  
            } catch (UnsupportedEncodingException e) {  
                e.printStackTrace();  
            }  
        }  
        return mdString;  
    }
    
    /** 
     * MD5加密以byte数组表示的字符串
     * 
     * @param source 源字节数组 
     * 
     * @return 加密后的字符串 
     */  
    public static String getMD5(byte[] source) {  
        String s = null;  
 
        final int temp = 0xf;  
        final int arraySize = 32;  
        final int strLen = 16;  
        final int offset = 4;  
        try {  
            java.security.MessageDigest md = java.security.MessageDigest  
                    .getInstance("MD5");  
            md.update(source);  
            byte [] tmp = md.digest();  
            char [] str = new char[arraySize];  
            int k = 0;  
            for (int i = 0; i < strLen; i++) {  
                byte byte0 = tmp[i];  
                str[k++] = hexDigitsChar[byte0 >>> offset & temp];  
                str[k++] = hexDigitsChar[byte0 & temp];  
            }  
            s = new String(str);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return s;  
    }
    
    /**
     * * 获取文件的MD5值
     * 
     * @param file
     *            目标文件
     * 
     * @return MD5字符串
     * @throws Exception 
     */
    public static String getFileMD5String(File file) throws Exception {
        String ret = "";
        FileInputStream in = null;
        FileChannel ch = null;
        try {
            in = new FileInputStream(file);
            ch = in.getChannel();
            ByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
                    file.length());
            MessageDigest messageDigest=MessageDigest.getInstance("MD5");
            messageDigest.update(byteBuffer);
            ret = byteArrayToHexString(messageDigest.digest());
        } catch (IOException e) {
            e.printStackTrace();

        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ch != null) {
                try {
                    ch.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return ret;
    }

    /**
     * * 获取文件的MD5值
     * 
     * @param fileName
     *            目标文件的完整名称
     * 
     * @return MD5字符串
     * @throws Exception 
     */
    public static String getFileMD5String(String fileName) throws Exception {
        return getFileMD5String(new File(fileName));
    }
    
    /**
     * 加密
     * 
     * @param source
     *            需要加密的原字符串
     * @param encoding
     *            指定编码类型
     * @param uppercase
     *            是否转为大写字符串
     * @return
     */
    public static String MD5Encode(String source, String encoding, boolean uppercase) {
        String result = null;
        try {
            result = source;
            // 获得MD5摘要对象
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            // 使用指定的字节数组更新摘要信息
            messageDigest.update(result.getBytes(encoding));
            // messageDigest.digest()获得16位长度
            result = byteArrayToHexString(messageDigest.digest());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return uppercase ? result.toUpperCase() : result;
    }
    
    
    /**
     * 转换字节数组为16进制字符串
     * 
     * @param bytes
     *            字节数组
     * @return
     */
    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte tem : bytes) {
            stringBuilder.append(byteToHexString(tem));
        }
        return stringBuilder.toString();
    }
    
    /**
     * * 将字节数组中指定区间的子数组转换成16进制字符串
     * 
     * @param bytes
     *            目标字节数组
     * 
     * @param start
     *            起始位置(包括该位置)
     * 
     * @param end
     *            结束位置(不包括该位置)
     * 
     * @return 转换结果
     */
    public static String bytesToHex(byte bytes[], int start, int end) {
        StringBuilder sb = new StringBuilder();
        for (int i = start; i < start + end; i++) {
            sb.append(byteToHexString(bytes[i]));
        }
        return sb.toString();

    }
    
    /**
     * 转换byte到16进制
     * 
     * @param b
     *            要转换的byte
     * @return 16进制对应的字符
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigitsStrings[d1] + hexDigitsStrings[d2];
    }
    
    

    
    /**
     * * 校验密码与其MD5是否一致
     * 
     * @param pwd
     *            密码字符串
     * 
     * @param md5
     *            基准MD5值
     * 
     * @return 检验结果
     */
    public static boolean checkPassword(String pwd, String md5) {
        return getMD5(pwd).equalsIgnoreCase(md5);
    }
    
    /**
     * * 校验密码与其MD5是否一致
     * 
     * @param pwd
     *            以字符数组表示的密码
     * 
     * @param md5
     *            基准MD5值
     * 
     * @return 检验结果
     */
    public static boolean checkPassword(char[] pwd, String md5) {
        return checkPassword(new String(pwd), md5);

    }
    
    
    /**
     * * 检验文件的MD5值
     * 
     * @param file
     *            目标文件
     * 
     * @param md5
     *            基准MD5值
     * 
     * @return 检验结果
     * @throws Exception 
     */
    public static boolean checkFileMD5(File file, String md5) throws Exception {
        return getFileMD5String(file).equalsIgnoreCase(md5);

    }
    
    /**
     * * 检验文件的MD5值
     * 
     * @param fileName
     *            目标文件的完整名称
     * 
     * @param md5
     *            基准MD5值
     * 
     * @return 检验结果
     * @throws Exception 
     */
    public static boolean checkFileMD5(String fileName, String md5) throws Exception {
        return checkFileMD5(new File(fileName), md5);

    }
}


你可能感兴趣的:(Java)