Search code examples
.nethashbcryptpassword-hashbcrypt.net

.Net implementation of bcrypt, which implements HashAlgorithm?


I'm looking to allow bcrypt support in my authentication library. One of the problems right now is that I assume that the hasher will be of type HashAlgorithm. Bcrypt.net does not implement this class. Also, it's sealed so I would have to make my own branch off of it and modify it myself. Are there any better alternatives that already implement HashAlgorithm?


Solution

  • Try this:

    public class BCryptHasher : HashAlgorithm
    {
        private MemoryStream passwordStream = null;
    
        protected override void HashCore(byte[] array, int ibStart, int cbSize)
        {
            if (passwordStream == null || Salt == null)
                Initialize();
    
            passwordStream.Write(array, ibStart, cbSize);
        }
    
        protected override byte[] HashFinal()
        {
            passwordStream.Flush();
    
            // Get the hash
            return Encoding.UTF8.GetBytes(BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(passwordStream.ToArray()), Salt));            
        }
    
        public override void Initialize()
        {
            passwordStream = new MemoryStream();
    
            // Set up salt
            if (Salt == null)
            {
                if (WorkFactor == 0)
                    Salt = BCrypt.Net.BCrypt.GenerateSalt();
                else
                    Salt = BCrypt.Net.BCrypt.GenerateSalt(WorkFactor);
            }
        }
    
        public int WorkFactor { get; set; }
    
        public string Salt { get; set; }
    
        public bool Verify(string plain, string hash)
        {
            return BCrypt.Net.BCrypt.Verify(plain, hash);
        }
    }
    

    Usage:

    BCryptHasher hasher = new BCryptHasher();
    string pw = "abc";
    string hash = Encoding.UTF8.GetString(hasher.ComputeHash(Encoding.UTF8.GetBytes(pw)));
    

    Also, I added a helper Verify method so you can verify that the password and hash match, but you can eliminate this if you just call the default BCrypt.Verify.

    bool matches = hasher.Verify(pw, hash);
    

    I added some extra properties so you can pass in a pre-computed salt or a work factor to generate a new salt before you do the hash:

    string pw = "abc";
    hasher.Salt = "$2a$06$If6bvum7DFjUnE9p2uDeDu";
    string hash = Encoding.UTF8.GetString(hasher.ComputeHash(Encoding.UTF8.GetBytes(pw)));
    

    I tried it with the BCrypt test case "abc" with a salt of "$2a$06$If6bvum7DFjUnE9p2uDeDu" and got the correct hash.