Rob Kraft's Software Development Blog

Software Development Insights

Use BCrypt to Hash Your Passwords: Example for C# and SQL Server

Posted by robkraft on October 9, 2012

By now you know passwords should be stored using a hash.  Given your decision to do the right thing and hash your passwords you still have to decide some implementation details.

First, choose a hashing algorithm.  Choose BCrypt.  Why BCrypt?  I’ll give you two reasons:

  1. It is slow, and slow is good because it thwarts brute-force attacks (read more here: http://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage).
  2. The output from BCrypt is a Base-64 alphabet (http://tools.ietf.org/html/rfc4648#section-4) which means there are no characters that are tricksy to store in a simple character field; CodePage is irrelevant.

Second, find a reliable implementation of BCrypt.  I am going to show an example of using C#.Net and SQL Server, but here is a good reference I found using  PHP and MySQL (http://oscarm.org/2012/6/using-bcrypt-store-passwords).  I also say a “reliable implementation” because there are flaws in some implementations, such as one discovered in 2011 and discussed in these articles (http://en.wikipedia.org/wiki/Crypt_(Unix)http://www.digipedia.pl/usenet/thread/16234/200/ (Search for $2y$) in this second article. – $2y$ indicates you are using a version of BCrypt for Unix that does not contain this bug).

I am using Derek Slager’s C# implementation of BCrypt downloaded from here:
http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx.  Based on a little testing I did myself, I believe it does not contain the flaw cited in the above article, but I am no expert at this.  Even if the bug discovered in 2011 exists in this implementation of BCrypt, it is of little concern to me as all of my users are located within the U.S. and are extremely unlikely to be using password characters that cannot be directly entered from a standard keyboard (characters with ASCII values greater than 127).  And even if a user does have such a password, the attack vector remains incredibly tiny for exploitation.

Third, understand the inputs and outputs.  BCrypt includes a method to generate a salt.  When the salt is applied to the password, the resulting hash holds the original salt and the hashed password.  You can store the salt and password combined in a CHAR(60) field in your database.  You don’t need to store the hashed password separately from the salt, nor should you, since the BCrypt class contains a method that expects the salt and password combined to be passed in as a parameter when later confirming the correctness of the user-entered password.

Note, the salt always begins with something like $2a$10$ meaning version 2a of BCrypt and 10 rounds of computations.  10 rounds is the default.  You can choose larger numbers to make it slower, or smaller numbers to make it faster, but 10 is a really good choice for most of us.  Since the rest of the salt is 22 bytes, and the $2a$10$ is 7 bytes for a total of 29 bytes, the hashed password is always the remaining 31 bytes.  The total length of the output that you will store in the database is always 60 bytes long.

<br>
string myPassword = "password";<br>
string mySalt = BCrypt.GenerateSalt();<br>
//mySalt == "$2a$10$rBV2JDeWW3.vKyeQcM8fFO"<br>
string myHash = BCrypt.HashPassword(myPassword, mySalt);<br>
//myHash == "$2a$10$rBV2JDeWW3.vKyeQcM8fFO4777l4bVeQgDL6VIkxqlzQ7TCalQvla"<br>
bool doesPasswordMatch = BCrypt.CheckPassword(myPassword, myHash);<br>

Each password stored will have a different salt, and every time a user changes their password you will generate a new salt for the user.  I also encourage you to add a little hard-coded salt to the password.  This hard-coded salt adds a little more challenge to brute force attacks from hackers that steal your database, but have not stolen your code and don’t have the hard-coded salt.

<br>
private void SetPassword(string user, string userPassword)<br>
{<br>
   string pwdToHash = userPassword + "^Y8~JJ"; // ^Y8~JJ is my hard-coded salt<br>
   string hashToStoreInDatabase = BCrypt.HashPassword(pwdToHash, BCrypt.GenerateSalt());<br>
   using (SqlConnection sqlConn = new System.Data.SqlClient.SqlConnection(...)<br>
   {<br>
     sqlConn.Open();<br>
     SqlCommand cmSql = sqlConn.CreateCommand();<br>
     cmSql.CommandText = "UPDATE LOGINS SET PASSWORD=@parm1 WHERE USERNAME=@parm2";<br>
     cmSql.Parameters.Add("@parm1", SqlDbType.Char);<br>
     cmSql.Parameters.Add("@parm2", SqlDbType.VarChar);<br>
     cmSql.Parameters["@parm1"].Value = hashToStoreInDatabase;<br>
     cmSql.Parameters["@parm2"].Value = user;<br>
     cmSql.ExecuteNonQuery();<br>
   }<br>
 }</p>
<p>private bool DoesPasswordMatch(string hashedPwdFromDatabase, string userEnteredPassword)<br>
{<br>
    return BCrypt.CheckPassword(userEnteredPassword + "^Y8~JJ", hashedPwdFromDatabase);<br>
}<br>

Another reference to BCrypt compared to SHA512:
http://stackoverflow.com/questions/1561174/sha512-vs-blowfish-and-bcrypt
.

Update for 2022: Some algorithms are now better than BCrypt because they are more difficult (take more time) to brute-force crack. Argon2 is the best, and PBKDF2 is also really good. However, only PBKDF2 is in .Net Core (or so I’ve read), and if you find an Argon2 library on github (or elsewhere), you have to decide for yourself if it is trustworthy and correctly implmeneted.

4 Responses to “Use BCrypt to Hash Your Passwords: Example for C# and SQL Server”

  1. Nath said

    How to verify a password saved in the database that is encrypted with the hash + salt, with the password that the user entered in a textbox? and if they match the login is successful and if they do not match the login failed

  2. robkraft said

    Have your code read the hash + salt that is in the database, then call the method DoesPasswordMatch above like this:
    DoesPasswordMatch(hash + salt, PasswordUserEnteredInTextbox).
    The code will apply the salt to the password the user entered in the textbox and hash it again and compare that new hash to the one that already exists in the database.
    Based on the full example I have above, your code would first read the users current password from the database:
    “SELECT PASSWORD FROM LOGINS WHERE USERNAME=@parm”
    Then you would pass the result of that query into
    DoesPasswordMatch(PasswordFromDatabase, PasswordUserEnteredInTextbox)

    • Riley Farro said

      Do I really need to use hardcoded salt? I do not want to use it and after I verify my password from database, it is always false even though the hash and text password is correct? What is supposed to be the problem?

      • robkraft said

        Hi Riley, that is a good question. If you want your hashing algorithm to be good enough to pass most audits you probably do not need to include the hard coded salt. But the extra hard-coded salt does help prevent the stealing of passwords in the following scenario: If a thief steals the database they will see the user logon and hashed password. By looking at the hash they will recognize the password was hashed using BCrypt. If the hackers know just one password belonging to one login (perhaps because the hacker created their own legitimate login prior to stealing the database); then they hacker can vary the number of computation rounds (I used 10 in my example) until they generate a matching hash. To rephrase that, the hackers can re-write your code and hashing algorithm because the algorithm is public and well known – but what is difficult for them is to know the number of computations. If they have a known login and password, they can try varying number of computations until they get a match. BUT, if you added some extra value to the password, then the hacker will have much more difficulty in this approach because they don’t really know the full password that was provided to the algorithm – they only know the part of the password the user entered. Ultimately, this is just adding another barrier to make it a little harder for the hackers. Nothing is 100% foolproof because if the hackers also steal your programs or code, they can probably analyze the code and discover the extra salt. We should never believe that we will built a solution that prevents hacking; but we want to make it as difficult to hack as is reasonable for the value of the data, and we also want to erect barriers that can slow down the hacking process us to give us time to detect the hack and take steps to protect the information.

Leave a comment