基于MessageDigest类编写MD5通用工具类

(一)基本介绍

Java的MessageDigest 类 能够提供MD5算法或SHA算法用于计算出信息的摘要;它接收任意大小的信息,并输出计算后的固定长度的哈希值。这个输出的哈希值就是咱们所说的信息摘要。java

MD5算法获得的摘要是固定长度为 128 bit 的二进制串(一堆0和1,一共128个),为了更友好的表示摘要,通常都将 128位的二进制串 转为 32个16进制位16个16进制位 以下:git

栏目1 栏目2
原始信息 1234567890
16位小写摘要 f82d132f9bb018ca
16位大写摘要 F82D132F9BB018CA
32位小写摘要 e807f1fcf82d132f9bb018ca6738a19f
32位大写摘要 E807F1FCF82D132F9BB018CA6738A19F

(二)16位的摘要?

MD5算法获得的摘要固定长度为 128 bit 也就是32个16进制位(32位摘要),那么怎么获得16位的摘要呢? 其实从上面的表格中能够看出,16位的摘要是32位摘要值的中间部分,即32位摘要中第8~24位的部分。算法

(三)MessageDigest类计算MD5摘要的步骤

1,实例化MessageDigest对象

public static MesageDigest getInstance(String algorithm);
复制代码

算法名不区分大小写,因此下面的写法都是正确的:数据库

MesageDigest getInstance("MD5");
MesageDigest getInstance("md5");
MesageDigest getInstance("mD5");
复制代码

2,向MessageDigest传送信息

该步骤就是调用下面的某个方法来完成信息的传递。数组

public void update(byte input);
public void update(byte[] input);
public void update(byte[] input, int offset, int len);
复制代码

3,计算信息的摘要

最后调用下面的某个方法来计算摘要。bash

public byte[] digest();
public byte[] digest(byte[] input);
public int digest(byte[] buf,int offset,int len);
复制代码

4,将摘要转为16进制位的字符串

为了更友好的表示摘要,通常都将 128位的二进制串 转为 32个16进制位16个16进制位 ,并以字符串的形式表示。app

摘要通常以字符串的形式展现,因此在WEB应用中,用于表示密码的MD5摘要的数据库字段通常设置为String password函数

虽然字段名的字面意思表示帐户密码,但实际上只是帐户密码的MD5摘要,参考 摘要与加密的区别(以MD5算法为例)工具

(四)基于MessageDigest类的MD5通用工具类实现

实现MD5通用工具类的方法有不少,下面我用清晰明了的代码去实现MD5Util通用工具类。post

1,MD5工具类完整代码

import java.security.MessageDigest;

public class MD5Util {

    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 将1个字节(1 byte = 8 bit)转为 2个十六进制位
     * 1个16进制位 = 4个二进制位 (即4 bit)
     * 转换思路:最简单的办法就是先将byte转为10进制的int类型,而后将十进制数转十六进制
     */
    private static String byteToHexString(byte b) {
        // byte类型赋值给int变量时,java会自动将byte类型转int类型,从低位类型到高位类型自动转换
        int n = b;

        // 将十进制数转十六进制
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;

        // d1和d2经过访问数组变量的方式转成16进制字符串;好比 d1 为12 ,那么就转为"c"; 由于int类型不会有a,b,c,d,e,f等表示16进制的字符
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * 将字节数组里每一个字节转成2个16进制位的字符串后拼接起来
     */
    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++){
            resultSb.append(byteToHexString(b[i]));
        }
        return resultSb.toString();
    }

    /**
     * MD5算法,统一返回大写形式的摘要结果,默认固定长度是 128bit 即 32个16进制位
     * String origin :须要进行MD5计算的字符串
     * String charsetname :MD5算法的编码
     */
    private static String MD5_32(String origin, String charsetname) {
        String resultString = null;
        try {
            // 1,建立MessageDigest对象
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 2,向MessageDigest传送信息;传入的信息须要转化为指定编码的字节数组
            md.update(origin.getBytes( charsetname ));
            // 3,计算摘要
            byte[] bytesResult = md.digest();

            // 第2步和第3步能够合并成下面一步
            // byte[] bytesResult = md.digest(origin.getBytes(charsetname));

            // 4,将字节数组转换为16进制位
            resultString = byteArrayToHexString( bytesResult );
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 统一返回大写形式的字符串摘要
        return resultString.toUpperCase();

    }

    /**
     * 获取 16位的MD5摘要,就是截取32位结果的中间部分
     */
    private static String MD5_16(String origin, String charsetname) {
        return MD5_32(origin, charsetname).substring(8,24);
    }

    public static void main(String[] args){
        String origin = "1234567890";

        // 默认MD5计算获得128 bit的摘要,即32个 16进制位
        String result_32 = MD5_32(origin, "utf-8");
        System.out.println(result_32);  // E807F1FCF82D132F9BB018CA6738A19F

        // 获取16位的摘要
        String result_16 = MD5_16(origin, "utf-8");
        System.out.println(result_16);  // F82D132F9BB018CA
    }
}
复制代码

2,没有调用update方法?

在不少时候,咱们常常看到相似下面的写法:

byte[] bytesResult = md.digest(origin.getBytes(charsetname));
复制代码

在这种写法中并无代码显式地调用update方法,但跟下面的写法是同样的,实际上也调用了update方法:

// 2,向MessageDigest传送信息;传入的信息须要转化为指定编码的字节数组
md.update(origin.getBytes( charsetname ));
// 3,计算摘要
byte[] bytesResult = md.digest();
复制代码

查看该digest(byte[] input)方法的源码,能够看到该方法其实就是将第2步和第3步封装成一个函数而已。

public byte[] digest(byte[] input) {
    update(input);
    return digest();
}
复制代码
相关文章
相关标签/搜索