BCrypt加密算法的使用及原理

2026-07-17wei👁 19 阅读11 分钟阅读📝 4953 字💬 0 评论
BCrypt加密算法的使用及原理

BCrypt加密算法的使用及原理

前言

在我们开发过程中肯定会对于一些保密数据进行加密存储,加密的方式有很多,例如大家常见的MD5、SHA-256等加密方法。这边我以前使用的是MD5,主要是因为MD5容易被解密。因为MD5在值相同时,加密出的内容都是相同的。这样对于数据很容易就会被破解,怎样能做到相同值在加密后的值不相同呢? 引入我们今天的主角BCrypt算法,BCrypt算法是一种用于密码散列的加密算法,设计用于安全地处理用户密码。它结合了散列算法和盐的使用,具有较高的安全性和抗破解能力。 下面带大家了解BCrypt算法具体的使用方法以及一些原理的解析,让大家能够清晰的明白该算法的用途以及解决大家心中的疑惑~

⚠️⚠️⚠️声明:若此文章不完善请看原文章,此文章摘抄于CSDN

一、加密原理及代码

1.1 原理

    相信大家在看这篇文章时也是见过BCrypt算法加密后的字符串样子,但对于里面的内容以及为什么会生成这样的字符串还不太明白,先来看下图:

上图中就是BCrypt算法生成字符串的结构组成,由四个部分组成:

算法标识符 (2a):这个前缀表示使用了Bcrypt算法。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密码加密的。如下图所示:

1.这里大家可能会有2个疑问:
为什么相同参数在加密时会生成不同的密文?
2.密文都不同了,怎么去和原密码进行的比较能得出结果呢?(即2个不同的密文,都和原文比较得出相同的结果)

二、原理

2.1 密文随机生成

盐的使用:BCrypt 在加密过程中会生成一个独特的盐,并将其与密码进行混合。每次加密时,盐都会不同,因此即使原始密码相同,每次加密生成的加密值也会不同。
加密过程:BCrypt 的加密过程包括盐的生成和散列计算。由于盐是随机的,生成的散列值会有所不同。即使输入相同,但由于每次使用不同的盐,输出也会不同。

上述加密字符串的结构是由四部分组成的,其中包含盐。这里BCrypt自动生成一个随机盐值,盐的目的是防止相同密码生成相同的散列值。也正因为如此,hash值也会发现不一样。所以我们看到的加密字符串的内容是不一样的。

2.2 passwordEncoder.matches 方法比较结果为什么都为true?

注:passwordEncoder.matches 方法在比较密码时并不是直接比较加密后的值,而是执行以下步骤:

提取盐:从加密后的值中提取盐。

重新加密:使用提取出的盐和提供的密码重新计算加密值。

比较:将重新计算的加密值与存储的加密值进行比较。

因为提取的是加密字符串中的盐,所以在传入的原密码使用提取的盐进行加密,得到的密文肯定是和一致的,相同的盐散列值(hash)一样。所以会返回true,
大家在这里应该会比较清楚的知道输出效果图中的含义了。但是我来问大家一个问题:它是怎么提取加密后值中的盐呢?

2.3 如何提取加密后值中的盐

这个其实第一张图认识字符串的组成结构时介绍了生成的规则。那么生成的规则也将是用来提取盐的重要部分:
1.首先底层会将加密的值进行拆分成四部分。
2.获取到盐的22个字符的字符串
3.因为这个字符串是BASE64加密后的,我们解码后也可以拿到一个16字节(128位)的随机值。

2.4 提取盐之后,如何进行的比较

1.使用提取出的盐原密码生成新的哈希值。此时原密码是必须的,因为新的哈希值是基于原密码和盐计算出来的。
2.将生成的哈希值与存储的哈希值进行比较。这一步确保了输入密码的正确性。

注:这里是重点,matches 方法比较原密码和加密是否一直时,是通过哈希值(也就是散列值)进行的比较。因为算法标识符 (2a)、代价因子(10)是固定的,盐的话也是通过密文去生成的,所以肯定一致。可能出现不一样的地方只有哈希值(因为哈希值的生成是通过提取出的盐和原密码生成的)

三、如何运用

大家到这里应该对BCrypt算法有了一定的理解,但光了解原理是不够的。先说一下场景:我们以常见的密码存储为例: 数据库中的密码存储是相关重要的,都会使用加密算法实现。所以这里我们也可以运用BCrypt算法。
1.首先用户表密码字段需存入BCrypt算法加密的密文(这里在用户注册时,可通过下述代码生成密文)。

public static String encryptPassword(String rawPassword) {        return passwordEncoder.encode(rawPassword);    } 

2.在用户登录时,通过用户名查询表,获取对应的实体。然后拿到实体中的密码作为matches(password, encryptedPassword);方法中的encryptedPassword参数,password就是用户输入的参数值。
3.调用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算法是一个密码哈希算法,它的好处有很多,专门设计用于保护密码存储,其主要优点包括:

1.抗暴力破解:Bcrypt使用自适应的计算复杂度(代价因子),使得随着计算能力的提升,算法仍然能够保持其破解难度。这种自适应性增加了暴力破解的难度。
2.内置盐:Bcrypt自动生成盐,并将其与哈希值一起存储。盐的使用防止了相同密码的哈希值重复,增强了安全性。
3.抗彩虹表攻击:由于每个密码都包含独特的盐,即使两个用户使用相同的密码,哈希值也会不同。这使得预计算的彩虹表攻击变得不切实际。
4.自适应性:代价因子(工作因子)可以调整,允许增加计算复杂度以抵御不断提高的计算能力。这使得Bcrypt能够应对未来的硬件进步。
5.稳定性和可靠性:Bcrypt算法经过广泛使用和测试,被认为是一个成熟、可靠的密码哈希算法。
6.防止硬件加速攻击:Bcrypt的设计旨在避免硬件加速攻击(例如GPU、FPGA等),因为它需要大量的计算资源,而不是简单的并行处理。

总结

相信大家通读完全文后也对于BCrypt算法有了一定的理解。希望大家在日后的学习或者工作中能够运用到,对于数据的安全性是有很大帮助的。
我在自己学习时也是遇到了很多的问题,本想着加密算法,没打算写文章的。但是在网上搜索时发现了很多文章介绍的不够全面,有些细节点在读完之后还是很疑惑。导致我在学习的过程中耗费了不少的时间成本。为了让大家的学习成本降低,所以写了这篇文章。希望大家能够通过这篇文章去理解和会去使用BCrypt加密算法~
如果有什么问题都可以留言或者私信我哦~ 也希望大家有更多见解的朋友能够下方留言,让更多的人对BCrypt算法的知识点体会更加深刻~

wei
技术博客作者
4953 字 · 0 评论
2026-07-17

评论 (0)

暂无评论,来写第一条吧

登录后发表评论

分类

友链

关于本站

用 SpringBoot + Nuxt3 搭建的个人技术博客,记录编程之路的思考与收获。

© 2026 好啵博客 · Built with ❤️ & SpringBoot + Nuxt3