Java 为什么 Spring 的 BCryptPasswordEncoder 为相同的输入生成不同的输出?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25844419/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why BCryptPasswordEncoder from Spring generate different outputs for same input?
提问by Bhavesh
I am using BCryptPasswordEncoderwith Spring security. My expectation was that for the same input I will always get the same output. But for the same input I get different output. You could test it with the code snippet below:
我正在使用具有 Spring 安全性的BCryptPasswordEncoder。我的期望是对于相同的输入,我将始终获得相同的输出。但是对于相同的输入,我得到不同的输出。您可以使用以下代码片段对其进行测试:
String password = "123456";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(password);
System.out.print(encodedPassword);
output: $2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
output2: $2a$10$KEvYX9yjj0f1X3Wl8S.KPuWzSWGyGM9ubI71NOm3ZNbJcwWN6agvW
output3: $2a$10$nCmrPtUaOLn5EI73VZ4Ouu1TmkSWDUxxD4N6A.8hPBWg43Vl.RLDC
输出:$2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
输出 2:$2a$10$KEvYX9yjj0f1X3Wl8S.KPuWzSWGyGM9ubI71NOm3ZNbJcwWN6agvW
输出 3:$2a$10$nCmrPtUaOLn5EI73VZ4Ouu1TmkSWDUxxD4N6A.8hPBWg43Vl.RLDC
Could someone explain, why BCryptPasswordEncoder behave like this?
有人可以解释一下,为什么 BCryptPasswordEncoder 会这样?
采纳答案by blueberry0xff
public static void main(String[] args) {
// spring 4.0.0
org.springframework.security.crypto.password.PasswordEncoder encoder
= new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();
// a$lB6/PKg2/JC4XgdMDXyjs.dLC9jFNAuuNbFkL9udcXe/EBjxSyqxW
// true
// a$KbQiHKTa1WIsQFTQWQKCiujoTJJB7MCMSaSgG/imVkKRicMPwgN5i
// true
// aWfW4uxVb4SIdzcTJI9U7eU4ZwaocrvP.2CKkWJkBDKz1dmCh50J2
// true
// a##代码##wR/6uaPxU7kGyUIsx/JS.krbAA9429fwsuCyTlEFJG54HgdR10nK
// true
// a$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));
}
}
回答by geoand
That is perfectly normal because BCryptPasswordEncoderuses a salt to generate the password. You can read about the idea behind "salting" a password hereand here.
这是完全正常的,因为BCryptPasswordEncoder使用盐来生成密码。您可以在此处和此处阅读有关“加盐”密码背后的想法。
This is what the documentation says for the encode
method
这是文档中对该encode
方法的说明
Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or greater hash combined with an 8-byte or greater randomly generated salt.
对原始密码进行编码。通常,好的编码算法应用 SHA-1 或更大的哈希值与 8 字节或更大的随机生成的盐值相结合。
回答by Uwe Plonus
回答by user1364368
The 22 characters directly after the 2nd $ represent the salt value, see https://en.wikipedia.org/wiki/Bcrypt#Description. "Salt" is some random data added to the password before hashing, so a given hash algorithm with given parameters will in most cases produce different hash values for the same password (protection against so called rainbow attacks).
紧跟在第二个 $ 之后的 22 个字符表示盐值,请参阅https://en.wikipedia.org/wiki/Bcrypt#Description。“盐”是在散列之前添加到密码中的一些随机数据,因此具有给定参数的给定散列算法在大多数情况下会为同一密码生成不同的散列值(防止所谓的彩虹攻击)。
Let's dissect the first output shown in the original question:
$2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
让我们剖析原始问题中显示的第一个输出:
$2a$10$cYLM.qoXpeAzcZhJ3oXRLu9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
$2a
: Identifier for BCrypt algorithm$10
: Parameter for number of rounds, here 2^10 roundscYLM.qoXpeAzcZhJ3oXRLu
: Salt (128 bits)9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
: Actual hash value (184 bits)
$2a
: BCrypt 算法的标识符$10
: 轮数参数,这里是 2^10 轮cYLM.qoXpeAzcZhJ3oXRLu
:盐(128 位)9Slkb61LHyWW5qJ4QKvHEMhaxZ5qCPi
: 实际哈希值(184 位)
The salt and the hash value are both encoded using Radix-64.
盐和哈希值都使用 Radix-64 编码。