Bcrypt Hashes using .NETMD5 and SHA-1 produce a hash signature, but this can be attacked by rainbow tables. Bcrypt is a more powerful hash generator for passwords and uses salt to create a non-recurrent hash. It was designed by Niels Provos and David Mazières, and is based on the Blowfish cipher. It is used as the default password hashing method for BSD and other systems. |
Theory
Overall it uses a 128-bit salt value, which requires 22 Radix-64 characters. It can use a number of iterations, which will slow down any brute-force cracking of the hashed value.
For example, “Hello” with a salt value of “\$2a\$06\$NkYh0RCM8pNWPaYvRLgN9.” gives:
$2a$06$NkYh0RCM8pNWPaYvRLgN9.LbJw4gcnWCOQYIom0P08UEZRQQjbfpy
As illustrated below, the first part is "\$2a\$" (or "\$2b\$"), and then followed by the number of iterations used (in this case is it 6 iterations (where each additional iternation doubles the hash time). The 128-bit (22 character) salt values comes after this, and then finally there is a 184-bit hash code (which is 31 characters).
The slowness of Bcrypt is highlighted with a recent AWS EC2 server benchmark using hashcat [here]:
Hash type: MD5 Speed/sec: 380.02M words Hash type: SHA1 Speed/sec: 218.86M words Hash type: SHA256 Speed/sec: 110.37M words Hash type: bcrypt, Blowfish(OpenBSD) Speed/sec: 25.86k words Hash type: NTLM. Speed/sec: 370.22M words
The salt value will be generated automatically. The work factor is 2^number of rounds, and is often set to 11 by default. set to 11 (which matches the default across most implementation and is currently viewed as a good level of security/risk). The mapping is:
| Cost | Iterations | |-------|--------------------------| | 8 | 256 iterations | | 9 | 512 iterations | | 10 | 1,024 iterations | | 11 | 2,048 iterations | | 12 | 4,096 iterations | | 13 | 8,192 iterations | | 14 | 16,384 iterations | | 15 | 32,768 iterations | | 16 | 65,536 iterations | | 17 | 131,072 iterations | | 18 | 262,144 iterations | | 19 | 524,288 iterations | | 20 | 1,048,576 iterations | | 21 | 2,097,152 iterations | | 22 | 4,194,304 iterations | | 23 | 8,388,608 iterations | | 24 | 16,777,216 iterations | | 25 | 33,554,432 iterations | | 26 | 67,108,864 iterations | | 27 | 134,217,728 iterations | | 28 | 268,435,456 iterations | | 29 | 536,870,912 iterations | | 30 | 1,073,741,824 iterations | | 31 | 2,147,483,648 iterations |
Coding
First we create a new folder, and then create our console application with:
dotnet new console --framework net8.0
Next we can install the .NET component of:
dotnet add package BCrypt.Net-Next
The coding is:
namespace bcrtypt { using BCryptNet = BCrypt.Net.BCrypt; class Program { static void Main(string[] args) { int cost=16; string msg="Pa$$w0rd"; if (args.Length >0) msg=args[0]; if (args.Length >1) cost= int.Parse(args[1]); string passwordHash = BCrypt.Net.BCrypt.HashPassword(msg,workFactor:cost); Console.WriteLine("Password:\t{0}\nCost:\t\t{1}\nBCrypt:\t\t{2}",msg,cost,passwordHash); bool rtn= BCrypt.Net.BCrypt.Verify(msg, passwordHash); Console.WriteLine("\nHash verified {0}",rtn); } } }
A sample run with a cost of 6 is::
Password: abc Cost: 6 BCrypt: $2a$06$rDoRFINMAGaPbgKm1Q0zDusNG/5Ww.N.VTKwdKzImOLPpgXPDWLj6 Hash verified True