系列文章目录
1.SpringBoot整合RabbitMQ并实现消息发送与接收
2. 解析JSON格式参数 & 修改对象的key
3. VUE整合Echarts实现简单的数据可视化
4. List<HashMap<String,String>>实现自定义字符串排序(key排序、Value排序)
5. 使用JAVA代码实现生成二维码
更多文章可看我主页哦~
文章目录
前言
在我们开发过程中肯定会对于一些保密数据进行加密存储,加密的方式有很多,例如大家常见的MD5、SHA-256等加密方法。这边我以前使用的是MD5,主要是因为MD5容易被解密。因为MD5在值相同时,加密出的内容都是相同的。这样对于数据很容易就会被破解,怎样能做到相同值在加密后的值不相同呢?
引入我们今天的主角BCrypt算法,BCrypt算法是一种用于密码散列的加密算法,设计用于安全地处理用户密码。它结合了散列算法和盐的使用,具有较高的安全性和抗破解能力。
下面带大家了解BCrypt算法具体的使用方法以及一些原理的解析,让大家能够清晰的明白该算法的用途以及解决大家心中的疑惑~
一、加密原理及代码
1.1 原理
相信大家在看这篇文章时也是见过BCrypt算法加密后的字符串样子,但对于里面的内容以及为什么会生成这样的字符串还不太明白,先来看下图:
上图中就是BCrypt算法生成字符串的结构组成,由四个部分组成:
- 算法标识符 (2a):这个前缀表示使用了Bcrypt算法。 2 a 2a 2a是Bcrypt的一个版本标识符。
- 代价因子 (10):代价因子表示算法的复杂度,也就是加密过程中迭代的次数。10表示算法使用2的10次方,即1024轮处理。这决定了计算哈希所需的时间和资源量。
- 盐(Salt) :盐是一个16字节(128位)的随机值,用于增加哈希的唯一性和安全性。它经过Base64编码后变成22个字符的字符串。
- 哈希值 (Hash):这是经过Bcrypt算法处理后的哈希值。它是24字节(192位)的原始哈希值,经过Base64编码后变成31个字符的字符串。
1.2 代码
下面我们来使用代码生成字符串,因为相同值生成的密文是不同的,所以这里代码也做比较。是具体代码如下:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class test {
// 创建 BCryptPasswordEncoder 实例
private static final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
/**
* 加密密码
*
* @param rawPassword 明文密码
* @return 加密后的密码
*/
public static String encryptPassword(String rawPassword) {
return passwordEncoder.encode(rawPassword);
}
public static void main(String[] args) {
String password = "123456";
String encryptedPassword = encryptPassword(password);
String encryptedPassword1 = encryptPassword(password);
// 比对加密后的密码
boolean isMatch = passwordEncoder.matches(password, encryptedPassword);
boolean isMatch1 = passwordEncoder.matches(password, encryptedPassword1);
System.out.println("Encrypted Password: " + encryptedPassword);
System.out.println("Encrypted Password1: " + encryptedPassword1);
System.out.println("Passwords match: " + isMatch);
System.out.println("Passwords match1: " + isMatch1);
}
}
注:导入包security权限包,需要引入依赖。
1.3 运行效果
从效果图可以看到,同样是进行原密码:123456的加密,加密的结果是不一样的。但是在通过原密码和加密后的密码对于时,都可以返回true。表名都是123456密码加密的。如下图所示:
这里大家可能会有2个疑问:
- 为什么相同参数在加密时会生成不同的密文?
- 密文都不同了,怎么去和原密码进行的比较能得出结果呢?(即2个不同的密文,都和原文比较得出相同的结果)
二、原理
2.1 密文随机生成
-
盐的使用:BCrypt 在加密过程中会生成一个独特的盐,并将其与密码进行混合。每次加密时,盐都会不同,因此即使原始密码相同,每次加密生成的加密值也会不同。
-
加密过程:BCrypt 的加密过程包括盐的生成和散列计算。由于盐是随机的,生成的散列值会有所不同。即使输入相同,但由于每次使用不同的盐,输出也会不同。
上述加密字符串的结构是由四部分组成的,其中包含盐。这里BCrypt自动生成一个随机盐值,盐的目的是防止相同密码生成相同的散列值。也正因为如此,hash值也会发现不一样。所以我们看到的加密字符串的内容是不一样的。
2.2 passwordEncoder.matches 方法比较结果为什么都为true?
注:passwordEncoder.matches 方法在比较密码时并不是直接比较加密后的值,而是执行以下步骤:
- 提取盐:从加密后的值中提取盐。
- 重新加密:使用提取出的盐和提供的密码重新计算加密值。
- 比较:将重新计算的加密值与存储的加密值进行比较。
因为提取的是加密字符串中的盐,所以在传入的原密码使用提取的盐进行加密,得到的密文肯定是和一致的,相同的盐散列值(hash)一样。所以会返回true,
大家在这里应该会比较清楚的知道输出效果图中的含义了。但是我来问大家一个问题:它是怎么提取加密后值中的盐呢?
2.3 如何提取加密后值中的盐
这个其实第一张图认识字符串的组成结构时介绍了生成的规则。那么生成的规则也将是用来提取盐的重要部分:
- 首先底层会将加密的值进行拆分成四部分。
- 获取到盐的22个字符的字符串
- 因为这个字符串是BASE64加密后的,我们解码后也可以拿到一个16字节(128位)的随机值。
2.4 提取盐之后,如何进行的比较
- 使用提取出的盐和原密码生成新的哈希值。此时原密码是必须的,因为新的哈希值是基于原密码和盐计算出来的。
- 将生成的哈希值与存储的哈希值进行比较。这一步确保了输入密码的正确性。
注:这里是重点,matches 方法比较原密码和加密是否一直时,是通过哈希值(也就是散列值)进行的比较。因为算法标识符 (2a)、代价因子(10)是固定的,盐的话也是通过密文去生成的,所以肯定一致。可能出现不一样的地方只有哈希值(因为哈希值的生成是通过提取出的盐和原密码生成的)
三、如何运用
大家到这里应该对BCrypt算法有了一定的理解,但光了解原理是不够的。先说一下场景:我们以常见的密码存储为例:
数据库中的密码存储是相关重要的,都会使用加密算法实现。所以这里我们也可以运用BCrypt算法。
- 首先用户表密码字段需存入BCrypt算法加密的密文(这里在用户注册时,可通过下述代码生成密文)。
public static String encryptPassword(String rawPassword) {
return passwordEncoder.encode(rawPassword);
}
- 在用户登录时,通过用户名查询表,获取对应的实体。然后拿到实体中的密码作为matches(password, encryptedPassword);方法中的encryptedPassword参数,password就是用户输入的参数值。
- 调用matches方法查看返回值,如果返回true,则用户名和密码正确。否则返回“用户名或密码错误”。
这里简写一个实现代码,来体现步骤二、三。具体如下所示:
@PostMapping("/login")
public R<User> login( @RequestBody User user){
//1.将页面提交的密码
String password=user.getPassword();
//2.根据页面提交的用户名username查询数据库
LambdaQueryWrapper<User> queryWrapper=new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername,user.getUsername());
User selectuser = userService.getOne(queryWrapper);
// 比对加密后的密码
boolean isMatch = passwordEncoder.matches(password, selectuser.getPassword());
if(!isMatch){
return R.error("用户名或密码不正确");
}
//封装token
Map<String,Object> map=new HashMap<>();
// map.put("token",token);
return R.success(newUser,map);
}
当然,这只是一个常规的场景需要用到对数据的加密。正常情况下对于表中的关键数据大多数都应该进行加密处理,例如用户的身份证号等重要信息值。这样即使是数据库数据泄露,也不会造成太大的影响,加密的数据很安全。
四、为什么要用BCrypt算法进行加密
Bcrypt算法是一个密码哈希算法,它的好处有很多,专门设计用于保护密码存储,其主要优点包括:
- 抗暴力破解:Bcrypt使用自适应的计算复杂度(代价因子),使得随着计算能力的提升,算法仍然能够保持其破解难度。这种自适应性增加了暴力破解的难度。
- 内置盐:Bcrypt自动生成盐,并将其与哈希值一起存储。盐的使用防止了相同密码的哈希值重复,增强了安全性。
- 抗彩虹表攻击:由于每个密码都包含独特的盐,即使两个用户使用相同的密码,哈希值也会不同。这使得预计算的彩虹表攻击变得不切实际。
- 自适应性:代价因子(工作因子)可以调整,允许增加计算复杂度以抵御不断提高的计算能力。这使得Bcrypt能够应对未来的硬件进步。
- 稳定性和可靠性:Bcrypt算法经过广泛使用和测试,被认为是一个成熟、可靠的密码哈希算法。
- 防止硬件加速攻击:Bcrypt的设计旨在避免硬件加速攻击(例如GPU、FPGA等),因为它需要大量的计算资源,而不是简单的并行处理。
总结
相信大家通读完全文后也对于BCrypt算法有了一定的理解。希望大家在日后的学习或者工作中能够运用到,对于数据的安全性是有很大帮助的。
我在自己学习时也是遇到了很多的问题,本想着加密算法,没打算写文章的。但是在网上搜索时发现了很多文章介绍的不够全面,有些细节点在读完之后还是很疑惑。导致我在学习的过程中耗费了不少的时间成本。为了让大家的学习成本降低,所以写了这篇文章。希望大家能够通过这篇文章去理解和会去使用BCrypt加密算法~
如果有什么问题都可以留言或者私信我哦~ 也希望大家有更多见解的朋友能够下方留言,让更多的人对BCrypt算法的知识点体会更加深刻~