什么是MD5?
基本概念
MD5,Message Digest Algorithm MD5(消息摘要算法第五版)是计算机安全领域广泛使用的的一种散列函数,用于确保信息传输完整一致。由MD2、MD3、MD4演变过来的,是一种单向加密算法,是不可逆的一种的加密方式。
将数据(如一段文字)运算变为另一固定长度值,是散列算法的基础原理。
特点
MD5算法具有以下特点:
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
- 容易计算:从原数据计算出MD5值很容易。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。这里“碰撞”我们理解为一种破解手段。
散列
一般128位的MD5散列被表示为32位十六进制数字。以下是一个43位长的仅ASCII字母列的MD5散列:
MD5("The quick brown fox jumps over the lazy dog")
= 9e107d9d372bb6826bd81d3542a419d6
即使在原文中作一个小变化(比如用c取代d)其散列也会发生巨大的变化:
MD5("The quick brown fox jumps over the lazy cog")
= 1055d3e698d289f2af8663725127bd4b
空文的散列为:
MD5("")
= d41d8cd98f00b204e9800998ecf8427e
MD5的应用
一致性验证
MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。
MD5将整个文件当作一个大文本信息,通过其不可逆的字符串变换算法,产生了这个唯一的MD5信息摘要。
比如下载服务器针对一个文件预先提供一个MD5值,用户下载完该文件后,用我这个算法重新计算下载文件的MD5值,通过比较这两个值是否相同,就能判断下载的文件是否出错,或者说下载的文件是否被篡改了。数字签名
文件认证。消息摘要用于生成唯一且可靠的数据标识符。它们有时被称为数据的“数字指纹”。安全访问认证
MD5将任意长度的“字节串”映射为一个128bit的大整数,并且是通过该128bit反推原始字符串是困难的。例如,我们在Android中用户的密码使用MD5加密之后,再传给服务器。
Android中的实现
MessageDigest
在java.security这个包下有一个类MessageDigest,在Android中我们就是通过这个类的相关方法来实现计算文件的MD5。
使用方法:
//方法1:返回MessageDigest实例 algorithm算法名称
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {}
//方法2:更新计算消息摘要的数据内容
public void update(byte[] input) {}
//方法3:计算消息摘要
public byte[] digest(){}
进行初始化操作,需要指定算法
算法名称不区分大小写。例如,以下所有调用都是等同的:
MessageDigest.getInstance("SHA-1");
MessageDigest.getInstance("sha-1");
MessageDigest.getInstance("sHa-1");
进行消息摘要内容的更新
这个方法不是一定要调用的,但是对于计算很大的数据的MD5,比方文件。我们就需要通过分段读取多次调用 update( ) 方法来将数据更新给MessageDigest对象。我们要理解这个方法的是,调用执行update并没有计算MD5的值,真正计算的MD5值是调用digest()
void update(byte input)
void update(byte[] input)
void update(byte[] input, int offset, int len)
计算消息摘要的值并返回
当使用更新方法提供数据之后,通过调用digest( ) 方法来返回消息摘要。
byte[] digest()
byte[] digest(byte[] input)
int digest(byte[] buf, int offset, int len)
获取字符串的MD5
public static String getStringMD5(String sourceStr) {
String s = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
//这两行代码的作用是:
// 将bytes数组转换为BigInterger类型。1,表示 +,即正数。
BigInteger bigInt = new BigInteger(1, md.digest(sourceStr.getBytes()));
// 通过format方法,获取32位的十六进制的字符串。032,代表高位补0 32位,X代表十六进制的整形数据。
//为什么是32位?因为MD5算法返回的时一个128bit的整数,我们习惯于用16进制来表示,那就是32位。
s = String.format("%032x", bigInt);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return s;
}
获取文件的MD5
public static String getFileMD5(File file) {
String s = null;
if (!file.exists()) {
return null;
}
FileInputStream in = null;
byte buffer[] = new byte[1024];
int len;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
in = new FileInputStream(file);
while ((len = in.read(buffer, 0, 1024)) != -1) {
md.update(buffer, 0, len);
}
in.close();
BigInteger bigInt = new BigInteger(1, md.digest());
s = String.format("%032x", bigInt);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return s;
}
这里和读取字符串的MD5差别主要在文件读取和使用update方法更新数据。更新数据我们前面已经讲解过了。
对于文件的读取有很多种方式,例如通过
FileInputStream
读取字节流,也可以包装成InputStreamReader读取字节流,也可以包装成BufferedInputStream进行带缓冲区的读取,以及RandomAccessFile
或者nio 包中FileChannel
加内存映射的方式。
在Android中,我们推荐使用RandomAccessFile
方法,因为这种方式性能更好。更重要的:计算文件MD5值是一个比较耗时的操作,不要在主线程中计算。
RandomAccessFile方式:
...
//RandomAccessFile方式:
RandomAccessFile randomAccessFile = null;
byte buffer[] = new byte[1024];
int len;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
randomAccessFile = new RandomAccessFile(file,"r");
while ((len = randomAccessFile.read(buffer, 0, 1024)) != -1) {
md.update(buffer, 0, len);
}
randomAccessFile.close();
BigInteger bigInt = new BigInteger(1, md.digest());
s = String.format("%032x", bigInt);
} catch (Exception e) {
e.printStackTrace();
return null;
}
...
MD5的安全性
从MD5加密本身来讲这个过程是不可逆的,但不意味着MD5算法不可破解。但是破解对于我们将MD5算法应用于文件的一致性检验并没有什么太大的影响。这
这个大家有兴趣的可以了解一下:
中国的王小云教授带领的研究小组于2004年、2005年先后破解了被广泛应用于计算机安全系统的MD5和SHA-1两大密码算法。
——百度百科-王小云
参考文章
声明
- 由于互联网数据的分享性,如果我发表的文章,来源于您的原创文章,且我没有注明,请微博私信或者邮件macouen@gmail.com说明。
- 欢迎转载,但请注明文章原始出处。