Java如何获取MD5值

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/iblade/article/details/73288822

MD5,Message Digest Algorithm 5,是一种被广泛使用的信息摘要算法,可以将给定的任意长度数据通过一定的算法计算得出一个 128 位固定长度的散列值。如百科介绍,MD5 具有如下特点:

  • 压缩性:任意长度的原数据,其 MD5 值都是固定的,即 128 位;

  • 易计算:计算原数据的 MD5 值是一个比较容易的过程;

  • 抗修改:原数据的任意改动,所得到的 MD5 值都是迥然不同的;

  • 防碰撞:这一点要特别介绍一下。MD5 使用的是散列函数(也称哈希函数),一定概率上也存在哈希冲突(也称哈希碰撞),即多个不同的原数据对应一个相同的 MD5 值。不过,经过 MD4、MD3 等几代算法的优化,MD5 已经充分利用散列的分散性高度避免碰撞的发生。
    可以看出,MD5 是一种不可逆的算法,也就说,你无法通过得到的 MD5 值逆向算出原数据内容。正是凭借这些特点,MD5 被广泛使用。

比如,客户端与服务器的 HTTP 通信,通信双方可以将报文内容做一个 MD5 计算,并将计算所得 MD5 值一并传递给彼此,这样,接收方可以通过对报文内容再次做 MD5 计算得到一个 MD5 值,与传递报文中的 MD5 值做比较,验证数据是否完整,或者是否中途被拦截篡改过。

再比如,网络云盘中的文件秒传功能也运用到 MD5 算法。服务器存储文件的时候,同时记录每一个文件的 MD5 值,不同文件对应着不同的 MD5 值。这样,遇到用户上传文件时,将上传文件的 MD5 值与服务器上所有存储的 MD5 值做比较,如果相同,则说明用户上传的文件已经在服务器存有。这样,只需要在数据库表中添加一个记录,映射到对应的文件,而不用重复上传,实现所谓秒传的功能。

当然,这只是常见的两个例子,MD5 的用途大有所在。值得注意的是,严格意义上来讲,MD5 以及 SHA1 并不属于加密算法,也不属于签名算法,而是一种摘要算法,用于数据完整性校验等。

了解完基本的 MD5 概念,再来看看 Java 语言中计算 MD5 值的实现方式。

   /**
     * 获取String的MD5值
     *
     * @param info 字符串
     * @return 该字符串的MD5值
     */
    private static String getMD5(String info) {
        try {
            //获取 MessageDigest 对象,参数为 MD5 字符串,表示这是一个 MD5 算法(其他还有 SHA1 算法等):
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            //update(byte[])方法,输入原数据
            //类似StringBuilder对象的append()方法,追加模式,属于一个累计更改的过程
            md5.update(info.getBytes("UTF-8"));
            //digest()被调用后,MessageDigest对象就被重置,即不能连续再次调用该方法计算原数据的MD5值。可以手动调用reset()方法重置输入源。
            //digest()返回值16位长度的哈希值,由byte[]承接
            byte[] md5Array = md5.digest();
            //byte[]通常我们会转化为十六进制的32位长度的字符串来使用,本文会介绍三种常用的转换方法
            return bytesToHex1(md5Array);
        } catch (NoSuchAlgorithmException e) {
            return "";
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    @NonNull
    private static String bytesToHex1(byte[] md5Array) {
        StringBuilder strBuilder = new StringBuilder();
        for (int i = 0; i < md5Array.length; i++) {
            int temp = 0xff & md5Array[i];//TODO:此处为什么添加 0xff & ?
            String hexString = Integer.toHexString(temp);
            if (hexString.length() == 1) {//如果是十六进制的0f,默认只显示f,此时要补上0
                strBuilder.append("0").append(hexString);
            } else {
                strBuilder.append(hexString);
            }
        }
        return strBuilder.toString();
    }

    //通过java提供的BigInteger 完成byte->HexString
    private static String bytesToHex2(byte[] md5Array) {
        BigInteger bigInt = new BigInteger(1, md5Array);
        return bigInt.toString(16);
    }

    //通过位运算 将字节数组到十六进制的转换
    public static String bytesToHex3(byte[] byteArray) {
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] resultCharArray = new char[byteArray.length * 2];
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        return new String(resultCharArray);
    }

下面看一下文件如何获取MD5:

public static String fileToMD5(String path){
    try {
        FileInputStream fis = new FileInputStream(path);
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            digest.update(buffer, 0, len);
        }
        fis.close();
        BigInteger bigInt = new BigInteger(1, digest.digest());
        return  bigInt.toString(16);
    } catch (IOException | NoSuchAlgorithmException e){
        e.printStackTrace();
    }
    return "";
}

文中有几个比较关键的点需要特别嘱文说明:
1.byte为什么 要 与上0xff?
2.BigInteger是何方神圣?

展开阅读全文

没有更多推荐了,返回首页