关于面试:面试突击91MD5-加密安全吗

2次阅读

共计 3194 个字符,预计需要花费 8 分钟才能阅读完成。

MD5 是 Message Digest Algorithm 的缩写,译为信息摘要算法,它是 Java 语言中应用很宽泛的一种加密算法。MD5 能够将任意字符串,通过不可逆的字符串变换算法,生成一个惟一的 MD5 信息摘要,这个信息摘要也就是咱们通常所说的 MD5 字符串。那么问题来了,MD5 加密平安吗?

这道题看似简略,其实是一道送命题,很多人尤其是一些新入门的同学会感觉,平安啊,MD5 首先是加密的字符串,其次是不可逆的,所以它肯定是平安的。如果你这样答复,那么就彻底掉进面试官给你挖好的坑了。

为什么呢?因为 答案是“不平安”,而不是“平安”

1. 彩虹表

MD5 之所以说它是不平安的,是因为每一个原始明码都会生成一个对应的固定明码,也就是说一个字符串生成的 MD5 值是永远不变的。这样的话,尽管它是不可逆的,但能够被穷举,而穷举的“产品”就叫做彩虹表。

什么是彩虹表?

彩虹表是一个用于加密散列函数逆运算的事后计算好的表, 为破解明码的散列值(或称哈希值、微缩图、摘要、指纹、哈希密文)而筹备。 个别支流的彩虹表都在 100G 以上。这样的表经常用于复原由无限集字符组成的固定长度的纯文本明码。这是空间 / 工夫替换的典型实际,比每一次尝试都计算哈希的暴力破解解决工夫少而贮存空间多,但却比简略的对每条输出散列翻查表的破解形式贮存空间少而解决工夫多。

简略来说,彩虹表就是一个很大的,用于寄存穷举对应值的数据表。 以 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;
    }
}

从上述代码咱们能够看出,加盐的实现具体步骤是:

  1. 应用 UUID 产生一个随机盐值;
  2. 将随机盐值 + 原始明码一起 MD5,产生一个新密码(雷同的原始明码,每次都会生成一个不同的新密码);
  3. 将随机盐值 + “$”+ 上一步生成的新密码加在一起,就是最终生成的明码。

那么,问题来了,既然每次生成的明码都不同,那么怎么验证明码是否正确呢?
要验证明码是否正确的要害是须要 先获取盐值,而后再应用雷同的加密形式和步骤,生成一个最终明码和和数据库中保留的加密明码进行比照,具体实现代码如下:

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 加密是不平安的,因为每个字符串都会生成固定的密文,那么咱们就能够应用彩虹表将密文还原进去,所以它不是平安的。想要解决这个问题,咱们须要通过加盐的伎俩,每次生成一个不同的明码,就把这个问题解决了。

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java 面试真题解析

面试合集:https://gitee.com/mydb/interview

正文完
 0