欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java计算文件MD5值

程序员文章站 2024-03-19 11:47:40
...

在文件上传下载相关的操作中,我们通常会遇到需要计算文件MD5的场景,计算文件MD5值的方法和计算字符串的MD5值有些类似,这里先来介绍普通的计算字符串的MD5方法。

commons-codec这个jar给我们提供了一个MD5实现,普通的MD5实现,基本大同小异,最终的结果也相同,这里不能说的太过,为什么称为普通的MD5,因为实现思路是一样的,而且只要是相同的字符串,计算的结果也一样,因此也很容易被**,虽然MD5这个算法本身是不可逆的,但是根据彩虹表的**方式,或者在线**,一些常用的密码很容易被猜到。

先来看看commons-codec提供的MD5的实现:

DigestUtils.md5Hex(String source);

就这么一行简单的代码就帮我们实现了MD5算法。

也有很多不用commons-codec提供的api,借助java security api实现MD5算法的,这里给出一个简单的示例:

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

static{
	try {
		digest = MessageDigest.getInstance("MD5");
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	}
}
public static String getMD5(String source){
	try {
		byte[] data = source.getBytes("UTF-8");
		return buffer2Hex(digest.digest(data));
	} catch (UnsupportedEncodingException e) {
		e.printStackTrace();
	}	
	return null;
}

public static String buffer2Hex(byte[] data){
	StringBuffer sb = new StringBuffer();
	for(int i=0;i<data.length;i++){
		int d = data[i];
		if(d<0){
			d+=256;
		}
		int d1 = d / 16;
		int d2 = d % 16;
		sb.append(strHex[d1]+strHex[d2]);
	}
	return sb.toString();
}

相信大家常见的MD5算法就是长这个样子的,其中,我们看到的复杂的部分就是在进行字节数组转十六进制。其实java提供的api已经帮助我们完成了MD5核心算法,我们这里进行的字节数组转16进制,其实是这个算法的冰山一角。

当然字节数组转16进制,也有很多办法,所以这个方法也有很多变种:

public static String buffer2Hex3(byte[] data){
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<data.length;i++){
            int d1 = data[i];
            if(d1<0){
                d1 = d1 & 0xff;
            }
            if(d1<16){
                sb.append("0");
            }
            sb.append(Integer.toHexString(d1));
        }
        return sb.toString();
}

借助BigInteger直接转16进制:    
public static String buffer2Hex4(byte[] data){
        BigInteger res = new BigInteger(1, data);
        return res.toString(16);

当然也有利用字符数组来替换前面的字符串数组的算法:

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

public static String buffer2Hex2(byte[] data){
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<data.length;i++){
            byte d = data[i];
            char d1 = digestHex[(d & 0xf0) >> 4];
            char d2 = digestHex[d & 0xf];
            sb.append(d1);
            sb.append(d2);
        }
        return sb.toString();
}

我们知道,这些不同的实现,核心的算法是字节数组转十六进制。那么对于文件而言,如何计算MD5,我们可以试想,如果把文件转为字节数组了,是不是就可以套用这个后面字节数组转十六进制的方法了。事实上,确实如此,我们只需要将文件读入内存,然后将其转为字节数组,剩下的就是和求字符串的MD5方法一样了。

commons-io提供了FileUtils.readFileToByteArray(File file)这个api,得到的结果就是byte[]类型的字节数组。所以求文件的MD5算法就有了这样简单的实现了:

public static String md5Hex(File file) {
        try {
            return DigestUtils.md5Hex(FileUtils.readFileToByteArray(file));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
  }

如果你有兴趣,可以将FileUtils.readFileToByteArray(file)方法替换为自己编写的一个简单的实现:通过IO流的方式将文件读入内存,然后将其转为byte[]类型的数组。

常见的字符串经过MD5加密之后的结果:

  • 123456->e10adc3949ba59abbe56e057f20f883e
  • admin ->21232f297a57a5a743894a0e4a801fc3
  • 111111->96e79218965eb72c92a549dd5a330112
  • 666666->f379eaf3c831b04de153469d1bec345e 

我们通过MessageDigest.digest(byte[]) 得到的数组,无论原始字符串或者文件多大,得到的字节数组,长度都是16,因此根据10进制转为16进制,结果都是32位的。再次说明了,普通的MD5算法还是有可能被**的。