从此
文章
📄文章 #️⃣专题 🌐上网 📺 🛒 📱

Spring Security BCryptPasswordEncoder

🕗2024-03-23

一、使用BCryptPasswordEncoder加密的值可以解出来吗

  Spring Security 中的 BCryptPasswordEncoder 是一种单向加密算法,它是为了安全性考虑而设计的,因此无法从加密后的密码值"解密"出原始密码。这是出于安全目的的设计。

  BCryptPasswordEncoder 加密过程是不可逆的,即使你有加密后的密码值,也无法通过任何方式获得原始密码。这种单向加密的目的就是为了防止密码泄露后被破解。

  通常,在实际应用中,我们只需要将用户输入的原始密码使用 BCryptPasswordEncoder 进行加密,然后将加密后的密码值存储在数据库中。在用户登录时,将用户输入的密码加密后,与存储在数据库中的加密密码进行比对,如果相同则验证通过,而无需知道原始密码。

  因此,你无法从 BCryptPasswordEncoder 加密后的密码值中"解密"出原始密码。这正是它被设计用于密码加密的目的,以提高系统的安全性。如果你需要知道用户的原始密码,建议在注册时不对密码进行加密,或者提供"忘记密码"的功能让用户重置密码。

二、为什么每次BCryptPasswordEncoder加密的值不一样,却可以都匹配上原始密码

  这是因为 BCryptPasswordEncoder 在加密时会自动添加一个随机的 Salt 值,这个 Salt 值的作用是使得相同的原始密码在每次加密时产生不同的密文,从而增加密码的安全性。

  具体来说,BCryptPasswordEncoder 加密过程包括以下几个步骤:

    1. 生成一个随机的 Salt 值,通常是 16 字节长度。
    2. 将原始密码和 Salt 值进行合并。
    3. 对合并后的值进行 BCrypt 哈希运算,计算出最终的密文。
    4. 将 Salt 值和密文拼接在一起,作为最终的加密结果。

  因此,每次加密相同的原始密码时,由于使用了不同的随机 Salt 值,所以最终的加密结果也是不同的。

  在验证密码时,BCryptPasswordEncoder 会自动从加密结果中提取出 Salt 值,并使用相同的方式对输入的原始密码进行加密,然后比较两个密文是否相同。由于使用了相同的 Salt 值,所以即使每次加密后的结果不同,只要原始密码相同,通过 BCrypt 算法计算出的密文就是相同的。

  这种机制不仅增加了密码的安全性,而且还能防止采用相同密码的不同用户的密文相同,从而增加了"彩虹表"攻击的难度。所以即使每次加密结果不同,BCryptPasswordEncoder 依然能够正确比对密码。

三、BCryptPasswordEncoder 加密结果的格式

  在 BCryptPasswordEncoder 加密后的结果中,前 29 个字符是包含了 Salt 值和其他算法信息的部分,后面的字符串才是实际的密文部分。

  具体来说,BCryptPasswordEncoder 加密结果的格式是:

$算法标识$rounds$salt$密文

 

  其中:

  • `$算法标识` 是一个固定的字符串 `$2a$`、`$2b$` 或 `$2y$`等,用于标识使用的哈希算法版本。
  • `$rounds` 是一个十进制数字,表示算法迭代的次数。
  • `$salt` 是一个 22 个字符长的 Base64 编码的字符串,这就是加密时使用的随机 Salt 值。
  • `$密文` 是实际的密文部分,长度为 31 个字符。

  例如,一个典型的 BCryptPasswordEncoder 加密结果可能是:

    

$2a$10$N9qo8uHh24cuh6zT6ZsEfe6Fzen7kNSNBqh.FkQPvQUNhxfrasXTi

  在这个加密结果中:

  •  `$2a$` 表示使用 BCrypt 算法的版本 2a
  •  `10` 表示迭代次数为 10
  •  `N9qo8uHh24cuh6zT6ZsEfe` 就是 22 个字符长的随机 Salt 值
  •  最后 31 个字符 `6Fzen7kNSNBqh.FkQPvQUNhxfrasXTi` 才是实际的密文部分

  所以,BCryptPasswordEncoder 加密结果中,第 4 到第 25 个字符就是随机生成的 Salt 值。每次加密相同的密码,这部分 Salt 值都会不同,从而导致最终的加密结果也不相同。但在验证时,会自动提取出这个 Salt 值用于计算,所以能够正确比对密码。

四、Salt 值和真实密码合并后的值进行 BCrypt 哈希运算,如果知道salt值和最终的密文可以解出原来的密码吗

  即使知道了Salt值和最终的密文,也无法通过任何方式反解出原始的密码。

  这是因为BCrypt是一种经过加盐(Salting)的单向自适应密钥导出函数(Key Derivation Function),它的设计目的就是使得从密文反推原始密码是计算上不可行的。

  具体来说,BCrypt算法包含以下几个关键设计:

    1. 使用了Eksblowfish算法作为核心,这是一种密钥设置可变的块加密算法,具有良好的密码学性质。
    2. 在密钥设置中引入了可变的迭代次数,默认为2^10次,这使得暴力破解的计算代价大大增加。
    3. 在密钥设置中引入了随机的Salt值,使得相同的密码每次产生的密文都不同,防止彩虹表攻击。
    4. 密钥设置过程是自适应和可变的,可以根据硬件性能动态调整计算代价。
    5. 最终密文使用Base64编码,长度为53个字符,其中包含了Salt和其他算法参数。

  因此,即使已知Salt值和密文,要反解出原始密码,仍然需要进行大量的计算和暴力破解。由于BCrypt算法设计的目的就是使这种破解行为在计算上不可行,所以实际上是无法通过任何方式从Salt和密文中反解出原始密码的。

  这就是BCrypt作为密码哈希算法的设计理念和安全保证。除非原始密码非常简单且Salt和迭代次数设置较低,否则从BCrypt密文中反解是极其困难的。