什么场景需要使用数据加密呢?比如秘密数据传输、用户密码加密存储等等
数据传输可使用密钥对的方式进行加密解密,使用签名方式验证数据是否可靠,而密码加密存储可使用MD5等一些算法对数据进行单向加密
一、MD5单向加密
1、百度说法:
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
2、理解
md5可以把明文加密成密文,但是不可以把密文解密成原来的明文
那我们使用的逻辑是
密码加密后把密文存在数据库中,要判断输入的密码是否正确时,只需要对输入密码经过相同的md5加密,验证两次加密结果是否相同就可以了
md5能做到的就是,两个不同的明文加密得到相同的密文的几率是极低的,所以可以用以上逻辑判断
3、破解
md5不能解密,那么如何破解呢?其实md5的弊端非常明显,那就是同一个明文每次加密得到的密文都是一样的,例如对123456加密的到的结果是e10adc3949ba59abbe56e057f20f883e,那么我们只要跑一次加密就可以知道密文对应的原文是什么了
如果密码单纯是10位以内的数字,那么跑10的10次方次MD5,就可以破解所有密码
考虑到查询的性能问题,可以把结果进行排序,通过二分查找就可以log(n)的复杂度查出结果
我们可以看看目前对应的破解平台
e10adc3949ba59abbe56e057f20f883e是123456加密出来的结果
破解:
4、解决办法
(1)一次md5加密容易被破解,那么我们进行多次md5加密呢?这种办法也是不太可行的,如果加密次数也知道了,那么也是容易被破解的
(2)加盐,可看以下详细解释
二、MD5+固定盐值
1、原理
对原来的明文拼接一个字符串,再进行加密,如果这个字符串不泄露,那么就密文就是安全的,因为即使你通过暴力打表的手段得到了原来的明文1afvr2ara34avf56bsrcafavtrt,你也不能知道原来的密码是123,还是246
那么我们使用的方法就是,将用户输入的密码按相同的方法加盐值,加密,对比存储的密码是否相同即可
3、不足
md5加固定盐值是多次加密的原理是一样的,只要固定盐值也泄露了,那么也就意味着被破解了
三、MD5+随机盐值
1、原理
固定盐值加密泄露了盐值,就可以被暴力计算出来,而且可以很快就破解全部的密码
那么可以考虑,如果每个用户加密使用的盐值都是不一样的,那么黑客就无法一下子破解全部的密码,每破解一个用户的密码,都需要按盐值暴力跑加密,直到密码被找到,这大大提高的破解的时间
这样,某个用户的盐值泄露了,不会影响其它用户,即使所有用户的盐值都泄露了,黑客也需要非常长的时间才可以全部破解
四、BCrypt
1、原理
spring security 封装了BCryptPasswordEncoder
BCryptPasswordEncoder的实现思路和md5+随机盐值的思路差不多
也是使用随机盐值加密,使得同一个明文每次的加密的结果都不同,不一样的是,BCryptPasswordEncoder会对明文进行多次加密,多次转换操作,提高破解难度
值得一提的一个点是,适当地提高单次完整加密的时间也可以提高破解难度,提高系统安全性,如果一次单次完整加密需要从0.1秒提升到1秒,那么破解的时间会从1年提高到10年
五、加密体验
添加security依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
使用MD5加密:
package com.wu.encryption.main;
import org.springframework.util.DigestUtils;
import java.util.UUID;
public class Md5 {
public static void main(String[] args) {
/**
* MD5加密
* 1、生成密文,存数据库
* 2、判断的时候,对密码加密,取出数据库密文校验
* 3、若一致则正确,否则不正确
*
*/
String password1 = "123456";
String encode1 = DigestUtils.md5DigestAsHex(password1.getBytes());
System.out.println("MD5加密:"+encode1);
System.out.println("-----------------------");
/**
* MD5+固定盐
* 1、密码拼接盐值
* 2、生成密文,存数据库
* 3、判断的时候,密码拼接盐值,生成密文,取出数据库密文校验
* 4、若一致则正确,否则不正确
*/
String salt2 = "hello";
String password2 = "123456";
String encode2 = DigestUtils.md5DigestAsHex((password2+salt2).getBytes());
System.out.println("MD5固定盐加密:"+encode2);
System.out.println("-----------------------");
/**
* MD5+随机盐
* 1、生成随机盐,UUID可以保证全局唯一,即可实现每个用户的盐都不一样
* 2、密码拼接盐值
* 3、生成密文,把密文和盐都存数据库
* 4、判断的时候,取出数据库盐值,密码拼接盐值,生成密文,取出数据库密文校验
* 5、若一致则正确,否则不正确
*/
String salt3 = UUID.randomUUID().toString();
String password3 = "123456";
String encode3 = DigestUtils.md5DigestAsHex((password3+salt3).getBytes());
System.out.println("MD5随机盐加密:"+encode3);
}
}
经过加盐处理后,就不能被简单解密了
使用spring security提供的BCryptPasswordEncoder:
package com.wu.encryption.main;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.*;
public class BCrypt {
public static void main(String[] args) {
/**
* 1、BCryptPasswordEncoder机制和md5+随机盐类似,BCryptPasswordEncoder采用的是安全hash,SHA256
* 2、BCryptPasswordEncoder同一个明文每次加密都会产生不同的结果
* 3、BCryptPasswordEncoder只需要简单地使用encode加密和matches判断匹配就可以了,盐值就存在密文里,不需要程序员关心
*
*/
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
int n=10;
long time1 = new Date().getTime();
for (int i = 0; i < n; i++) {
String encode = encoder.encode("123456");
System.out.println(encode);
System.out.println(encoder.matches("123456",encode));
}
long time2 = new Date().getTime();
System.out.println("加密"+n+"条数据,耗时"+(Double.valueOf(time2)-time1)/1000+"秒");
}
}
可以看到,每次加密的结果都不一样,但是都可以匹配成功
BCrypt是相对安全的,目前使用也比较广泛