mrgx mrgx - 1 year ago 138
MySQL Question

BCrypt Verify stored password hash

Storing the hashed password in database is successful. But when verifying if the input password and the hash stored in database, it always returning false.

Dim pw As String = txt_password.Text
Dim salt As String = BCrypt.Net.BCrypt.GenerateSalt(12)
Dim hash As String = BCrypt.Net.BCrypt.HashPassword(pw, salt)

With sqlLogin
.Parameters.AddWithValue("@userid", txt_username.Text)
.Parameters.AddWithValue("@userpass", hash)
End With

Dim Reader As MySqlDataReader = sqlLogin.ExecuteReader()
If (BCrypt.Net.BCrypt.Verify(pw, hash)) Then
MessageBox.Show("Login Succesful!", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Information)
ElseIf Reader.HasRows = False Then
MessageBox.Show("Invalid Login Information!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
txt_username.Text = ""
txt_password.Text = ""
End If

Answer Source

Especially when it comes to encryption, you ought to have some general idea of the principles and concepts involved. Salted Password Hashing explains common pitfalls and makes a number of recommendations (one is BCrypt, so you may be on the right path).

It looks like you are not reading the stored hash from the DB before you verify. You don't show how it is saved, but that is important in order for it to verify.

 ' cuts down on dot operators
 Imports BCryptor = BCrypt.Net.BCrypt

Create New Logon

' new user save
Dim sql = "INSERT INTO userlogin (email, username, pwhash) VALUES (@email, @n, @pw)"
' prep:
Dim salt = BCryptor.GenerateSalt(12)    ' == 2^12
Dim hash = BCryptor.HashPassword(tbPass)

' to do: Try/Catch for an email that already exists
Using dbCon As New MySqlConnection(MySQLConnStr),
    cmd As New MySqlCommand(sql, dbCon)

    cmd.Parameters.Add("@email", MySqlDbType.Text).Value = tbEmail
    cmd.Parameters.Add("@n", MySqlDbType.Text).Value = tbUserName
    cmd.Parameters.Add("@pw", MySqlDbType.Text).Value = hash

End Using

Verify an Attempt

Dim bRet As Boolean = False

' login user 
Dim sql = "SELECT pwhash FROM userlogin WHERE email = @email"

Using dbCon As New MySqlConnection(MySQLConnStr),
        cmd As New MySqlCommand(sql, dbCon)

    ' data for the where clause
    cmd.Parameters.Add("@email", MySqlDbType.Text).Value = tbEmail

    Using rdr = cmd.ExecuteReader()
       ' read from the reader to load data
        If rdr.Read() Then
            ' get the saved hash
            Dim savedHash = rdr.GetString(0)
            bRet = BCryptor.Verify(tbPass, savedHash)
            bRet = False
        End If
    End Using
    ' return whether the hash verified
    Return ret
End Using


  • DbConnection, DbCommand and DbDataReader all implement Dispose which means they may very well allocate resources which need to be released. The code uses each of them in a Using block. This creates them at the start and disposes of them at the end of the Block.
  • This uses an email for the unique identifier because there are a lot of Steves out there. This means the SQL will return one record at most.
  • After loading the hashed pw from the DB, use it to verify the password attempt entered. Your code seems to be creating a new hash (and doesnt actually load anything from the DB).

The random salt originally generated when the account was created becomes part of the hash (as well as the work factor you used) as shown here:

Dim pw = "My!Weak#Pa$$word"
Dim salt = BCryptor.GenerateSalt(12)
Dim hash = BCryptor.HashPassword(pw, salt)




The 12 after "$2a$" is the work factor.