1

In an ASP.NET application, I have saved a password to the database as "Binary" data using md5.

How can I now compare passwords?

I used the code in this article to encrypt the password with md5

The code is working. How can I compare the password when users enter their password at login? What's the code to check if the password is matched with encrypted password in database.

I used the following code, but it always display "Incorrect username or password" even if it's correct."the modifird code"

Byte[] hashedBytes;
    string Password = txtPassword.Text;
    MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
    UTF8Encoding encoder = new UTF8Encoding();

    hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(Password));
    Byte[] pass = new Byte[16];
    SqlConnection conn = new SqlConnection("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin");

    SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE UserName=@UserName", conn);
    cmd.Parameters.AddWithValue("@UserName", txtUserName.Text);
    conn.Open();
    SqlDataReader rdr = cmd.ExecuteReader();
    if (rdr.Read())
        pass = (Byte[])rdr["password"];

    foreach (Byte b in pass)
    {
        Label1.Text += b.ToString() + " ";



        //Response.Write(b.ToString());

        string UserName = txtUserName.Text;
        bool isMatch = false;
        Byte[] password = new Byte[16];

        SqlConnection con = new SqlConnection("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin");
        con.Open();

        SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where UserName='{0}'", UserName), con);
        cmd.CommandType = CommandType.Text;
        cmd.Parameters.AddWithValue("@UserName", txtUserName.Text);
        SqlDataReader dr = cmdd.ExecuteReader();
        if (dr.Read())
        {
            password = (Byte[])dr["Password"];
        }
        foreach (Byte c in password)
        {
            Label2.Text += c.ToString() + " ";//I didnt close the pracket fo that reason data is repeted if I close it I cant type c.toString



            while (dr.Read())
            {

                if (b.ToString() == c.ToString()) // I mean this statment
                {
                    isMatch = true;
                }
            }
        }




        dr.Close();
        con.Close();

        if (isMatch)
        {
            Response.Write("correct");
        }
        else
        {
            Response.Write("Incorrect username or password!");
        }

    }

the edited code protected void Button1_Click(object sender, EventArgs e) { } public static bool ValidateUser(string userName, string password) { SqlConnection con = new SqlConnection("Data Source=shihab-PC;Initial Catalog=test;User ID=sh;password=admin"); con.Open();

    using (var connection = new SqlConnection("connectionString"))
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT dbo.checkUserExists (@userName, @password)";
        command.Parameters.Add("@userName", SqlDbType.NVarChar, 25).Value = userName;
        command.Parameters.Add("@password", SqlDbType.NVarChar).Value = GenerateHash(password);

        connection.Open();
        return (bool)command.ExecuteScalar();
    }
}

private static string GenerateHash(string value)
{
    return Convert.ToBase64String(new System.Security.Cryptography.HMACSHA1(Encoding.UTF8.GetBytes("salt")).ComputeHash(Encoding.UTF8.GetBytes(value)));
}

}

shihab
  • 11
  • 1
  • 4
  • MD5 does not encrypt; there is no way to decrypt, short of brute force or a weakness of MD5. MD5 is a hash; many different inputs can produce the same hash output. – Steven Sudit Nov 11 '10 at 18:44
  • 1
    A few more things: 1) MD5 is broken; use SHA-1 or (better) SHA-2. 2) Use a different salt with each row. – Steven Sudit Nov 11 '10 at 18:45
  • I dont want to decrypt i just want to compare betwwen the password that users enters and password stored in database – shihab Nov 11 '10 at 20:15
  • You should use a good key derivation function such as PBKDF2 and not play md5/sha-1/sha-2. http://en.wikipedia.org/wiki/Key_derivation_function Implementation of PBKDF2 in .net: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx – CodesInChaos Nov 11 '10 at 22:20

3 Answers3

4

When you compare passwords - you need to compute the MD5 on the password they are submitting.

So in your code you want to something like this:

MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
//create an array of bytes we will use to store the encrypted password
Byte[] hashedBytes;
//Create a UTF8Encoding object we will use to convert our password string to a byte array
UTF8Encoding encoder = new UTF8Encoding();

//encrypt the password and store it in the hashedBytes byte array
hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(txtPassword.Text));

//set the password they are using now to password for the compare:
Password = hashedBytes;

Then after that, you can run your comparison code. The point is that the password in the database is the original hashedBytes you computed when they signed up

EDIT: here it is in your original code:

SqlConnection con = new SqlConnection("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin");
con.Open();
string UserName = txtUserName.Text;
string Password = txtPassword.Text;

//hash password
MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
Byte[] hashedBytes;
UTF8Encoding encoder = new UTF8Encoding();
hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(Password));
Password = hashedBytes.ToString();

bool isMatch = false;
SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where UserName='{0}'", UserName),con);
SqlDataReader dr = cmdd.ExecuteReader();
while (dr.Read())
{
    if (dr["password"].ToString()==Password)  
    {
        isMatch = true;
    }
}
dr.Close();
con.Close(); 
if (isMatch)
{
    Response.Write("correct");
}
else
{
    Response.Write("Incorrect username or password!");
}

Updated the code for the conversion error

Try this code to see the passwords this will print out the password that is hashed and the password in the database - if they mismatch you have a problem (possibly a salt problem)

Byte[] hashedBytes;
string Password = txtPassword.Text;
MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
UTF8Encoding encoder = new UTF8Encoding();
hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(Password));

Response.Write(string.Format("Hashed Password (Given): {0}<br />", hashedBytes.ToString()));

string UserName = txtUserName.Text;
SqlConnection con = new SqlConnection("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin");
con.Open();

SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where UserName='{0}'", UserName),con);

SqlDataReader dr = cmdd.ExecuteReader();

//should be only one row..
while (dr.Read())
{
    Response.Write(string.Format("Hashed Password (DB): {0}", dr["password"].ToString()));
}
dr.Close();
con.Close(); 
Prescott
  • 7,312
  • 5
  • 49
  • 70
  • Can U explain in detail I dont know where to add your code exactlly. – shihab Nov 11 '10 at 19:57
  • error in Line 88: Password = hashedBytes; //Compiler Error Message: CS0029: Cannot implicitly convert type 'byte[]' to 'string' – shihab Nov 12 '10 at 10:00
  • IM tring the problem is this topic(encryption using md5) is anew topic for me also I dont know how to convert string to byte and Vise versa . I tried the updated code but still the same problem is there – shihab Nov 12 '10 at 14:06
  • also I have a quston why we need to convert it to string . Cant we compare byte with byte – shihab Nov 12 '10 at 14:19
  • dr["password"] returns a string, so you have to cast one to the other. – Prescott Nov 12 '10 at 15:12
  • yes its the same Compiler Error Message: CS0030: Cannot convert type 'byte[]' to 'string' – shihab Nov 12 '10 at 15:19
  • alright, I updated it again, the line changed to Password = hashedBytes.ToString(); – Prescott Nov 12 '10 at 15:31
  • no eror found but when I intered acorect user name but wrong password its displyed that is correct that means its check only the user name not the password . so I opened the database and changed the password field from binary to varchar and when I tried it again it disply wrong username or password . so what to do now – shihab Nov 12 '10 at 16:06
  • I also tried and changed this code SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where UserName='{0}'", UserName),con); to SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where Password='{0}'", password),con); the result was incorrect username or password for what ever I type – shihab Nov 12 '10 at 16:17
  • I modfided the code { Response.Write("correct"); Label2.Text = @Password;// I added this code to display the value of password } when I run the page I intered correct username but wrong password but it display "correct" and in the label it display "System.Byte[] " why??whats the error in the code – shihab Nov 13 '10 at 18:33
  • remove all the code you have and add the "try this" code to have it write out the Hashed passwords on the page - they should be the same otherwise it's likely that the hash algorithm used to create the password is different from the hash algorithm you linked me and that which I used. (likely a hash issue) – Prescott Nov 14 '10 at 09:24
  • when I intered a correct user name and correct password the result was "Hashed Password (Given): System.Byte[] Hashed Password (DB): System.Byte[] " Then I intered correct username but wrong password the result was "Hashed Password (Given): System.Byte[] Hashed Password (DB): System.Byte[] " and when I intered wrong username and password the result was "Hashed Password (Given): System.Byte[]" – shihab Nov 14 '10 at 11:34
  • I modified the code to display both hash value (DB and given) but the problem in DB I got repeted data because of for loop so the two hash value will never match this is the code – shihab Nov 15 '10 at 05:05
  • { Byte[] hashedBytes; string Password = txtPassword.Text; MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider(); UTF8Encoding encoder = new UTF8Encoding(); hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(Password)); – shihab Nov 15 '10 at 05:08
  • Byte[] pass = new Byte[16]; SqlConnection conn = new SqlConnection ("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin"); SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE UserName=@UserName", conn); cmd.Parameters.AddWithValue("@UserName", txtUserName.Text); conn.Open(); SqlDataReader rdr = cmd.ExecuteReader(); if (rdr.Read()) pass = (Byte[])rdr["password"]; foreach (Byte b in pass) { Label1.Text += b.ToString() + " "; string UserName = txtUserName.Text; – shihab Nov 15 '10 at 05:10
  • bool isMatch = false; Byte[] password = new Byte[16]; SqlConnection con = new SqlConnection ("Data Source=Shihab-PC;Initial Catalog=test;User ID=sh;password=admin" con.Open(); SqlCommand cmdd = new SqlCommand(string.Format("select * from Users where UserName='{0}'", UserName), con); cmd.CommandType = CommandType.Text; cmd.Parameters.AddWithValue("@UserName", txtUserName.Text); SqlDataReader dr = cmdd.ExecuteReader(); if (dr.Read()) – shihab Nov 15 '10 at 05:11
  • { password = (Byte[])dr["Password"]; } foreach (Byte c in password) { Label2.Text += c.ToString() + " ";//I cant close the braket because later on I cant write b.toString for that reason I got repeted data while (dr.Read()) { if (b.ToString() == c.ToString()) { isMatch = true; } } } – shihab Nov 15 '10 at 05:12
  • dr.Close(); con.Close(); if (isMatch) { Response.Write("correct"); } else { Response.Write("Incorrect username or password!"); } } } – shihab Nov 15 '10 at 05:12
1

This is the code I adjust from your code, it´s working fine(is comparing byte to byte from stored pwd in db with current pwd given by the user):

        public bool AddNewUser(string userId, string pwd, string dept,string mail,string displayName)
    {
        //get the username    
        string UserName = userId;

        SqlConnection conn = new SqlConnection(GetConnectionString());     //sql command to add the user and password to the database    
        SqlCommand cmd = new SqlCommand("INSERT INTO MAIN_USER_DATA(USERID, PWD,DEPARTMENT,MAIL,DISPLAY_NAME) VALUES (@USERID, @PWD,@DEPARTMENT,@MAIL,@DISPLAY_NAME)", conn);    
        cmd.CommandType = CommandType.Text;     //add parameters to our sql query    
        cmd.Parameters.AddWithValue("@USERID", UserName);   
        cmd.Parameters.AddWithValue("@PWD", GenerateHash(userId ,pwd));
        cmd.Parameters.AddWithValue("@DEPARTMENT", dept);
        cmd.Parameters.AddWithValue("@MAIL", mail);
        cmd.Parameters.AddWithValue("@DISPLAY_NAME", displayName);
        using (conn)    {        //open the connection       
            conn.Open();        //send the sql query to insert the data to our Users table 
            try
            {
                cmd.ExecuteNonQuery();
                return true;
            }
            catch
            {
                return false;
            }
        }

    }
    public bool ValidateUser(string userId, string password)
    {
        using (SqlConnection connection = new SqlConnection(GetConnectionString())) 
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandText = "SELECT PWD FROM MAIN_USER_DATA WHERE USERID=@USERID";
            command.Parameters.Add("@USERID", SqlDbType.NVarChar, 25).Value = userId;
            connection.Open();
            SqlDataReader dr = command.ExecuteReader();
            if (dr.Read())
            {
                byte[] storedPwd = (byte[])dr["PWD"];
                byte[] currentPwd = GenerateHash(userId, password);
                for (int i = 0;i< storedPwd.Length; i++)
                {
                    if (storedPwd[i] != currentPwd[i])
                    {
                        return false;
                    }
                }
            }
            else
                return false;
            return true;
        }
    }
    private byte[] GenerateHash(string userId, string password)
    {
        //create the MD5CryptoServiceProvider object we will use to encrypt the password    
        HMACSHA1 hasher = new HMACSHA1(Encoding.UTF8.GetBytes(userId));             //create an array of bytes we will use to store the encrypted password    
        //Byte[] hashedBytes;    //Create a UTF8Encoding object we will use to convert our password string to a byte array    
        UTF8Encoding encoder = new UTF8Encoding();     //encrypt the password and store it in the hashedBytes byte array    
        return hasher.ComputeHash(encoder.GetBytes(password));     //connect to our db 
    }
0

C# code:

public static bool ValidateUser(string userName, string password)
{
    using (var connection = new SqlConnection("connectionString"))
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT dbo.checkUserExists (@userName, @password)";
        command.Parameters.Add("@userName", SqlDbType.NVarChar, 25).Value = userName;
        command.Parameters.Add("@password", SqlDbType.NVarChar).Value = GenerateHash(password);

        connection.Open();
        return (bool)command.ExecuteScalar();
    }
}

private static string GenerateHash(string value)
{
    return Convert.ToBase64String(new System.Security.Cryptography.HMACSHA1(Encoding.UTF8.GetBytes("salt")).ComputeHash(Encoding.UTF8.GetBytes(value)));
}

SQL stored proc:

IF OBJECT_ID(N'checkUserExists', N'FN') IS NOT NULL
DROP FUNCTION checkUserExists
GO
CREATE FUNCTION checkUserExists
(
    @userName NVARCHAR(25),
    @password NVARCHAR(255)
)
RETURNS BIT
AS
BEGIN
    RETURN ISNULL((
        SELECT 1
        FROM users
        WHERE
            name = @userName
        AND password = @password
    ), 0)
END
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • See also http://stackoverflow.com/questions/1300890/md5-hash-with-salt-for-keeping-password-in-db-in-c – abatishchev Nov 14 '10 at 09:36
  • "Compiler Error Message: CS1012: Too many characters in character literal" also the light blue lines are black ,shall I add any statment in the begining – shihab Nov 15 '10 at 04:43
  • @shihab: Have you anyway code like `char x = 'ab'`?? You can't add more then 1 character to type `char`! – abatishchev Nov 15 '10 at 07:31
  • I didnt get your question . I pasted your code and run it the result was "Too many characters in character literal"" – shihab Nov 15 '10 at 10:22
  • Line 95: IF OBJECT_ID(N'checkUserExists', N'FN') IS NOT NULL – shihab Nov 17 '10 at 04:45
  • @shihab: This is the code of SQL STORED PROCEDURE called by the code above – abatishchev Nov 17 '10 at 08:22
  • Where I have to write the SQl prog .shall I write the both codes in same button or what? – shihab Nov 17 '10 at 18:16
  • @shihab: Sorry, missed your last answer. You should use SQL Server Management Studio to deploy new sp. Or just put into C# call not sp itself, but only its inner query. – abatishchev Nov 19 '10 at 14:28
  • i dont know how to put in sql management . I put it in visual studio in the same button still the error is there. what to do and thank for your help – shihab Nov 20 '10 at 07:48
  • @shihab: try `command.CommandText = "SELECT ISNULL((SELECT 1 FROM users WHERE name = @userName AND password = @password ), 0)";`. You need table `users` for that – abatishchev Nov 20 '10 at 09:22
  • the same eror is there (CS1012: Too many characters in character literal ) (Line 95: command.CommandText = "SELECT ISNULL((SELECT 1 FROM users WHERE name = @userName AND password = @password ), 0)";IF OBJECT_ID(N'checkUserExists', N'FN') IS NOT NULL – shihab Nov 20 '10 at 18:04
  • @shihab: Examine my previous post more careful! No mentioning of `IF OBJECT_ID(N'checkUserExists', N'FN') IS NOT NULL` at all! Only the first part. You're inserting SQL code into C# and of course it raises an error! – abatishchev Nov 21 '10 at 07:41
  • @abatishchev: Im confused where to add command.CommandText = "SELECT ISNULL((SELECT 1 FROM users WHERE name = @userName AND password = @password ), 0)"; Shall I add it in c#code or SQL stored proc and where to add it exactully . Another thing is when I write this code "using (var connection = new SqlConnection("connectionString")) "there is error CS0246: The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?) thank you alot for your help – shihab Nov 21 '10 at 15:27
  • @shihab: You can call stored proc from c# code (just deploy it before that) or call SQL query directly (without doing anything with SQL Server). So put code you quoted into your c# app. `var` raises an error because it's c# 3.0 syntax available in VS 2008 and later, if you're using VS 2005, write `SqlConnection connection = new SqlConnection(..)` – abatishchev Nov 21 '10 at 16:56