Md5加密是否足够安全
Message digest algorithm 的缩写为 MD5,译为信息摘要算法,它是 Java 语言中使用很广泛的一种加密算法。MD5 可以将任意字符串,通过不可逆的字符串变换算法,生成一个唯一的 MD5 信息摘要,这个信息摘要也就是我们通常所说的 MD5 字符串。那么问题来了,MD5 加密安全吗? 为什么呢?因为答案是“不安全”,而不是“安全”。 1.彩虹表 MD5 之所以说它是不安全的,是因为每一个原始密码都会生成一个对应的固定密码,也就是说一个字符串生成的 MD5 值是永远不变的。这样的话,虽然它是不可逆的,但可以被穷举,而穷举的“产品”就叫做彩虹表。 简单来说,彩虹表就是一个很大的,用于存放穷举对应值的数据表。以 MD5 为例,“1”的 MD5 值是“C4CA4238A0B923820DCC509A6F75849B”,而“2”的 MD5 值是“C81E728D9D4C2F636F067F89CC14862C”,那么就会有一个 MD5 的彩虹表是这样的: 原始值 加密值 1 C4CA4238A0B923820DCC509A6F75849B 2 C81E728D9D4C2F636F067F89CC14862C ... ... 大家想想,如果有了这张表之后,那么我就可以通过 MD5 的密文直接查到原始密码了,所以说数据库如果只使用 MD5 加密,这就好比用了一把插了钥匙的锁一样不安全。 2.解决方案 想要解决以上问题,我们需要引入“加盐”机制。 盐(Salt):在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。 说的通俗一点“加盐”就像炒菜一样,放不同的盐,炒出菜的味道就是不同的,咱们之前使用 MD5 不安全的原因是,每个原始密码所对应的 MD5 值都是固定的,那我们只需要让密码每次通过加盐之后,生成的最终密码都不同,这样就能解决加密不安全的问题了。 3.实现代码 加盐是一种手段、是一种解决密码安全问题的思路,而它的实现手段有很多种,我们可以使用框架如 Spring Security 提供的BCrypt 进行加盐和验证,当然,我们也可以自己实现加盐的功能。 本文为了让大家更好的理解加盐的机制,所以我们自己来动手来实现一下加盐的功能。 实现加盐机制的关键是在加密的过程中,生成一个随机的盐值,而且随机盐值尽量不要重复,这时,我们就可以使用 Java 语言提供的 UUID(Universally Unique Identifier,通用唯一识别码)来作为盐值,这样每次都会生成一个不同的随机盐值,且永不重复。 加盐的实现代码如下: import . org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; import java.util.UUID; public class PasswordUtil { /** * 加密(加盐处理) * @param password 待加密密码(需要加密的密码) * @return 加密后的密码 */ public static String encrypt(String password) { // 随机颜值 UUID String salt = UUID.randomUUID().toString().replaceAll("-", ""); // 密码=md5(随机盐值+密码) String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes()); return salt + "$" + finalPassword; } } 从上述代码我们可以看出,加盐的实现具体步骤是: 使用 UUID 产生一个随机盐值; 将随机盐值 + 原始密码一起 MD5,产生一个新密码(相同的原始密码,每次都会生成一个不同的新密码); 将随机盐值 + "$"+上一步生成的新密码加在一起,就是最终生成的密码。 那么,问题来了,既然每次生成的密码都不同,那么怎么进一步的验证账户的密码设置是否使用的正确呢? 要验证密码是否正确的关键是需要先获取盐值,然后再使用相同的加密方式和步骤,生成一个最终密码和和数据库中保存的加密密码进行对比,具体实现代码如下: import org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; import java.util.UUID; public class PasswordUtil { /** * 加密(加盐处理) * @param password 待加密密码(需要加密的密码) * @return 加密后的密码 */ public static String encrypt(String password) { // 随机盐值 UUID String salt = UUID.randomUUID().toString().replaceAll("-", ""); // 密码=md5(随机盐值+密码) String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes()); return salt + "$" + finalPassword; } /** * 解密 * @param password 要验证的密码(未加密) * @param securePassword 数据库中的加了盐值的密码 * @return 对比结果 true OR false */ public static boolean decrypt(String password, String securePassword) { boolean result = false; if (StringUtils.hasLength(password) && StringUtils.hasLength(securePassword)) { if (securePassword.length() == 65 && securePassword.contains("$")) { String[] securePasswordArr = securePassword.split("\\$"); // 盐值 String slat = securePasswordArr[0]; String finalPassword = securePasswordArr[1]; // 使用同样的加密算法和随机盐值生成最终加密的密码 password = DigestUtils.md5DigestAsHex((slat + password).getBytes()); if (finalPassword.equals(password)) { result = true; } } } return result; } } 只是简单的使用 MD5 加密是不安全的,因为每个字符串都会生成固定的密文,那么我们就可以使用彩虹表将密文还原出来,所以它不是安全的。想要解决这个问题,我们需要通过加盐的手段,每次生成一个不同的密码,就把这个问题解决了。在这个过程中,我们可以看到,密码的长度是固定的,只要我们不断重复加盐,就可以一直保持密码的长度不变。 (编辑:银川站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |