1.MD5码的概述
MD5(Message Digest Algorithm 5)是一种广泛使用的哈希函数算法,用于将任意长度的数据转换成固定长度的散列值。它由Ronald Rivest于1991年设计,并被广泛应用于密码学和数据完整性校验等领域。MD5算法的输入可以是任意长度的数据,输出是一个128位(16字节)的散列值。该散列值通常表示为一个32位十六进制数字,例如:5d41402abc4b2a76b9719d911017c592。
MD5算法的核心原理是将输入数据分成一系列固定大小的数据块,并对每个数据块进行一系列复杂的运算操作,最终得到一个散列值。这个散列值具有以下特点:
固定长度:无论输入数据的大小,MD5算法的输出始终是128位(16字节)长。
唯一性:对于不同的输入数据,其散列值应当是唯一的,即不同的输入应当生成不同的散列值。
不可逆性:无法通过散列值逆推出原始输入数据。也就是说,即使散列值相同,无法确定其对应的具体输入数据。
高效性:MD5算法的计算速度相对较快,适用于快速生成散列值。
由于其设计上的弱点,MD5算法容易受到碰撞攻击(collision attack)或可使用彩虹表进行破解。碰撞攻击是指找到两个不同的输入数据,但它们却产生相同的MD5散列值的情况。
彩虹表(Rainbow Table)是一种用于加速密码破解的预计算技术。通过预先计算一系列可能的输入和其对应的散列值,然后通过比对查找输入值。
2.传统用户注册登录
通过用户在前端输入用户名和密码,后端得到用户名和密码后将其密码进行MD5编码后,存入sql表中;
待用户下次登陆时,同样输入用户名和密码,发送给后端,用户名找到sql中编码后的密码,因为MD5码具有不可逆性,故而需将输入的密码进行在次MD5加密,然后与sql中的密码进行比对,相同则返回登录成功。
3.密码加盐
密码加盐是一种增加密码安全性的技术。在密码加盐过程中,一个随机的字符串(称为盐)会与密码进行组合,然后对组合后的字符串进行哈希运算,最终存储在系统中。
盐的作用是为了使相同密码在哈希后产生不同的结果。这是因为即使两个用户使用了相同的密码,但由于他们的盐不同,最终生成的哈希值也会不同。这种方法的目的是增加密码破解的难度,即使攻击者获得了哈希值,他们也无法轻易地通过查找常见的明文密码来猜测原始密码。
密码加盐还能够防止使用彩虹表(rainbow table)进行暴力破解。彩虹表是一种预先计算并存储了密码哈希值和对应明文密码的表格,可以用于快速查找哈希值对应的明文密码。然而,由于每个用户都有唯一的盐值,攻击者无法事先生成包含所有可能盐值的彩虹表,从而增加了破解的难度。
总结来说,密码加盐是一种增加密码安全性的方法,通过随机盐值的引入,使相同密码在哈希后产生不同的结果,增加了密码破解的难度,并有效防止了彩虹表攻击。
4.代码举例
package com.example.demo.common;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.util.UUID;
/**
* 密码工具类
* 加盐加密 / 加盐解密
*/
public class PasswordTools {
/**
* 加盐加密
*
* @param password 明文密码
* @return 加盐加密的密码
*/
public static String encrypt(String password) {
// 1.产生盐值
String salt = UUID.randomUUID().toString().replace("-", "");
// 2.使用(盐值+明文密码)得到加密的密码
String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 3.将盐值和加密的密码共同返回(合并盐值和加密密码)
String dbPassword = salt + "$" + finalPassword;
return dbPassword;
}
/**
* 加盐加密
*
* @param password 明文密码
* @param salt 盐值
* @return 加盐加密的密码
*/
public static String encrypt(String password, String salt) {
// 1.使用(盐值+明文密码)得到加密的密码
String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 2.将盐值和加密的密码共同返回(合并盐值和加密密码)
String dbPassword = salt + "$" + finalPassword;
return dbPassword;
}
/**
* 验证加盐加密密码
*
* @param password 明文密码(不一定对,需要验证明文密码)
* @param dbPassword 数据库存储的密码(包含:salt+$+加盐加密密码)
* @return true=密码正确
*/
public static boolean decrypt(String password, String dbPassword) {
boolean result = false;
if (StringUtils.hasLength(password) && StringUtils.hasLength(dbPassword) &&
dbPassword.length() == 65 && dbPassword.contains("$")) { // 参数正确
// 1.得到盐值
String[] passwrodArr = dbPassword.split("\\$");
// 1.1 盐值
String salt = passwrodArr[0];
// 2.生成验证密码的加盐加密密码
String checkPassword = encrypt(password, salt);
if (dbPassword.equals(checkPassword)) {
result = true;
}
}
return result;
}
}