MD5是MessageDigestAlgorithm的缩写,译为消息摘要算法,是Java语言中广泛使用的一种加密算法。MD5通过不可逆的字符串变换算法,可以通过任何字符串生成唯一的MD5信息摘要。这个信息摘要就是我们通常所说的MD5字符串。那么问题来了,MD5加密安全吗?这个问题看似简单,其实是一个命题。很多人,尤其是一些新人,会认为它是安全的。MD5首先是一个加密的字符串,然后是不可逆的,所以一定是安全的。如果你这样回答,那你就彻底掉进面试官给你挖的坑里了。为什么?因为答案是“不安全”,而不是“安全”。1、之所以说rainbowtableMD5是不安全的,是因为每一个原始密码都会生成一个对应的固定密码,也就是说一个字符串生成的MD5值是永远不会变的。在这种情况下,虽然不可逆,但可以耗尽,耗尽的“产物”称为彩虹表。什么是彩虹表?彩虹表是加密哈希函数逆运算的预计算表,为破解密码的哈希值(或哈希值、缩略图、摘要、指纹、哈希密文)而准备。一般主流的彩虹表都在100G以上。此类表通常用于恢复由有限字符集组成的固定长度明文密码。这是一种典型的空间/时间替换的做法,比每次尝试计算哈希值的暴力破解需要更少的处理时间和更多的存储空间,但它比单纯看起来的破解方法存储空间更少,存储空间更多每次输入哈希表的处理时间都很高。简单的说,彩虹表就是一个用来存储穷举对应值的大数据表。以MD5为例,“1”的MD5值是“C4CA4238A0B923820DCC509A6F75849B”,而“2”的MD5值是“C81E728D9D4C2F636F067F89CC14862C”,那么就有了一个MD5的彩虹表是这样的:原始值加密值1C4CA4238A0B923820DCC509A6F75849B2C81E728D9D4C2F636F067F89CC14862C......想一想,如果我有这个表,那么我可以直接通过MD5找到原始密码的数据库密码加密,就像使用钥匙一样锁是不安全的。2.解决方案要解决以上问题,我们需要引入“加盐”机制。Salt:在密码学中,是指在密码的任意固定位置插入特定的字符串,使哈希后的结果与使用原密码的哈希后的结果不匹配。这个过程称为“腌制”。”。通俗地说,“加盐”就像炒菜,加不同的盐,炒菜的味道就不一样。我们之前之所以用MD5不安全,是因为每个原始密码对应的MD5值是固定的,那么我们只需要让每次对密码加盐后生成的最终密码都不一样,就可以解决加密不安全的问题。3.实施代码加盐是解决密码安全问题的一种手段和思路,有实现方式很多,我们可以使用SpringSecurity提供的BCrypt等框架进行加盐验证,当然我们也可以自己实现加盐的功能,本文为了让大家更好的了解了加盐的机制,我们就自己实现加盐的功能,实现加盐机制的关键是在加密过程中产生一个随机的盐值,随机的盐值不应该be尽可能重复。这时候我们就可以使用Java语言提供的UUID(UniversallyUniqueIdentifier)。作为盐值,这样每次都会产生不同的随机盐值,并且永远不会重复。加盐的实现代码如下:importorg.springframework.util.DigestUtils;importorg.springframework.util.StringUtils;importjava.util.UUID;publicclassPasswordUtil{/***加密(加盐处理)*@parampassword待加密密码(passwordtobeencrypted)*@returnencryptedpassword*/publicstaticStringencrypt(Stringpassword){//随机盐值UUIDStringsalt=UUID.randomUUID().toString().replaceAll("-","");//password=md5(随机盐+密码)StringfinalPassword=DigestUtils.md5DigestAsHex((salt+password).getBytes());返回盐+“$”+最终密码;}}从上面的代码我们可以看出加盐的具体步骤是:使用UUID生成一个随机的盐值;MD5随机加盐值+原密码共同生成新密码(相同的原密码每次都会生成不同的新密码);将随机盐值+“$”+上一步生成的新密码相加,得到最终生成的密码。那么,问题来了,既然每次生成的密码都不一样,如何验证密码是否正确呢?验证密码是否正确的关键是先获取盐值,然后使用相同的加密方法和步骤生成最终密码,并与存储在数据库中的加密密码进行比较。具体实现代码如下:importorg.springframework.util.DigestUtils;importorg.springframework.util.StringUtils;importjava.util.UUID;publicclassPasswordUtil{/***加密(盐处理)*@parampassword待加密密码(passwordtobeencrypted)*@returnencryptionPassword*/publicstaticStringencrypt(Stringpassword){//随机盐UUIDStringsalt=UUID.randomUUID().toString().replaceAll("-","");//password=md5(随机盐+密码)StringfinalPassword=DigestUtils.md5DigestAsHex((salt+password).getBytes());返回盐+“$”+最终密码;}/***解密*@parampassword要验证的密码(未加密)*@paramsecurePassword数据库中的加盐密码*@return比较结果为真或假*/publicstaticbooleandecrypt(Stringpassword,StringsecurePassword){booleanresult=假;如果(StringUtils.hasLength(密码)&&StringUtils.hasLength(安全密码)){if(securePassword.length()==65&&securePassword.contains("$")){String[]securePasswordArr=securePassword.split("\\$");//盐值Stringslat=securePasswordArr[0];StringfinalPassword=securePasswordArr[1];//使用相同的加密算法和随机盐值生成最终的加密密码password=DigestUtils.md5DigestAsHex((slat+password).getBytes());如果(finalPassword.equals(密码)){结果=真;}}}返回结果;}}总结简单的使用MD5加密是不安全的,因为每个字符串都会生成一个固定的密文,那么我们可以使用彩虹表将密文转换还原,所以是不安全的。为了解决这个问题,我们需要每次生成不同的密码,通过加s??alt来解决这个问题。
