MD5 是一种加密的消息摘要算法,可产生128位的哈希值。哈希函数采用任意大小的数据,并生成固定长度的哈希值。散列是一种单向函数,无法从散列中获取原始消息,并且两个不同的字符串都不能具有相同的散列值。在本文中,我们将学习使用MessageDigest, Guava 和 Apache Commons进行Java MD5哈希处理。
散列使我们能够验证通过通道传输的消息是否已被篡改,如果输入发生了很小的变化,则产生的散列将有所不同。尽管由于发现了多个漏洞,MD5不被认为是一种好的加密算法,但是它的校验和足以验证文件的完整性。
在进行编码之前,让我们首先对校验和有一些基本的了解
什么是校验和?
您是否曾经从互联网上下载某些文件(例如更新,补丁等)时注意到过,它们会提供校验和或MD5或SHA256等,这将是很长的乱序字符序列,称为校验和用于特定文件。校验和可确保通过网络传输的文件的完整性。
校验和是使用诸如MD5,SHA1,SHA256等哈希算法计算的。即使文件进行了很小的修改,文件的校验和也会更改,下载文件的用户将计算已下载文件的校验和,并且两者应该匹配,如果不匹配,那么我们可以假设文件已被篡改。
让我们看看到例如,我们有两个文件下面的内容为文件1是的“Hello World”和内容文件2是的“Hello World”。。我们在file2中添加的所有内容都是一个点(。),但所得的校验和不同。
文件1校验和:b10a8db164e0754105b7a99be72e3fe5
文件2校验和:d7527e2509d7b3035d23dd6701f5d8d0
让我们深入研究代码
Java MD5散列示例
package MD5HashingExample;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Example {
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String input = "JavaInterviewPoint";
// MessageDigest instance for MD5
MessageDigest md = MessageDigest.getInstance("MD5");
// Update MessageDigest with input text in bytes
md.update(input.getBytes(StandardCharsets.UTF_8));
// Get the hashbytes
byte[] hashBytes = md.digest();
// Convert hash bytes to hex format
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
// Print the hashed text
System.out.println(sb.toString());
}
}
- 通过将“ MD5”作为参数传递给getInstance()方法,为MD5哈希函数创建MessageDigest实例
MessageDigest md = MessageDigest.getInstance(“ MD5”);
- 获取输入文本的字节,并将其传递到消息摘要实例的update()方法。
md.update(input.getBytes(StandardCharsets.UTF_8));
注意:使用密码术时,请始终确保指定要表示字节的编码。简单来说,如果您使用的是getBytes(),它将使用平台默认的编码。并非所有的操作系统都使用相同的默认编码。
- 的摘要()方法执行实际的散列,并返回传递给它的文本的哈希版本。
byte [] hashBytes = md.digest();
- 最后,将hashBytes转换为十六进制格式
for(字节b:hashBytes){
sb.append(String.format(“%02x”,b)); }
用Java获取文件的MD5校验和
在上面的代码中,我们已经为简单的输入字符串计算了MD5哈希值。在现实世界中,大多数MD5都将用于计算文件的校验和,例如zip,exe,iso等。
例如,如果由于网络问题或篡改而导致文件下载不正确,则您知道原始文件的校验和以及已下载文件的运行校验和。如果结果校验和匹配,则您拥有的文件是相同的(如果文件没有损坏或被篡改)。
现在,让我们看一下获取文件的MD5校验和的代码。
package MD5HashingExample;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Example_File {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
// MessageDigest instance for MD5
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] fileBytes = Files.readAllBytes(Paths.get("D:\\temp.txt"));
// Get the hashbytes
byte[] hashBytes = md.digest(fileBytes);
// Convert hash bytes to hexadecimal
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
// Print the hashed text
System.out.println(sb.toString());
}
}
- 通过将“ MD5”作为参数传递给getInstance()方法,为MD5哈希函数创建MessageDigest实例
MessageDigest md = MessageDigest.getInstance(“ MD5”);
- 使用readAllBytes()中的文件类来获取fileBytes
byte [] fileBytes = Files.readAllBytes(Paths.get(“ D:\\ temp.txt”));
- 通过fileBytes到摘要()消息摘要实例,它返回的方法MD5哈希所述的文件。
byte [] hashBytes = md.digest(fileBytes);
用盐保护MD5哈希
尽管MD5被广泛使用,但它很容易出现哈希冲突弱点。通过使用Lookup表和Rainbow表,黑客可以轻松地识别密码,为了缓解这一问题,我们可以在哈希之前添加盐。
Salt是固定长度的安全随机字符串,在散列之前将其添加到密码中,因此对于相同的密码,散列将有所不同。
现在,让我们看看如何使用Salt保护MD5哈希
package MD5HashingExample;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class MD5ExampleWithSalt {
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String input = "JavaInterviewPoint";
// MessageDigest instance for MD5
MessageDigest md = MessageDigest.getInstance("MD5");
// Generate the random salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
// Passing the salt to the digest for the computation
md.update(salt);
// Update MessageDigest with input text in bytes
md.update(input.getBytes(StandardCharsets.UTF_8));
// Get the hashbytes
byte[] hashBytes = md.digest();
// Convert hash bytes to hexadecimal
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
// Print the hashed text
System.out.println(sb.toString());
}
}
我们将为SecureRandom类创建一个新实例,nextByte()方法将生成随机盐。
SecureRandom random =新的SecureRandom();
字节[]盐=新的字节[16];
random.nextBytes(盐);
这段代码每次为相同的输入文本生成不同的哈希
运行1: 85a9df6cdbc31b7dd89a2165203e794b
运行2: f2cc604967e2206f5f1513a4e31839dc