none
password hashing issue

    Question

  • I have problem aftering hashed a password, I can't get the same hashed password using the salt.

    in other way saying, creating user is ok, but login keep failed. the hashed password just not match.

    this my code:

            const int DefaultSaltSize = 128;
            const int DefaultHashSize = 128;
            const int DefaultHashIteration = 1003;
    
            private PasswordManager() { }
    
            public PasswordModel HashPassword(string password, string salt = null)
            {
                bool saltIsNotNull = (salt != null);
    
                Rfc2898DeriveBytes h = new Rfc2898DeriveBytes(password, DefaultSaltSize, DefaultHashIteration);
                h.Salt = saltIsNotNull ? Encoding.Unicode.GetBytes(salt) : GenSalt();
                //string c = Encoding.Unicode.GetString(h.Salt);
                byte[] rslt = h.GetBytes(DefaultHashSize);
    
                PasswordModel ret = new PasswordModel
                {
                    Salt = saltIsNotNull ? salt : Encoding.Unicode.GetString(h.Salt),
                    Password = Encoding.Unicode.GetString(rslt),
                };
                //var x = (c == ret.Salt);
                //if (!saltIsNotNull)
                //{
                //}
                return ret;
            }
    
            internal byte[] GenSalt()
            {
                RNGCryptoServiceProvider p = new RNGCryptoServiceProvider();
    
                byte[] b = new byte[DefaultSaltSize];
                p.GetBytes(b);
                return b;
            }
        }
    
        internal class PasswordModel
        {
            public string Password { get; set; }
            public string Salt { get; set; }
        }

    the function is quite simple, if not salt provided, typically during creation, it will gen a salt.

    then I will save the hashed password and salt.

    when doing login, I pass in the saved salt to the function, but the hashed password is different, hence login is always failed. unless you manually write the new hashed password in the db.

    also, when I check the length of these: byte[] = 128 and string = 64. shouldn't string be 128?

    is it due to Encoding.Unicode.GetString?


    • Edited by Kelmen Monday, June 17, 2013 9:09 AM
    Monday, June 17, 2013 9:06 AM

Answers

  • "is it due to Encoding.Unicode.GetString?"

    Yes, you're use of text encoding is incorrect. Once when you convert the randomly generated salt to a string and once when you convert the password hash to a string. You can't expect to give Encoding.GetString some random bytes and get a "valid" string out of it, some of those bytes might there well be invalid in the given encoding.

    Store the password hash and salt as binary. If you find strings easier to use then convert the bytes to strings by using base64 encoding, see Convert.To/FromBase64 methods: http://msdn.microsoft.com/en-us/library/system.convert.tobase64string.aspx

    • Marked as answer by Kelmen Wednesday, June 19, 2013 9:23 AM
    Wednesday, June 19, 2013 4:04 AM
    Moderator

All replies

  • Hi Kelmen,

    Welcome to the MSDN Forum.

    >>I pass in the saved salt to the function, but the hashed password is different, 

    Based on this, it seems that the same function generate two different hashedpassword with the same password string and same salt.

    Where do you store the hashpassword?

    If you save it to a file, the issue should be related to the file encoding. since your code has specified with unicode.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, June 18, 2013 2:56 AM
    Moderator
  • saved it into db, as nvarchar(128)

    I believe it's nothing to do with persistency.

    as I put in code/testing at immediate mode interactive debug, right after I gen a random salt then hashed the password, I reuse the salt pass to the function, the returned hashed password is different.

    and this testing has nothing/not even over the saving part.

    Wednesday, June 19, 2013 2:15 AM
  • "is it due to Encoding.Unicode.GetString?"

    Yes, you're use of text encoding is incorrect. Once when you convert the randomly generated salt to a string and once when you convert the password hash to a string. You can't expect to give Encoding.GetString some random bytes and get a "valid" string out of it, some of those bytes might there well be invalid in the given encoding.

    Store the password hash and salt as binary. If you find strings easier to use then convert the bytes to strings by using base64 encoding, see Convert.To/FromBase64 methods: http://msdn.microsoft.com/en-us/library/system.convert.tobase64string.aspx

    • Marked as answer by Kelmen Wednesday, June 19, 2013 9:23 AM
    Wednesday, June 19, 2013 4:04 AM
    Moderator
  • thx. it resolved my issue.

    initially i'm using Convert.*Base64String(), but the generated string size seems incorrect to me.

    using Encoding, it give me half the size, but I can understand this, and it fit my column size.

    but just can't get the same hashed result.

    now reverting back to Convert, and increased the column size, everything working as expected.

    Wednesday, June 19, 2013 9:26 AM
  • "initially i'm using Convert.*Base64String(), but the generated string size seems incorrect to me."

    Well, the size of hash refers to the binary form of the hash, the byte array you obtain from the hash function. The textual form of a hash will always be larger. For example if you convert every byte of the hash to a hex number made of 2 digits then the text representation will be 2 times larger.

    base64 is a more compact representation than hex but it is still larger (~33%) than the binary form.

    Wednesday, June 19, 2013 9:33 AM
    Moderator