Question

Hello everyone I am currently trying to match my PHP login system which uses

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

My C#:

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

Problem: My hashed password I make with C# does not match the login PHP hashed password on database...

Was it helpful?

Solution

Your full function looks like this

/**
 * 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;
 }

EDIT: More research suggested that $2a$ which seems to be BlowFish Encryption from research. (http://php.net/crypt)

CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string. The two digit cost parameter is the base-2 logarithm of the iteration count for the underlying Blowfish-based hashing algorithmeter and must be in range 04-31, values outside this range will cause crypt() to fail. Versions of PHP before 5.3.7 only support "$2a$" as the salt prefix: PHP 5.3.7 introduced the new prefixes to fix a security weakness in the Blowfish implementation. Please refer to » this document for full details of the security fix, but to summarise, developers targeting only PHP 5.3.7 and later should use "$2y$" in preference to "$2a$".

It must begin with "$2", optional "a", "$", two digits, "$", and 22 base 64 digits. The rest of the string is ignored. The presence of the optional "a" means that a NUL is to be appended to the password before it is used as a key. The two digits set the cost parameter. The 22 base 64 digits encode the salt.


$13$ is called the cost. (how much times to loop the encryption). Now the salt itself is encoded in base64 which you have to decode it before using it with Eksblowfish

Eksblowfish, states for (expensive key schedule blowfish, is a cost parameterizable and salted variation of the blowfish block cipher.)

Anyways to summarize it you have to use this
http://bcrypt.codeplex.com/

which should be like this

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;

Password = test123abc
Output for textbox1 was: $2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS

C# test program screenshot

Also ran your code in php http://ideone.com/8piXMq

php Output for

echo hashPassword("test123abc");

Output for php was: $2a$13$/8Wncr26eAmxD1l6cAF9FuVnazDlahXc73He5NB1GKNYG7v3mOOyS

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

As you can see the answers are both IDENTICAL


As you can see from the BCrypt HashPassword implementation it decodes the base64 salt and re-encrypts your new password with the salt you specified.

/// <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();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top