对Bcrypt C#盐有问题
大家好,我正在尝试匹配我的PHP登录系统,它使用对Bcrypt C#盐有问题,c#,bcrypt,C#,Bcrypt,大家好,我正在尝试匹配我的PHP登录系统,它使用 define('PASSWORD_ENCRYPTION', "bcrypt"); define('PASSWORD_SHA512_ITERATIONS', 25000); define('PASSWORD_BCRYPT_COST', "13"); define('PASSWORD_SALT', "/8Wncr26eAmxD1l6cAF9F8"); //22 characters to be appended on first 7 char
define('PASSWORD_ENCRYPTION', "bcrypt");
define('PASSWORD_SHA512_ITERATIONS', 25000);
define('PASSWORD_BCRYPT_COST', "13");
define('PASSWORD_SALT', "/8Wncr26eAmxD1l6cAF9F8"); //22 characters to be appended on first 7 characters that will be generated using PASSWORD_ info above
我的C#:
问题:我用C#创建的哈希密码与数据库上的登录PHP哈希密码不匹配…您的完整函数如下所示
/**
* Hash given password.
* @param string $password Unhashed password.
* @return string Hashed password.
*/
public function hashPassword($password) {
//this salt will be used in both algorithms
//for bcrypt it is required to look like this,
//for sha512 it is not required but it can be used
$salt = "$2a$" . PASSWORD_BCRYPT_COST . "$" . PASSWORD_SALT;
if(PASSWORD_ENCRYPTION == "bcrypt") {
$newPassword = crypt($password, $salt);
}
else {
$newPassword = $password;
for($i=0; $i<PASSWORD_SHA512_ITERATIONS; $i++)
$newPassword = hash('sha512',$salt.$newPassword.$salt);
}
return $newPassword;
}
int PASSWORD_BCRYPT_COST = 13;
string PASSWORD_SALT = "/8Wncr26eAmxD1l6cAF9F8";
string salt = "$2a$" + PASSWORD_BCRYPT_COST + "$" + PASSWORD_SALT;
string password "test123abc";
var hash = BCrypt.HashPassword(password, salt);
textBox1.Text = hash;
密码=test123abctextbox1
的输出为:$2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS
还使用php运行了您的代码
的php输出
echo hashPassword("test123abc");
php的输出为:2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS
C#$2a$13$/8WNCR26EAMXD1L6CAF9FUVNAZDLAHXC73HE5NB1GKNYG7V3OOYS
PHP
$2a$13$/8WNCR26EAMXD1L6CAF9FUVNAZDLAHXC73HE5NB1GKNYG7V3OOYS
如您所见,答案都是相同的
从BCrypt HashPassword实现中可以看到,它解码base64 salt并使用指定的salt重新加密新密码
/// <summary>
/// Hash a password using the OpenBSD bcrypt scheme.
/// </summary>
/// <param name="password">The password to hash.</param>
/// <param name="salt">The salt to hash with (perhaps generated
/// using <c>BCrypt.GenerateSalt</c>).</param>
/// <returns>The hashed password.</returns>
public static string HashPassword(string password, string salt) {
if (password == null) {
throw new ArgumentNullException("password");
}
if (salt == null) {
throw new ArgumentNullException("salt");
}
char minor = (char)0;
if (salt[0] != '$' || salt[1] != '2') {
throw new ArgumentException("Invalid salt version");
}
int offset;
if (salt[1] != '$') {
minor = salt[2];
if (minor != 'a' || salt[3] != '$') {
throw new ArgumentException("Invalid salt revision");
}
offset = 4;
} else {
offset = 3;
}
// Extract number of rounds
if (salt[offset + 2] > '$') {
throw new ArgumentException("Missing salt rounds");
}
int rounds = Int32.Parse(salt.Substring(offset, 2), NumberFormatInfo.InvariantInfo);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password + (minor >= 'a' ? "\0" : String.Empty));
byte[] saltBytes = DecodeBase64(salt.Substring(offset + 3, 22),
BCRYPT_SALT_LEN);
BCrypt bcrypt = new BCrypt();
byte[] hashed = bcrypt.CryptRaw(passwordBytes, saltBytes, rounds);
StringBuilder rs = new StringBuilder();
rs.Append("$2");
if (minor >= 'a') {
rs.Append(minor);
}
rs.Append('$');
if (rounds < 10) {
rs.Append('0');
}
rs.Append(rounds);
rs.Append('$');
rs.Append(EncodeBase64(saltBytes, saltBytes.Length));
rs.Append(EncodeBase64(hashed,
(bf_crypt_ciphertext.Length * 4) - 1));
return rs.ToString();
}
//
///使用OpenBSD bcrypt方案散列密码。
///
///要哈希的密码。
///要散列的盐(可能是生成的)
///使用BCrypt.GenerateSalt)。
///哈希密码。
公共静态字符串HashPassword(字符串密码、字符串salt){
如果(密码==null){
抛出新的ArgumentNullException(“密码”);
}
if(salt==null){
抛出新的异常(“salt”);
}
char minor=(char)0;
if(salt[0]!='$'| | salt[1]!='2'){
抛出新ArgumentException(“无效的salt版本”);
}
整数偏移量;
如果(salt[1]!=“$”){
小调=盐[2];
如果(小调!=“a”| salt[3]!=“$”){
抛出新的ArgumentException(“无效的salt修订”);
}
偏移量=4;
}否则{
偏移量=3;
}
//提取轮数
如果(盐[偏移量+2]>“$”){
抛出新的ArgumentException(“缺少盐回合”);
}
int rounds=Int32.Parse(salt.Substring(offset,2),NumberFormatInfo.InvariantInfo);
byte[]passwordBytes=Encoding.UTF8.GetBytes(password+(minor>='a'?“\0”:String.Empty));
字节[]saltBytes=DecodeBase64(salt.Substring(偏移量+3,22),
b crypt_SALT_LEN);
BCrypt BCrypt=新的BCrypt();
byte[]hashed=bcrypt.CryptRaw(passwordBytes、saltBytes、rounds);
StringBuilder rs=新的StringBuilder();
卢比(“$2”);
如果(次要>='a'){
卢比(小调);
}
rs.Append(“$”);
如果(轮数<10){
rs.Append('0');
}
卢比(轮);
rs.Append(“$”);
rs.Append(EncodeBase64(saltBytes,saltBytes.Length));
rs.Append(EncodeBase64)(散列,
(bf_crypt_ciphertext.Length*4)-1);
返回rs.ToString();
}
您的全部功能如下所示
/**
* Hash given password.
* @param string $password Unhashed password.
* @return string Hashed password.
*/
public function hashPassword($password) {
//this salt will be used in both algorithms
//for bcrypt it is required to look like this,
//for sha512 it is not required but it can be used
$salt = "$2a$" . PASSWORD_BCRYPT_COST . "$" . PASSWORD_SALT;
if(PASSWORD_ENCRYPTION == "bcrypt") {
$newPassword = crypt($password, $salt);
}
else {
$newPassword = $password;
for($i=0; $i<PASSWORD_SHA512_ITERATIONS; $i++)
$newPassword = hash('sha512',$salt.$newPassword.$salt);
}
return $newPassword;
}
int PASSWORD_BCRYPT_COST = 13;
string PASSWORD_SALT = "/8Wncr26eAmxD1l6cAF9F8";
string salt = "$2a$" + PASSWORD_BCRYPT_COST + "$" + PASSWORD_SALT;
string password "test123abc";
var hash = BCrypt.HashPassword(password, salt);
textBox1.Text = hash;
密码=test123abctextbox1
的输出为:$2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS
还使用php运行了您的代码
的php输出
echo hashPassword("test123abc");
php的输出为:2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS
C#$2a$13$/8WNCR26EAMXD1L6CAF9FUVNAZDLAHXC73HE5NB1GKNYG7V3OOYS
PHP
$2a$13$/8WNCR26EAMXD1L6CAF9FUVNAZDLAHXC73HE5NB1GKNYG7V3OOYS
如您所见,答案都是相同的
从BCrypt HashPassword实现中可以看到,它解码base64 salt并使用指定的salt重新加密新密码
/// <summary>
/// Hash a password using the OpenBSD bcrypt scheme.
/// </summary>
/// <param name="password">The password to hash.</param>
/// <param name="salt">The salt to hash with (perhaps generated
/// using <c>BCrypt.GenerateSalt</c>).</param>
/// <returns>The hashed password.</returns>
public static string HashPassword(string password, string salt) {
if (password == null) {
throw new ArgumentNullException("password");
}
if (salt == null) {
throw new ArgumentNullException("salt");
}
char minor = (char)0;
if (salt[0] != '$' || salt[1] != '2') {
throw new ArgumentException("Invalid salt version");
}
int offset;
if (salt[1] != '$') {
minor = salt[2];
if (minor != 'a' || salt[3] != '$') {
throw new ArgumentException("Invalid salt revision");
}
offset = 4;
} else {
offset = 3;
}
// Extract number of rounds
if (salt[offset + 2] > '$') {
throw new ArgumentException("Missing salt rounds");
}
int rounds = Int32.Parse(salt.Substring(offset, 2), NumberFormatInfo.InvariantInfo);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password + (minor >= 'a' ? "\0" : String.Empty));
byte[] saltBytes = DecodeBase64(salt.Substring(offset + 3, 22),
BCRYPT_SALT_LEN);
BCrypt bcrypt = new BCrypt();
byte[] hashed = bcrypt.CryptRaw(passwordBytes, saltBytes, rounds);
StringBuilder rs = new StringBuilder();
rs.Append("$2");
if (minor >= 'a') {
rs.Append(minor);
}
rs.Append('$');
if (rounds < 10) {
rs.Append('0');
}
rs.Append(rounds);
rs.Append('$');
rs.Append(EncodeBase64(saltBytes, saltBytes.Length));
rs.Append(EncodeBase64(hashed,
(bf_crypt_ciphertext.Length * 4) - 1));
return rs.ToString();
}
//
///使用OpenBSD bcrypt方案散列密码。
///
///要哈希的密码。
///要散列的盐(可能是生成的)
///使用BCrypt.GenerateSalt)。
///哈希密码。
公共静态字符串HashPassword(字符串密码、字符串salt){
如果(密码==null){
抛出新的ArgumentNullException(“密码”);
}
if(salt==null){
抛出新的异常(“salt”);
}
char minor=(char)0;
if(salt[0]!='$'| | salt[1]!='2'){
抛出新ArgumentException(“无效的salt版本”);
}
整数偏移量;
如果(salt[1]!=“$”){
小调=盐[2];
如果(小调!=“a”| salt[3]!=“$”){
抛出新的ArgumentException(“无效的salt修订”);
}
偏移量=4;
}否则{
偏移量=3;
}
//提取轮数
如果(盐[偏移量+2]>“$”){
抛出新的ArgumentException(“缺少盐回合”);
}
int rounds=Int32.Parse(salt.Substring(offset,2),NumberFormatInfo.InvariantInfo);
byte[]passwordBytes=Encoding.UTF8.GetBytes(password+(minor>='a'?“\0”:String.Empty));
字节[]saltBytes=DecodeBase64(salt.Substring(偏移量+3,22),
b crypt_SALT_LEN);
BCrypt BCrypt=新的BCrypt();
byte[]hashed=bcrypt.CryptRaw(passwordBytes、saltBytes、rounds);
StringBuilder rs=新的StringBuilder();
卢比(“$2”);
如果(次要>='a'){
卢比(小调);
}
rs.Append(“$”);
如果(轮数<10){
rs.Append('0');
}
卢比(轮);
rs.Append(“$”);
rs.Append(EncodeBase64(saltBytes,saltBytes.Length));
rs.Append(EncodeBase64)(散列,
(bf_crypt_ciphertext.Length*4)-1);
返回rs.ToString();
}
您不应该这样做。您只需确保BCrypt.Net生成的散列可以通过PHP库进行验证,反之亦然。Aron您能给我举个例子说明如何正确实现这一点吗?您的PHP脚本在散列到数据库中时必须使用不同的密码\u SALT
,数据库散列看起来如何?(显示示例)因为我运行了您的脚本,所以它为BCrypt提供了完全相同的结果。如果数据库中$2a$13$
后面的22个字符与密码\u SALT
不同,则您有不同的SALT,您现在必须使用不同的SALT