BcryptMD5 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 The results are then:
|
Outline
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
You can see that Blowfish is almost four times slower than MD5 (380,000,000 words/sec down to only 25,860 words/sec). With John The Ripper:
md5crypt [MD5 32/64 X2] 318237 c/s real, 8881 c/s virtual bcrypt ("$2a$05", 32 iterations) 25488 c/s real, 708 c/s virtual LM [DES 128/128 SSE2-16] 88090K c/s real, 2462K c/s virtual
where you can see that Bcrypt is 3,000 times slower than LM hashes.
Here is the Python implementation:
import hashlib; import passlib.hash; salt="ZDzPE45C" string="password" salt2="1111111111111111111111" print "General Hashes" print "MD5:"+hashlib.md5(string).hexdigest() print "SHA1:"+hashlib.sha1(string).hexdigest() print "SHA256:"+hashlib.sha256(string).hexdigest() print "SHA512:"+hashlib.sha512(string).hexdigest() print "UNIX hashes (with salt)" print "DES:"+passlib.hash.des_crypt.encrypt(string, salt=salt[:2]) print "MD5:"+passlib.hash.md5_crypt.encrypt(string, salt=salt) print "Bcrypt:"+passlib.hash.bcrypt.encrypt(string, salt=salt2[:22]) print "Sun MD5:"+passlib.hash.sun_md5_crypt.encrypt(string, salt=salt) print "SHA1:"+passlib.hash.sha1_crypt.encrypt(string, salt=salt) print "SHA256:"+passlib.hash.sha256_crypt.encrypt(string, salt=salt) print "SHA512:"+passlib.hash.sha512_crypt.encrypt(string, salt=salt)
and which gives:
MD5:5f4dcc3b5aa765d61d8327deb882cf99 SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 SHA256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 SHA512:b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 UNIX hashes (with salt) DES:ZD3yxA4N/XZVg MD5:$1$ZDzPE45C$EEQHJaCXI6yInV3FnskmF1 Bcrypt:$2a$12$111111111111111111111uAQxS9vJNRtBb6zeFDV6k7tyB0DZJF0a Sun MD5:$md5,rounds=34000$ZDzPE45C$$RGKsbBUBhidHsaNDUMEEX0 SHA1:$sha1$480000$ZDzPE45C$gfgoLWRrJHj/ZiXsV101NCX1GfUH
In this case we see we are using 12 iterations and a pre-prepared salt of "1111111111111111111111" (22 characters to give a 128-bit salt value):
Bcrypt:$2a$12$111111111111111111111uAQxS9vJNRtBb6zeFDV6k7tyB0DZJF0a
We can increase the rounds to 20 with:
print "Bcrypt:"+passlib.hash.bcrypt.encrypt(string, salt=salt2[:22],rounds=14)
to give:
Bcrypt:$2a$14$NkYh0RCM8pNWPaYvRLgN9.OcinBT2h.8NWt/KfmHQ5eIr/50zCt8q
and which considerably slows down the hashing.
Presentation
Sample Code
The following integrates a Bcrypt library and uses 6 rounds. The salt sting produced will contain the rounds and the salt value:
using DevOne.Security.Cryptography.BCrypt; [HttpPost] public ActionResult bcrypt(FormCollection form) { string message = form["message"]; string salt = BCryptHelper.GenerateSalt(6); var hashedPassword = BCryptHelper.HashPassword(message, salt); ViewData["salt"] = salt; ViewData["message"] = message; ViewData["hash"] = hashedPassword; return PartialView("bcrypt_partial"); }