locked
System.Web.Helpers Crypto HashPassword RRS feed

  • Question

  • User1608082067 posted

    Hello,

    I have been looking at the new Crypto HashPassword and VerifyMethod method in System.Web.Helpers.

    Both methods use a Salt but they do not return the Salt only the Hash.

    Don't I need the Salt to store it in the database? Am I missing something?

    Thank you,

    Miguel

    Tuesday, September 11, 2012 3:26 PM

Answers

  • User1779161005 posted

    Yea, ignore that -- it's an internal salt for the Rfc2898DeriveBytes and it's derived from the password so it's not a true salt in the context of storing passwords since it's consistently re-created for the same password. You will want to still do your own salt at the password string level. So something like this:

    public void CreateAccount(string username, string password)
    {
        var salt = Crypto.GenerateSalt();
        var saltedPassword = password + salt;
        var hashedPassword = Crypto.HashPassword(saltedPassword);
        CreateAccount(username, salt, hashedPassword);
    }
            
    public void Verify(string username, string password)
    {
        var salt = GetSaltForUserFromDatabase(username);
        var hashedPassword = GetHashedPasswordForUserFromDatabase(username);
        var saltedPassword = password + salt;
        if (Crypto.VerifyHashedPassword(saltedPassword, password))
        {
            // valid password for this username
        }
    }
    
    



    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 11, 2012 3:50 PM

All replies

  • User1779161005 posted

    You'd generate the salt when the user creates the account, and then you'd append it to the password before you call HashPassword. You'd then store that salt and the hashed password in your DB. When you validate credentials just read the salt from the DB and appends it to the password from the login before calling VerifyHashedPassword.

    Tuesday, September 11, 2012 3:34 PM
  • User1608082067 posted

    Not sure if I understood it ... Because in HashPassword a Salt is generated inside the method:

            public static string HashPassword(string password)
            {
                if (password == null)
                {
                    throw new ArgumentNullException("password");
                }
    
                // Produce a version 0 (see comment above) password hash.
                byte[] salt;
                byte[] subkey;
                using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
                {
                    salt = deriveBytes.Salt;
                    subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
                }
    
                byte[] outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
                Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
                Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
                return Convert.ToBase64String(outputBytes);
            }

    This is what I don't understand ...

    Tuesday, September 11, 2012 3:37 PM
  • User1779161005 posted

    Yea, ignore that -- it's an internal salt for the Rfc2898DeriveBytes and it's derived from the password so it's not a true salt in the context of storing passwords since it's consistently re-created for the same password. You will want to still do your own salt at the password string level. So something like this:

    public void CreateAccount(string username, string password)
    {
        var salt = Crypto.GenerateSalt();
        var saltedPassword = password + salt;
        var hashedPassword = Crypto.HashPassword(saltedPassword);
        CreateAccount(username, salt, hashedPassword);
    }
            
    public void Verify(string username, string password)
    {
        var salt = GetSaltForUserFromDatabase(username);
        var hashedPassword = GetHashedPasswordForUserFromDatabase(username);
        var saltedPassword = password + salt;
        if (Crypto.VerifyHashedPassword(saltedPassword, password))
        {
            // valid password for this username
        }
    }
    
    



    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 11, 2012 3:50 PM
  • User1608082067 posted

    Thank You for the help,

    Miguel

    Tuesday, September 11, 2012 7:29 PM
  • User1806986182 posted

    Hi there,

    I am using the snippet of code you posted, but I am getting the following error

     

    "Invalid length for a Base-64 char array or string."

     

    The combination of the salt and the user password is 35 characters, which I presume is the issue. How did this code work in the first place then?

     

    Can you advise please?

     

    Thanks alot

     

    UPDATE:

     The code in the answer is slightly wrong, you need to following paramters:

     

    VerifyHashedPassword(hashedPassword, saltedPassword);

    Friday, January 11, 2013 10:47 AM
  • User1472342445 posted

    I use Rfc2898DeriveBytes with my own random salt.

    This is the function that generates the hashed password

    /// <summary>
    /// Compute the PBKDF2 hash part of the password
    /// </summary>
    /// <param name="password">Password to hash</param>
    /// <param name="salt">The salt</param>
    /// <param name="iterations">Number of iterations</param>
    /// <param name="length">Length of the hash to generate in bytes</param>
    /// <returns>Hashed password</returns>
    private static byte[] PBKDF2(string password, byte[] salt, int iterations, int length)
    {
        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
        pbkdf2.IterationCount = iterations;
        return pbkdf2.GetBytes(length);
    }
    

    and the sub-routine that calls the the above function uses RNGCryptoServiceProvider to generate the salt.

    // Generate random salt
    RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
    byte[] salt = new byte[SaltBytes];
    rnd.GetBytes(salt);
    
    // Generate Hash
    byte[] hash = PBKDF2(password, salt, iterations, HashBytes);
    

    For the iterations I get a random number between 1000 and 4095.
    You will have your salt, the number of iterations to perform and the hashed password to store in your database.

     



     

    Friday, January 11, 2013 11:10 AM
  • User601318186 posted

    Thank Q 

    for ur Update its help me

    public void CreateAccount(string password)
    {
    var salt = Crypto.GenerateSalt();
    var saltedPassword = password + salt;
    var hashedPassword = Crypto.HashPassword(saltedPassword);
    Session["salt1"] = salt;// Just remove session and store in db
    Session["PH"] = hashedPassword;// Just remove session and store in db

    }

    public void Verify(string password)
    {
    string s=Session["salt1"].ToString();//Remove session and  Retrive from db
    string h=Session["PH"].ToString();//Remove session and  Retrive from db
    var salt = s;
    var hashedPassword = h;
    var saltedPassword = password + salt;
    if (Crypto.VerifyHashedPassword(hashedPassword, saltedPassword))
    {
    // valid password for this username
    }
    }


    Wednesday, November 13, 2013 6:41 AM
  • User1779161005 posted

    @thakur86 -- there's no need to manually salt the password -- that is already being done by the Crypto.HashPassword API. See this post:

    http://brockallen.com/2012/10/19/password-management-made-easy-in-asp-net-with-the-crypto-api/

    Wednesday, November 13, 2013 11:58 AM