Search code examples
node.js.netbcrypt

Why is there a difference between hash results of nodeJs bcrypt and dotNet version


So the resulting hashes (using same number of salt rounds) are different, here is what i used :

NodeJs implementation : https://www.npmjs.com/package/bcrypt

dotNet implementation : http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx

Both resulting hashes start with $2a$12$ indicating we use same bcrypt version and same number of salt rounds but the results are different for same input.

Anyone has any idea why?

So first 3 times i'm checking if hashed passwords match using BCrypt.CheckPassword and next 3 i'm comparing with hashes generated in nodeJs

            string password = "SomePassword";

            byte[] bytes = Encoding.Default.GetBytes(password);
            password = Encoding.UTF8.GetString(bytes);


            //string candidate = "$2a$12$zDG1M72eRg9FAeSEJrVNNeJYh0Fa3DsuAP9nBa.IDgbDQLOw.525O";
            string candidate = "SomePassword";

            Console.OutputEncoding = System.Text.Encoding.UTF8;

            string hashed;
            bool matches;

            hashed = BCrypt.HashPassword(password, BCrypt.GenerateSalt(12));
            Console.WriteLine(hashed);
            matches = BCrypt.CheckPassword(candidate, hashed);
            Console.WriteLine(matches.ToString());

            hashed = BCrypt.HashPassword(password, BCrypt.GenerateSalt(12));
            Console.WriteLine(hashed);
            matches = BCrypt.CheckPassword(candidate, hashed);
            Console.WriteLine(matches.ToString());

            hashed = BCrypt.HashPassword(password, BCrypt.GenerateSalt(12));
            Console.WriteLine(hashed);
            matches = BCrypt.CheckPassword(candidate, hashed);
            Console.WriteLine(matches.ToString());


            matches = BCrypt.CheckPassword(candidate, "$2a$12$wnCnB5lEX8XT.QUnYVmzQ.pCC03QobNZ2uxDz17BASn03maFfnWGq");
            Console.WriteLine(matches.ToString());
            matches = BCrypt.CheckPassword(candidate, "$2a$12$eYbwrN6P.BYc.4NncivrQeOEGeXBKGwPcfnEGRSOgKJfWzZoO9auu");
            Console.WriteLine(matches.ToString());
            matches = BCrypt.CheckPassword(candidate, "$2a$12$kKaPRy9u9w.1Jjh/aG5PfuT3IohSDFIG/1B2i7twE9huGwAR/kMTm");
            Console.WriteLine(matches.ToString());

            Console.ReadKey();

output :

$2a$12$Yl3IO09nJ.dTc621yjvH5uUz7TOz/UjziEI5lZuNW6dED.K0GlOSC
True
$2a$12$t8ruHd0FrKNazO/t3j0dP.KG683vjyqiE7lErC7iQfwAxK6pLASG.
True
$2a$12$ZhbyzBh956Mxps9Y5pho9e.age8oj3VsJjf4ScWpv0nilSNx00Axq
True
False
False
False

Solution

  • Each time you do the check you are using a different salts which are included in the password hash, so this approach will never work.

    What you should be doing is using BCrypt.Verify(password, hash) which will use the same version, number of salt rounds and salt to verify whether the password is valid.

    It also appears that the password hash has extra spaces in it...

    var password = "Dule1Savic";
    
    var hash = "$2a$12$rmrr0tLPKOpX7BI1XEZj3.rn8US.J8Cm13A/eHm3uzg70XXzaMHSi";
    
    var isMatch = BCrypt.Net.BCrypt.Verify(password, hash);
    
    Console.WriteLine(isMatch); // True