4

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?

1 Answer 1

7
+200

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.

Sign up to request clarification or add additional context in comments.

2 Comments

This looks perfect. I'll try it tonight and if all is well, then you just really did me a huge favor
To future viewers: Note that this isn't "traditionally" compatible with HashAlgorithm. Because of how BCrypt works, it has what I call a "tracked salt". You can't just add the salt to the hashed password or something like that, you must explicitly store the salt somewhere in plain text(or encrypted) in order to get the same hash for the password.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.