【Java】聊聊常用的摘要算法,比如MD5

时间:2023-03-09 22:10:13
【Java】聊聊常用的摘要算法,比如MD5

摘要算法的特性

  • 摘要算法的目的的将信息进行简单地摘要,将任意长的信息摘要成固定长的信息。比如MD5,将任意长的信息摘要成128位的摘要。
  • 不可逆的,将报文摘要成一段信息后,无法通过摘要信息还原会报文。
  • 冲突性。一份报文跟其他报文的摘要信息有可能是一致的,即冲突的。一般来说,摘要算法会设计得冲突性尽量小。

常用的摘要算法有:MD5、SHA(Secure Hash Algorithm)等。下面介绍MD5。

MD5

MD,全文是Message Digest,即信息摘要的意思,是常用的信息摘要算法。

它是一种不可逆的算法,也就是说,假如将一个信息MD5摘要成一串代码,将不能通过这串代码还原回原来的信息。

使用的场景

出于这种特性,MD5常用来校验密码是否正确、校验下载文件是否完整无损。

-> 校验密码是否正确:将用户注册的密码MD5摘要后储存起来,待用户登录时将用户录入的密码MD5摘要,对比两次摘要后的信息是否相等,相等即密码正确。(这里如果加盐会更好,见后面章节)

-> 校验下载文件是否完整无损:我们常下载开源文件资源包时,可以看到网站上提供的该资源包的MD5(比如下载POI,如图一),下载文件完毕后,可通过自己的程序或直接去一些网站上计算其MD5码,如果与官网上提供的一致,则表示文件完整无损。(图二第3行日志可见,以下程序计算的MD5码与图一的官网截图一致)

直接进行MD5是否足够,为什么要加盐

如图一可见,123456的MD5码为e10adc3949ba59abbe56e057f20f883e。一般来说,MD5摘要的结果是128位的摘要信息,然后每4位用一个16进制字符表示,所有,MD5摘要的结果一般显示为32位的16进制。

如果你的系统用MD5摘要,并且无加盐,还经常使用123456为测试密码,对这一串MD5码一定很熟悉。这一些MD5码就静静地躺在DB中待校验。

如果数据库被暴露(比如拖库),坏人得到了这些MD5码,就可以通过常用密码与其MD5码的映射关系,轻而易举地翻译出大多数密码。

所以,一般来说,我们需要对原始密码进行加盐,所谓加盐,就是按照一定规则扰乱原有字符串,然后再进行MD5摘要。这个规则,自己定义,并且一定保密。

调用Spring工具类获取MD5码

目前Spring应用广泛,我们就直接使用Spring的api获取MD5码了。

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.15.RELEASE</version>
</dependency>
package com.nicchagil.md5study;

import org.springframework.util.DigestUtils;

public class MD5UtilBySpring {

    /**
* 使用MD5作信息摘要,并以十六进制表示
*/
public static String md5(byte[] bytes) {
return DigestUtils.md5DigestAsHex(bytes);
} /**
* 使用MD5作信息摘要,并以十六进制表示
*/
public static String md5(String s) {
if (s == null || s.length() == 0) {
return null;
}
return MD5UtilBySpring.md5(s.getBytes());
} }
package com.nicchagil.md5study;

public class Salter {

    public static final String PREFIX = "HOW";
public static final String FILLING = "ARE";
public static final Integer FILLING_INDEX = 5;
public static final String POSTFIX = "YOU!"; public static String salt(String source) {
if (source == null || source.length() == 0) {
return null;
} StringBuffer sb = new StringBuffer(source);
if (sb.length() > FILLING_INDEX) {
sb.insert(FILLING_INDEX, FILLING);
} return sb.insert(0, PREFIX).append(POSTFIX).toString();
} }
package com.nicchagil.md5study;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream; import org.junit.Test; public class HowToUse { @Test
public void test() throws Exception {
String source = "123456";
String md5Hex = MD5UtilBySpring.md5(source);
System.out.println(source + "'s hex md5 -> " + md5Hex);
} @Test
public void test2() throws Exception {
File file = new File("d:/poi-bin-3.13-20150929.tar.gz"); FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
fis = new FileInputStream(file);
baos = new ByteArrayOutputStream(); byte[] bytes = new byte[1024];
int size = 0;
while ((size = fis.read(bytes)) != -1) {
if (size == 1024) {
baos.write(bytes);
} else {
baos.write(bytes, 0, size);
}
} byte[] resultBytes = baos.toByteArray();
System.out.println("size -> " + resultBytes.length);
String md5Hex = MD5UtilBySpring.md5(resultBytes);
System.out.println("Hex md5 -> " + md5Hex);
} finally {
if (fis != null) {
fis.close();
}
if (baos != null) {
baos.close();
}
}
} @Test
public void test3() throws Exception {
String source = "123456";
String saltSource = Salter.salt(source);
String md5 = MD5UtilBySpring.md5(saltSource);
System.out.println(source + " salt -> " + saltSource + " MD5 -> " + md5); source = "12345";
saltSource = Salter.salt(source);
md5 = MD5UtilBySpring.md5(saltSource);
System.out.println(source + " salt -> " + saltSource + " MD5 -> " + md5); source = "1234";
saltSource = Salter.salt(source);
md5 = MD5UtilBySpring.md5(saltSource);
System.out.println(source + " salt -> " + saltSource + " MD5 -> " + md5);
} }

图一

aaarticlea/png;base64,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" alt="" />

图二

aaarticlea/png;base64,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" alt="" />