遇到Bcrypt C#Salt问题

Gha*_*our 1 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 characters that will be generated using PASSWORD_ info above
Run Code Online (Sandbox Code Playgroud)

我的C#:

            string myPassword = this.password_txt.Text;
            string mySalt = "$2a$13$/8Wncr26eAmxD1l6cAF9F8";
            string hashed = BCrypt.HashPassword(myPassword, mySalt);
Run Code Online (Sandbox Code Playgroud)

问题:我使用C#编写的哈希密码与数据库上的登录PHP哈希密码不匹配...

SSp*_*oke 6

你的全部功能看起来像这样

/**
 * 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;
 }
Run Code Online (Sandbox Code Playgroud)

编辑:更多的研究表明,$2a$这似乎BlowFish Encryption来自研究.(http://php.net/crypt)

CRYPT_BLOWFISH - 带有盐的河豚散列如下:"$ 2a $","$ 2x $"或"$ 2y $",两位数的成本参数"$",字母表中的22个字符"./0-9A -za-Z".在salt中使用此范围之外的字符将导致crypt()返回零长度字符串.两位数的成本参数是底层基于Blowfish的散列算法的迭代计数的基数2对数,并且必须在04-31范围内,超出此范围的值将导致crypt()失败.5.3.7之前的PHP版本仅支持"$ 2a $"作为salt前缀:PHP 5.3.7引入了新的前缀来修复Blowfish实现中的安全漏洞.有关安全修复程序的完整详细信息,请参阅»此文档,但总而言之,仅针对PHP 5.3.7及更高版本的开发人员应使用"$ 2y $"优先于"$ 2a $".

它必须以"$ 2",可选"a","$",两位数字,"$"和22位64位数字开头.字符串的其余部分将被忽略.可选"a"的存在意味着在将密码用作密钥之前将NUL附加到密码.两位数设置成本参数.22个基本64位数字编码盐.


$13$被称为cost.(循环加密多少次).现在,盐本身已编码base64,您必须在使用之前对其进行解码Eksblowfish

Eksblowfish,状态为(昂贵的关键时间表河豚,是河豚块密码的成本可参数化和盐渍变化.)

无论如何总结一下,你必须使用这个
http://bcrypt.codeplex.com/

这应该是这样的

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;
Run Code Online (Sandbox Code Playgroud)

密码= test123abc
输出为textbox1:$2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS

C#测试程序截图

还在php http://ideone.com/8piXMq中运行你的代码

php输出

echo hashPassword("test123abc");
Run Code Online (Sandbox Code Playgroud)

php的输出是: $2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS

C#
$2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS
PHP
$2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS

正如您所看到的,答案都是IDENTICAL


从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();
}
Run Code Online (Sandbox Code Playgroud)