DigestUtils调用md5Hex对用一个输入流连续加密返回不一样md5码

    关于org.apache.commons.codec.digest.DigestUtils的这个类,对一个输入流连续的进行两次加密,输出的MD5码不一样,若是屡次连续加密,则除过第一次获得的MD5码都一致java

    前一段儿时间在公司写接口的项目,webservice的那种,中途碰到一个问题。具体状况是别的公司调用咱们的接口上传一些数据,其中设计到MD5码加密的问题,传过来文件及MD5码,而后咱们去校验是否一致(此处有个疑问就是别人可能改文件也可能改MD5码的哇,到如今也没相通为啥要多这一步,,,固然了,这都是扯淡,跟本次问题没啥关系,继续说)。刚开始一切正常,忽然有一天一家保险公司的人跟咱们反馈MD5码校验一直不经过,问咱们是不是用同一个工具去进行MD5码加密的。查过程序没有问题之后,我将报文以及加密后的MD5码要了过来,本身编写了一个测试类测试了一下。web

package com.yl;

import java.io.File;
import java.io.FileInputStream;

import org.apache.commons.codec.digest.DigestUtils;

/**
 * MD5码建立测试类
 * @author 杨力
 *
 */
public class Test {

	/**
	 * 
	 * @param args
	 * @throws Exception
	 * @author 杨力
	 */
	public static void main(String[] args) throws Exception{
		//从本地c盘读入一个文件流
		FileInputStream fileInputStream = new FileInputStream(new File("C:/MyDrivers/123.txt"));
		//查看是否相同
		System.out.println("d41d8cd98f00b204e9800998ecf8427e".equals(DigestUtils.md5Hex(fileInputStream)));
		
	}
}

输出结果为falseapache

而后我就想看看他输出的MD5码是什么,我就加了一句话数组

package com.yl;

import java.io.File;
import java.io.FileInputStream;

import org.apache.commons.codec.digest.DigestUtils;

/**
 * MD5码建立测试类
 * @author 杨力
 *
 */
public class Test {

	/**
	 * 
	 * @param args
	 * @throws Exception
	 * @author 杨力
	 */
	public static void main(String[] args) throws Exception{
		//从本地c盘读入一个文件流
		FileInputStream fileInputStream = new FileInputStream(new File("C:/MyDrivers/123.txt"));
		//查看是否相同
		System.out.println("d41d8cd98f00b204e9800998ecf8427e".equals(DigestUtils.md5Hex(fileInputStream)));
		//查看MD5码
		System.out.println(DigestUtils.md5Hex(fileInputStream));
	}
}

对,我偷懒了一下,直接又进行了一遍加密。最后发现,尽管上边儿返回的是false,可是下边儿第二次输出的结果却和他发过来的MD5码一致。工具

我又将输出流重复加密之后,进行输出,结果得出了以下的结果测试

a2da77a5b5a6c6ba9f31d2d57cc36878
d41d8cd98f00b204e9800998ecf8427e
d41d8cd98f00b204e9800998ecf8427e
d41d8cd98f00b204e9800998ecf8427e加密

第一次的不一样, 其他的都相同。因而我试了试将文件读入两次,分别对其进行加密debug

package com.yl;

import java.io.File;
import java.io.FileInputStream;

import org.apache.commons.codec.digest.DigestUtils;

/**
 * MD5码建立测试类
 * @author 杨力
 *
 */
public class Test {

	/**
	 * 
	 * @param args
	 * @throws Exception
	 * @author 杨力
	 */
	public static void main(String[] args) throws Exception{
		//从本地c盘读入一个文件流
		FileInputStream fileInputStream = new FileInputStream(new File("C:/MyDrivers/123.txt"));
		FileInputStream fileInputStream2 = new FileInputStream(new File("C:/MyDrivers/123.txt"));
		//查看MD5码
		System.out.println(DigestUtils.md5Hex(fileInputStream));
		System.out.println(DigestUtils.md5Hex(fileInputStream2));
	}
}

结果输出了两个相同的MD5码。设计

到这里,我就想应该是同一个输入流在第一次进行加密的时候进行了什么改变。致使屡次加密与第一次结果不一样,因而我点开了DigestUtils的源码。code

/**
     * Calculates the MD5 digest and returns the value as a 32 character hex string.
     *
     * @param data
     *            Data to digest
     * @return MD5 digest as a hex string
     * @throws IOException
     *             On error reading from the stream
     * @since 1.4
     */
    public static String md5Hex(final InputStream data) throws IOException {
        //Hex.encodeHexString()方法是对byte[]进行加密,
        //在debug后整明md5()方法返回的byte[]数组已经发生了变化,此处跳过
        //有兴趣的能够深刻看看hex类是如何进行加密的
        return Hex.encodeHexString(md5(data));
    }
/**
     * Calculates the MD5 digest and returns the value as a 16 element <code>byte[]</code>.
     *
     * @param data
     *            Data to digest
     * @return MD5 digest
     * @throws IOException
     *             On error reading from the stream
     * @since 1.4
     */
    public static byte[] md5(final InputStream data) throws IOException {
        return digest(getMd5Digest(), data);
    }
/**
     * Read through an InputStream and returns the digest for the data
     *
     * @param digest
     *            The MessageDigest to use (e.g. MD5)
     * @param data
     *            Data to digest
     * @return the digest
     * @throws IOException
     *             On error reading from the stream
     */
    private static byte[] digest(final MessageDigest digest, final InputStream data) throws IOException {
        return updateDigest(digest, data).digest();
    }
/**
     * Reads through an InputStream and updates the digest for the data
     *
     * @param digest
     *            The MessageDigest to use (e.g. MD5)
     * @param data
     *            Data to digest
     * @return the digest
     * @throws IOException
     *             On error reading from the stream
     * @since 1.8
     */
    public static MessageDigest updateDigest(final MessageDigest digest, final InputStream data) throws IOException {
        final byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
        //此处注意
        int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);

        while (read > -1) {
            digest.update(buffer, 0, read);
            read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
        }

        return digest;
    }

看到这里时,我发现了inputStream流调用了read()方法。又生成了一个1024长度的byte数组,而且在读完的时候调用了digest.update方法。对该对象进行了操做。此处,我才发现问题,是在第一次加密的时候,已经将整个输入流读入完成。致使第二次操做的时候再次read()返回了-1,这样致使第二次之后的digest对象都是初始化的对象。缘由终于找到。

过后想一想,当时他们确定是在生成MD5码的时候调用了屡次,才会发生初始化MD5码与结果不相同。可是当时也没有看这么细,也就回复了一句咱们这边儿没有问题草草结束。最后他们应该是进行了更正,没有提出新的问题。