How to validate salted and hashed password in c#

17,109

Solution 1

Create an column in your user table Username and Hash and Salt

User Register

1) Take input username or password from user in your registration form.

2) Create Hash and Salt for input password with below method.

public class HashSalt
{
    public string Hash { get; set; }
    public string Salt { get; set; }
}

public static HashSalt GenerateSaltedHash(int size, string password)
{
    var saltBytes = new byte[size];
    var provider = new RNGCryptoServiceProvider();
    provider.GetNonZeroBytes(saltBytes);
    var salt = Convert.ToBase64String(saltBytes);

    var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, saltBytes, 10000);
    var hashPassword = Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256));

    HashSalt hashSalt = new HashSalt { Hash = hashPassword, Salt = salt };
    return hashSalt;
}

Rfc2898DeriveBytes class is used to generate the hash using the RFC2898 specification, which uses a method known as PBKDF2 (Password Based Key Derivation Function #2) and is currently recommend by the IETF (Internet Engineering Task Force) for new applications.

3) Then stored this Hash and Salt with user record in database.

public void Submit1_click(object sender, EventArgs r)
{
    //Your code here

    HashSalt hashSalt = GenerateSaltedHash(64, password1.Text);

    //Your code here

    cmd.Parameters.AddWithValue("@hash", hashSalt.Hash);
    cmd.Parameters.AddWithValue("@salt", hashSalt.Salt);

    //You code here
}

User Login

1) Take input username or password from user in your login form.

2) In Login_click get user by username from database.

3) Pass stored Hash and Salt to below function.

public static bool VerifyPassword(string enteredPassword, string storedHash, string storedSalt)
{
    var saltBytes = Convert.FromBase64String(storedSalt);
    var rfc2898DeriveBytes = new Rfc2898DeriveBytes(enteredPassword, saltBytes, 10000);
    return Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256)) == storedHash;
}

4) Then login your user by verifying his/her password.

public void Login_click(object sender, EventArgs r)
{
    //You code here

    User user = GetUserByUsername(txtUsername.Text);

    bool isPasswordMatched = VerifyPassword(txtpassword.Text, user.Hash, user.Salt);

    if (isPasswordMatched)
    {
        //Login Successfull
    }
    else
    {
        //Login Failed
    }

    //Your code here
}

Reference: Effective Password Hashing

Solution 2

You have to store the salt before adding it to the password and hashing it.

So when someome tries to login you concatenate the password with the stored salt and then you can hash it and compare it to the existing hash in base.

So your user table should have at least these 3 columns : username, hashedpassword, salt

longer explanation : The hashing function is deterministic but not reversable so when the user create his password for the first time :

  • you generate a salt
  • you concatenate the password and the salt
  • you hash the resulting string of the concatenation of password and salt
  • you save both the hash and the salt

so you have : hashedpassword = hashingfunction(password+salt)

When you try to login :

  • you get the login and password as input
  • with the login you can retrieve the hash and the salt in base
  • you already have the hashingfunction
  • so you can process hashingfunction(password_entered_by_user+salt)
  • you compare the result with hashedpassword and log the user if it's the same

Having the salt next to the hashed password doesnt make it less secure : the goal of the salt is to prevent the use of rainbowtable

If someone breaks into your database he will probably aim for the email+password of your users due to the fact most people reuse the same password and email in a lot of different places.

Now since the hash function is not reversable, the only thing an attacker can do is trying to guess the password and creating a dictionnary with :

guessed_password => hash(guessed_password)

ex :

pet345 => 23FD7890F0F3FA3AE468F37CB900402A1F1977CF926F3452CA519056E16985AB
lola78 => 876B42DC10A0822CC52B894DC7517C784A542B43FB033B4A93635ADA67946B2E
lola79 => A7DCAF5195463FA367CDEA6F23688C1280EC98F4AF2B08BC3469D2496537D48D
lola80 => 8D8E1CF212F5DDC3CA1D510900382DF945625A9AE1584CE0D539B2C4D73717CB

if hash(guessed_password) is in your database, he knows the password for this (these) user is guessed_password.

He can generate a dictionnary with billions of guessed_passwords and since a lot of users do not use really strong password, it's very likely that he will be able to find a good number of your users hash in his dictionnary. So if he generates hashes for 'lola80' and 'lola79' and these are the passwords of 2 of your users he know knows it.

Now if you add a random salt to every password entered, for each salt he has to generate a complete dictionary since he has to do :

guessed_password + salt = hash(guessed_password + salt)

For user A with salt '09ç@p$' he has to generate a full dictionnary where every word ends by '09ç@p$'

Now if he wants to guess the password of user B associated with salt 'Yuè45gh' he has to generate another dictionnary where every word ends by 'Yuè45gh'

Basically it slows down the process of guessing your users passwords by a your_number_of_users factor.

Share:
17,109
prkash
Author by

prkash

Updated on June 28, 2022

Comments

  • prkash
    prkash almost 2 years

    I used the below method to salt and hash the passwords

    public string CreateSalt(int size)
    {
        var rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
        var buff = new byte[size];
        rng.GetBytes(buff);
        return Convert.ToBase64String(buff);
    }
    public string GenerateSHA256Hash(String input, String salt)
    {
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input + salt);
        System.Security.Cryptography.SHA256Managed sha256hashstring =
            new System.Security.Cryptography.SHA256Managed();
        byte[] hash = sha256hashstring.ComputeHash(bytes);
        return Convert.ToBase64String(hash);
    }
    public void Submit1_click(object sender, EventArgs r)
    {
    
        try
        {
            String salt = CreateSalt(10);
            String hashedpassword = GenerateSHA256Hash(password1.Text, salt);
            string MyConString = "SERVER=localhost;DATABASE=mydb;UID=root;PASSWORD=abc123;";
            MySqlConnection connection = new MySqlConnection(MyConString);
            string cmdText = "INSERT INTO authentication(agentlogin ,password ,question ,answer)VALUES ( @login, @pwd, @question, @answer)";
            MySqlCommand cmd = new MySqlCommand(cmdText, connection);
            cmd.Parameters.AddWithValue("@login", labeluname.Text);
            cmd.Parameters.AddWithValue("@pwd", hashedpassword);
            cmd.Parameters.AddWithValue("@question", ddlquestion.Text);
            cmd.Parameters.AddWithValue("@answer", txtanswer.Text);
            connection.Open();
            int result = cmd.ExecuteNonQuery();
            connection.Close();
            lblmsg.Text = "Registered succesfully";
            lblmsg.ForeColor = System.Drawing.Color.Green;
            Response.Redirect("index.aspx");
        }
        catch (Exception)
        {
            Console.Write("not entered");
            lblmsg.Text = "Registration failed!";
            lblmsg.ForeColor = System.Drawing.Color.Red;
            Response.Redirect("index.aspx");
        }
    }
    

    So I get the fully encrypted password from the above, but now I can't login using the passwords that was entered in there. How can I unsalt the password when logging in? I figured I can just use the same method used to encrypt to unhash it but the salting doesn't return the same values. The below is the code on validation page

        public string GenerateSHA256Hash(String input)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input);
            System.Security.Cryptography.SHA256Managed sha256hashstring =
                new System.Security.Cryptography.SHA256Managed();
            byte[] hash = sha256hashstring.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }
    
        public void Login_click(object sender, EventArgs r)
        {
            String hashedpassword = GenerateSHA256Hash(txtpassword.Text);
            string MyConString = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
            MySqlConnection con = new MySqlConnection(MyConString);
            MySqlCommand cmd = new MySqlCommand("select * from authentication where agentlogin=@username and password=@word", con);
            cmd.Parameters.AddWithValue("@username", txtusername.Text);
            cmd.Parameters.AddWithValue("@word", hashedpassword);
            MySqlDataAdapter sda = new MySqlDataAdapter(cmd);
            DataTable dt = new DataTable();
            sda.Fill(dt);
            con.Open();
            int i = cmd.ExecuteNonQuery();
            con.Close();
            if (dt.Rows.Count > 0)
            {
                Session["id"] = txtusername.Text;
                Response.Redirect("calendar.aspx");
                Session.RemoveAll();
            }
            else
            {
                lblmsg.Text = "Credential doesn't match!";
                lblmsg.ForeColor = System.Drawing.Color.Red;
    
            }
    
        }