Question

I've been reading over and over the code again to see where the error is being made but I'm unable to find it. I've copied this code from stackoverflow an never really checked it or understood it perfectly as to fix it. I'm receiving passwords from a webservice, hashing, salting and saving it to a SqlServer 2008. The variables on the SqlServer are declared as mail as nvarchar(64), hash as varbinary(128) and salt as varbinary(128). The passwords are being saved but when I try to check if the password are correct the method always returns false. This are my methods.

public int InsertData(string mail,string Password)
    {

        int lineas;
        UserData usuario = HashPassword(Password);
        using (SqlConnection connection = new SqlConnection(Connection))
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandText = "INSERT INTO Usuarios (Mail,Hash,Salt) VALUES (@mail,@hash,@salt)";

            command.Parameters.AddWithValue("@mail", mail);
            command.Parameters.AddWithValue("@hash", usuario.Password);
            command.Parameters.AddWithValue("@salt", usuario.salt);


            connection.Open();
            lineas=command.ExecuteNonQuery();
        }
        usuario = null;
        return lineas;
    }



private UserData HashPassword(string Password)
    {
        //This method hashes the user password and saves it into the object UserData
        using (var deriveBytes = new Rfc2898DeriveBytes(Password, 20))
        {
            byte[] salt = deriveBytes.Salt;
            byte[] key = deriveBytes.GetBytes(20);  // derive a 20-byte key
            UserData usuario = new UserData();
            usuario.Password = key;
            usuario.salt = salt;
            return usuario;

        }


    }

And the next method is the one I use to validate de password, it always returns false

private bool CheckPassword(string Password, byte[] hash, byte[] salt)
    {


        // load salt and key from database

        using (var deriveBytes = new Rfc2898DeriveBytes(Password, salt))
        {
            byte[] newKey = deriveBytes.GetBytes(20);  // derive a 20-byte key

            if (!newKey.SequenceEqual(hash))
                return false;

            else
                return true;

        }
    }

This method receives the login info

 public bool ValidateLogIn(string mail, string Password)
    {



        using (SqlConnection connection = new SqlConnection(Connection))
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandText = "Select * from Usuarios where Mail=@mail";
            command.Parameters.AddWithValue("@mail",mail);
            connection.Open();
            using (SqlDataReader reader = command.ExecuteReader())
            {
                reader.Read();
                byte[] hash = (byte[])reader["Hash"];
                byte[] salt = (byte[])reader["Salt"];
                if(CheckPassword(Password,hash,salt))
                {
                    /
                    UpdateData(mail, Password);
                    return true;
                }
                else
                {
                    return false;
                }

            }

        }

    }

Any ideas what could be wrong?

EDIT: I've found the link where I got the hashing code https://stackoverflow.com/a/4330586/1861617

Was it helpful?

Solution 2

After checking with deroby's answer and still not getting luck. I rechecked and realized 20 bytes are 120 bits so the varbinary wasn't able to store the whole salt. After increasing it to 256 it worked.

OTHER TIPS

Using your code in a test-project (Windows Forms with textbox+button+label) I added this:

   internal class UserData
    {
        public byte[] Password { get; set; }
        public byte[] Salt { get; set; }
    }

    public string Connection { get; set; }

    private void UpdateData(string mail, string password)
    {
        // not a clue what to do here....
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var password = textBox1.Text;
        var u = HashPassword(password);

        var b = new SqlConnectionStringBuilder {DataSource = "127.0.0.1", IntegratedSecurity = true};
        Connection = b.ConnectionString;

        InsertData("test@domain.com", password);

        label1.Text = string.Format("Using direct check: {0}\nVia the database: {1}", 
            CheckPassword(password, u.Password, u.Salt),
            ValidateLogIn("test@domain.com", password));
    }

And it returns true;true without any problems. (VS2010, .Net4 CP, SQL2008R2)

On the database I used this:

CREATE TABLE tempdb..t_hash 
    (
        Mail nvarchar(64) NOT NULL PRIMARY KEY (Mail), 
        Hash varbinary(128), 
        Salt varbinary(128)
     )

My best guess is that your definition for the UserData clas is 'flawed' ?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top