I am trying to create and test my api for login using C#. Below is the major part of the code:
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
private static bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
return computedHash.SequenceEqual(passwordHash);
}
}
My Db is Mysql 8 with CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci format. Values are stored as varchar(512) so that length is not a problem when saving.
The code for the DB part for saving data is:
Cmd.Parameters.Add(new MySqlParameter("@username", MySqlDbType.VarChar)).Value = user.UserName;
Cmd.Parameters.Add(new MySqlParameter("@passwordhash", MySqlDbType.VarChar)).Value = Convert.ToBase64String(user.PasswordHash);
Cmd.Parameters.Add(new MySqlParameter("@passwordsalt", MySqlDbType.VarChar)).Value = Convert.ToBase64String(user.PasswordSalt);
The code for retrieving data from db for password check is:
while (Rdr.Read() == true)
{
user.UserName = Rdr.GetString("username");
user.PasswordHash = System.Text.Encoding.UTF8.GetBytes(Rdr.GetString("passwordhash"));
user.PasswordSalt = System.Text.Encoding.UTF8.GetBytes(Rdr.GetString("passwordsalt"));
user.UserID = Rdr.GetInt16("userid");
}
Rdr.Close();
However when I am doing password match, it is always not matching, could you please help me where I am doing wrong, is it in encoding etc..
VerifyPasswordHash(request.Password, user.PasswordHash, user.PasswordSalt)
learning the code from the tutorial but tutorial is from DB extraction, so want to know if I am doing anything wrong in the process for it to not match.
CodePudding user response:
This is the wrong approach for storing password hashes, it can be calculated way too fast (several Giga SHA512 per second) with common hardware. Thus you need a password-hash function like BCrypt or Argon2 which have a cost factor to control the necessary time for a single calculation.
The BCrypt library https://www.nuget.org/packages/BCrypt.Net-Next/ is a good choice. It also includes/extracts the salt into the resulting password-hash-string, so you need only a single db field to store the hash.
CodePudding user response:
I think you need to convert back hash and salt using Convert.FromBase64String
.
while (Rdr.Read() == true)
{
user.UserName = Rdr.GetString("username");
user.PasswordHash = Convert.FromBase64String(Rdr.GetString("passwordhash"));
user.PasswordSalt = Convert.FromBase64String(Rdr.GetString("passwordsalt"));
user.UserID = Rdr.GetInt16("userid");
}
Rdr.Close();