Link to home
Start Free TrialLog in
Avatar of Brian
BrianFlag for United States of America

asked on

Authenticate against password hash & salt

Hello,

I have the following code below that works when authenticating against a username and password in my database. I need the ability to check against the passwordhash and the passwordsalt to authenticate a user.

I'm going to attach the code that i have for the hash and salt below.
Protected Sub pssalogin_click(ByVal sender As Object, ByVal e As ImageClickEventArgs)
 
        Session("username") = txtUsername.Text
 
        ' Create Connection to Database
        Dim connectionString As String = ConfigurationManager.ConnectionStrings("PSSA").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)
 
        'declare the command
        Dim comm As SqlCommand
 
        comm = New SqlCommand("LoginData", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure
 
        'declare the parameters
        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text
        comm.Parameters.Add("@passwordhash", SqlDbType.VarChar, 128)
        comm.Parameters("@passwordhash").Value = txtPassword.Text
 
        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then
                FormsAuthentication.RedirectFromLoginPage(txtUsername.Text, True)
            Else
                pssaerrors.Text = "your login credentials are invalid!!!"
            End If
            myreader.Close()
        Catch
            pssaerrors.Text = "the pssa database is currently offline."
        Finally
            conn.Close()
        End Try
    End Sub
 
 
HASH AND SALT CODE:
 
Public Function gfCreatePasswordHash(ByVal vstrPwd As String, ByVal vstrSalt As String) As String
        Dim strSaltAndPwd As String = String.Concat(vstrPwd, vstrSalt)
        Dim strHashedPwd As String = FormsAuthentication.HashPasswordForStoringInConfigFile(strSaltAndPwd, "SHA1")
        Return strHashedPwd
    End Function
 
    Public Function gfCreateSalt(ByVal intSize As Integer) As String
        ' Generate a cryptographic random number using the cryptographic
        ' service provider
        Dim rng As RNGCryptoServiceProvider = New RNGCryptoServiceProvider
        Dim buff() As Byte = New Byte(intSize) {}
        rng.GetBytes(buff)
        ' Return a Base64 string representation of the random number
        Return Convert.ToBase64String(buff)
    End Function
 
    Protected Sub btnAddUser_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddUser.Click
        If Page.IsValid Then
 
            Dim strSalt As String
 
            Dim conn As SqlConnection
            Dim Insertcomm As SqlCommand
 
            Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
 
            conn = New SqlConnection(connectionString)
 
            Insertcomm = New SqlCommand("SaltInsert", conn)
            Insertcomm.CommandType = CommandType.StoredProcedure
 
            Insertcomm.Parameters.Add("@name", System.Data.SqlDbType.VarChar, 50)
            Insertcomm.Parameters("@name").Value = txtName.Text
 
            Insertcomm.Parameters.Add("@email", System.Data.SqlDbType.VarChar, 100)
            Insertcomm.Parameters("@email").Value = txtEmail.Text
 
            Insertcomm.Parameters.Add("@username", System.Data.SqlDbType.VarChar, 50)
            Insertcomm.Parameters("@username").Value = txtUsername.Text
 
            strSalt = gfCreateSalt(5)   'This creates a 5-digit salt
            Insertcomm.Parameters.Add("@passwordsalt", System.Data.SqlDbType.VarChar, 128)
            Insertcomm.Parameters("@passwordsalt").Value = strSalt
 
            Insertcomm.Parameters.Add("@passwordhash", System.Data.SqlDbType.VarChar, 128)
            Insertcomm.Parameters("@passwordhash").Value = gfCreatePasswordHash(txtPassword.Text, strSalt)
            'strSalt was created above and strUserPswd is what the user keyed in
 
            Try
                conn.Open()
 
                Insertcomm.ExecuteNonQuery()
 
            Catch ex As Exception
                ex.Message.ToString()
 
            Finally
                conn.Close()
            End Try
        End If
 
    End Sub

Open in new window

Avatar of guru_sami
guru_sami
Flag of United States of America image

From what I understood here is what you should do upon pssalogin_click:
1: Get Salt from DB for txtUsername.Text username
2: Hash txtPassword.Text using Salt you get from step 1.
3: Now call SP LoginData passing the username and hashedPassword from step 2.

I am not sure if thats what your were looking for.
To verify the login do the following:

(See Function Below)

1.  Read the user info from the database.
2.  Take the password just keked in anad concatenate it to the Salt
3.  Hash the concatenated result
4.  Compare it to the encrypted password stored in the database.

The function in the code snippet was derived from one I use.  It may not work as it hasn't been tested as written, but it should give you a good idea of what needs to be done.
Forgot to attach code snippet...Here it is.
Function VerifyPassword(ByVal vstrUserName As String, ByVal vstrPassword as String) As Integer
 
        Dim bolPasswordMatch As Boolean = False
        Dim strDbPasswordHash As String
        Dim strDbSalt As String
        Dim ObjUserMaster as New UserMaster(vstrUserName)
 
'Get the user's login info from the database using your preferred method (I use a UserMaster Class)
 
    Try
        With objUserMaster
                strDbPasswordHash = .WebPin
                strDbSalt = .WebPinSalt
                If .NbrOfAttempts > 3 Then
                    .AccountLocked = True
                    .UpdateRecord()
                    'They've used up all their login chances, so inform them (by returning -4) and lock their account
                    Return -4
                End If
        End With
        
        ' Now take the salt and the password entered by the user
        ' and concatenate them together.
        Dim strPasswordAndSalt As String = String.Concat(vstrPassword, strDbSalt)
 
        ' Now hash them
        Dim strHashedPasswordAndSalt As String = FormsAuthentication.HashPasswordForStoringInConfigFile(strPasswordAndSalt, "SHA1")
 
        ' Now verify them.
        bolPasswordMatch = strHashedPasswordAndSalt.Equals(strDbPasswordHash)
        If bolPasswordMatch = True Then
            With objUserMaster
                .NbrOfAttempts = 0
                .UpdateRecord()
            End With
            Return 0
        Else
            With objUserMaster
                .NbrOfAttempts += 1
                .UpdateRecord()
            End With
            Return -1
        End If
    Catch ex As Exception
        mstrDisplayMsg = ex.ToString
    Finally
        objUserMaster.Dispose
        System.GC.Collect()
    End Try
End Function

Open in new window

Avatar of Brian

ASKER

is there any way i could have a little more help :(

this is the first time dealing with hash and salt and to be honest i don't know where to begin with the following code above.
Avatar of Brian

ASKER

can i have help filling in the pieces from what i'm use to compared to the function that you supplied?

Protected Sub btnAuthenticate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAuthenticate.Click

        Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)

        Dim comm As SqlCommand

        comm = New SqlCommand("PSSA_LoginData", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure

        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text
        comm.Parameters.Add("@passwordhash", SqlDbType.VarChar, 128)
        comm.Parameters("@passwordhash").Value = txtPassword.Text

        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then
                FormsAuthentication.RedirectFromLoginPage(txtUsername.Text, True)
            Else
                lblError.Text = "your login credentials are invalid!!!"
            End If
            myreader.Close()
        Catch
            lblError.Text = "the pssa database is currently offline."
        Finally
            conn.Close()
        End Try

    End Sub


STORED PROCEDURE THAT I'M USING WITH CODE ABOVE:

ALTER PROCEDURE PSSA_LoginData

@username varchar(50),
@password varchar(128)

AS

SELECT [password ] FROM [PSSA_USERS] WHERE (([username ] = @username ) AND ([password ] = @password ))
I think you are missing one major point the point. i.e. you should store hashedPassword and Salt in the DB as well.
When you Create the user account ... you do the following:
1: Call gfCreateSalt function and get the Salt
2: Call gfCreatePasswordHash and get hashedPwd
3: Store username,hashedPwd(step 2) and Salt(step 1) in the DB.

Upon login....in your btnAuthenticate_Click:
1: Get Salt and HashedPwd from DB calling call SP PSSA_LoginData
2: Hash txtPassword.Text using Salt you get from step 1 by calling gfCreatePasswordHash .
3: Now compare HashedPwd that was returned by PSSA_LoginData and the hashedPwd you get from step 2.

Note: You will have to modify your PSSA_LoginData stored Proc to take only "username" and return Salt and HashedPwd(that you stored during User account creation).

Hope that is a better explanation ...Sorry I don't have code right now.
Avatar of Brian

ASKER

Hi guru_sami,

yes hashedPassword and Salt is and has been stored in the DB.

Steps 1,2, and 3 above when creating the user account have been completed and everything works on the user creation end.

>> upon login, this is where i'm having the problems at :(

is the following SP correct that i have below

CREATE PROCEDURE PSSA_LoginData

@username varchar(50),
@passwordhash varchar(128),
@passwordsalt varchar(128)

AS

SELECT [username] FROM [EmpSalt] WHERE (([username] = @username) AND ([passwordhash] = @passwordhash))
Is the username,passwordhash and passwordsalt stored in same table?

Looks your storedProc needs some changes...
Your SP should take username as input parameter and return salt and passwordhash for that user.

The idea is you get Salt from the DB ...then Hash the password that User entered using this Salt.
So now you have hashedpwd(for what user entered) and passwordhash that was initally stored for the user when the account was created.

Now you compare that hashedpwd with the "passwordhash" that your storedProc returned.

Hope that helps.
Avatar of Brian

ASKER

>> Is the username,passwordhash and passwordsalt stored in same table?
Yes, all three fields are in the same table

I think i understand how i need the SP to look, is the following SP correct now?

@username varchar(50)

AS

SELECT username, passwordhash, passwordsalt FROM EmpSalt WHERE username = @username
Yep thats what I am talking about....
Now follow step 2 and 3 as in my previous post.
Avatar of Brian

ASKER

ok, step 1 down and 2 to go :)

I modified the btnAuthenticate Code below, can you check to see if i accomplished either Step 2 or 3? This is where things are getting crazy and above me :)

I added the Function Code below:

Public Function gfCreateSalt(ByVal intSize As Integer) As String
        ' Generate a cryptographic random number using the cryptographic
        ' service provider
        Dim rng As RNGCryptoServiceProvider = New RNGCryptoServiceProvider
        Dim buff() As Byte = New Byte(intSize) {}
        rng.GetBytes(buff)
        ' Return a Base64 string representation of the random number
        Return Convert.ToBase64String(buff)
    End Function




Protected Sub btnAddUser_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddUser.Click
        If Page.IsValid Then

            Dim strSalt As String

            Dim conn As SqlConnection
            Dim Insertcomm As SqlCommand

            Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString

            conn = New SqlConnection(connectionString)

            Insertcomm = New SqlCommand("SaltInsert", conn)
            Insertcomm.CommandType = CommandType.StoredProcedure

            Insertcomm.Parameters.Add("@name", System.Data.SqlDbType.VarChar, 50)
            Insertcomm.Parameters("@name").Value = txtName.Text

            Insertcomm.Parameters.Add("@email", System.Data.SqlDbType.VarChar, 100)
            Insertcomm.Parameters("@email").Value = txtEmail.Text

            Insertcomm.Parameters.Add("@username", System.Data.SqlDbType.VarChar, 50)
            Insertcomm.Parameters("@username").Value = txtUsername.Text

            strSalt = gfCreateSalt(5)   'This creates a 5-digit salt
            Insertcomm.Parameters.Add("@passwordsalt", System.Data.SqlDbType.VarChar, 128)
            Insertcomm.Parameters("@passwordsalt").Value = strSalt

            Insertcomm.Parameters.Add("@passwordhash", System.Data.SqlDbType.VarChar, 128)
            Insertcomm.Parameters("@passwordhash").Value = gfCreatePasswordHash(txtPassword.Text, strSalt)
            'strSalt was created above and strUserPswd is what the user keyed in

            Try
                conn.Open()

                Insertcomm.ExecuteNonQuery()

            Catch ex As Exception
                ex.Message.ToString()

            Finally
                conn.Close()
            End Try
        End If

    End Sub
Ok that is Step 1..creating user...
I do not see anything about Login ..
Avatar of Brian

ASKER

LOL, sorry please see my btnAuthenticate Code below. Now, the strSalt is not declared because i was not sure how to declare and use it below.

Protected Sub btnAuthenticate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAuthenticate.Click

        Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)

        Dim comm As SqlCommand

        comm = New SqlCommand("PSSA_LoginData", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure

        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text
        comm.Parameters.Add("@passwordhash", SqlDbType.VarChar, 128)
        comm.Parameters("@passwordhash").Value = gfCreatePasswordHash(txtPassword.Text, passwordsalt)

        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then
                FormsAuthentication.RedirectFromLoginPage(txtUsername.Text, True)
            Else
                lblError.Text = "your login credentials are invalid!!!"
            End If
            myreader.Close()
        Catch
            lblError.Text = "the pssa database is currently offline."
        Finally
            conn.Close()
        End Try

    End Sub
ASKER CERTIFIED SOLUTION
Avatar of guru_sami
guru_sami
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Brian

ASKER

ok, let me know if this looks okay now. I now recieve a different error before running which is listed below.

ERROR MESSAGE:

Variable 'strSaltFromDB' is used before it has been assigned  a value. A null reference exception could result at runtime.

Protected Sub btnAuthenticate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAuthenticate.Click

        Dim strSaltFromDB As String
        Dim hashPwdInDB As String
        Dim hashPwdEnterd As String

        Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)

        Dim comm As SqlCommand

        comm = New SqlCommand("PSSA_LoginData", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure

        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text
        comm.Parameters.Add("@passwordhash", SqlDbType.VarChar, 128)
        comm.Parameters("@passwordhash").Value = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)

        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then

                hashPwdInDB = myreader.GetString("passwordhash")
                strSaltFromDB = myreader.GetString("passwordsalt")

                hashPwdEnterd = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)
                If hashPwdEnterd = hashPwdInDB Then
                    FormsAuthentication.RedirectFromLoginPage(txtUsername.Text, True)

                Else
                    lblError.Text = "your login credentials are invalid!!!"
                End If
            End If
            myreader.Close()
        Catch
            lblError.Text = "the pssa database is currently offline."
        Finally
            conn.Close()
        End Try

    End Sub
Did you see my code does not have this:

    comm.Parameters.Add("@passwordhash", SqlDbType.VarChar, 128)
        comm.Parameters("@passwordhash").Value = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)

there is no need to pass passwordhash to your StoreProc as it just take username as input parameter.

So remove those two lines shown above.
Also you change this which might throw exception:
                hashPwdInDB = myreader.GetString("passwordhash")
                strSaltFromDB = myreader.GetString("passwordsalt")
GetString() takes int as input and not String...
So look at my code and use that first and see if it works.

I think this is the other way:
hashPwdInDB = myreader("passwordhash").ToString
strSaltFromDB = myreader("passwordsalt").ToString
Sorry my explanation was so confusing.  I tend to have all my sql and vb code in classes and common code pages.  That way my aspx.vb pages aren't so cluttered and can take care of just what's on the web page.
Avatar of Brian

ASKER

THANK YOU VERY MUCH FOR ALL YOUR HELP!!! That worked out GREAT!!

you where right i forgot to remove the following line below and once i did that and applied your changes everything worked.

Thanks again!!