为什么 Spring 的 BCryptPasswordEncoder 对于相同的输入生成不同的输出?

问题描述 投票:0回答:5

我正在使用 BCryptPasswordEncoder 和 Spring 安全性。我的期望是,对于相同的输入,我总是会得到相同的输出。但对于相同的输入,我会得到不同的输出。您可以使用下面的代码片段进行测试:

String password = "123456"; 
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 
String encodedPassword = passwordEncoder.encode(password);
System.out.print(encodedPassword);

输出:$2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi

输出2:$2a$10$KEvYX9yjj0f1X3Wl8S.KPuWzSWGyGM9ubI71NOm3ZNbJcwWN6agvW

输出3:$2a$10$nCmrPtUaOLn5EI73VZ4Ouu1TmkSWDUxxD4N6A.8hPBWg43Vl.RLDC

有人可以解释一下,为什么 BCryptPasswordEncoder 会这样吗?

java spring spring-security bcrypt
5个回答
52
投票
public static void main(String[] args) {
  // spring 4.0.0
  org.springframework.security.crypto.password.PasswordEncoder encoder
   = new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();

   // $2a$10$lB6/PKg2/JC4XgdMDXyjs.dLC9jFNAuuNbFkL9udcXe/EBjxSyqxW
   // true
   // $2a$10$KbQiHKTa1WIsQFTQWQKCiujoTJJB7MCMSaSgG/imVkKRicMPwgN5i
   // true
   // $2a$10$5WfW4uxVb4SIdzcTJI9U7eU4ZwaocrvP.2CKkWJkBDKz1dmCh50J2
   // true
   // $2a$10$0wR/6uaPxU7kGyUIsx/JS.krbAA9429fwsuCyTlEFJG54HgdR10nK
   // true
   // $2a$10$gfmnyiTlf8MDmwG7oqKJG.W8rrag8jt6dNW.31ukgr0.quwGujUuO
   // true

    for (int i = 0; i < 5; i++) {
      // "123456" - plain text - user input from user interface
      String passwd = encoder.encode("123456");

      // passwd - password from database
      System.out.println(passwd); // print hash

      // true for all 5 iteration
      System.out.println(encoder.matches("123456", passwd));
    }
}

27
投票

生成的密码经过加盐处理,因此有所不同。

请阅读 encode() 方法的文档,其中明确指出密码已加盐。


21
投票

第三个 $ 后面的 22 个字符代表盐值,请参阅 https://en.wikipedia.org/wiki/Bcrypt#Description 。 “盐”是在散列之前添加到密码中的一些随机数据,因此具有给定参数的给定散列算法在大多数情况下将为同一密码生成不同的散列值(防止所谓的彩虹攻击)。

让我们剖析一下原始问题中显示的第一个输出:

$2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi

  • $2a
    :BCrypt 算法的标识符
  • $10
    :轮数参数,这里2^10 = 1.024轮
  • cYLM.qoXpeAzcZhJ3oXRLu
    :盐(128 位)
  • 9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
    :实际哈希值(184位)

盐和哈希值均使用 Radix-64 进行编码。


8
投票

这是完全正常的,因为 BCryptPasswordEncoder 使用盐来生成密码。您可以在此处此处了解“加盐”密码背后的想法。

这就是文档中关于

encode
方法的说法

对原始密码进行编码。一般来说,好的编码算法应用 SHA-1 或更大的哈希值与 8 字节或更大的随机生成的盐相结合。


1
投票

BCrypt 输出为: $2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi

$2a$表示哈希算法

10$ 是日志轮次

以下是盐和哈希密码

由于Spring每次生成的盐都会不同,所以你的输出也不一样。您可以参考 BCrypt 语法 https://en.wikipedia.org/wiki/Bcrypt#Description

© www.soinside.com 2019 - 2024. All rights reserved.