Я пытаюсь создать и протестировать свой API для входа в систему с помощью С#. Ниже приведена основная часть кода:
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);
}
}
Моя база данных - это Mysql 8 с форматом CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci. Значения сохраняются как varchar(512), так что длина не является проблемой при сохранении.
Код для части БД для сохранения данных:
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);
Код для получения данных из БД для проверки пароля:
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();
Однако, когда я сопоставляю пароль, он всегда не совпадает, не могли бы вы помочь мне, где я делаю неправильно, это в кодировке и т. д.
VerifyPasswordHash(request.Password, user.PasswordHash, user.PasswordSalt)
изучаю код из руководство, но учебник связан с извлечением БД, поэтому хочу знать, делаю ли я что-то неправильно в процессе, чтобы он не совпадал.
Я думаю, вам нужно преобразовать обратно хэш и соль, используя 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();
Это неправильный подход к хранению хэшей паролей, они могут быть вычислены слишком быстро (несколько гигабайт SHA512 в секунду) на обычном оборудовании. Таким образом, вам нужна функция хэширования пароля, такая как BCrypt или Argon2, у которых есть фактор стоимости, чтобы контролировать необходимое время для одного вычисления.
Библиотека BCrypt https://www.nuget.org/packages/BCrypt.Net-Next/ — хороший выбор. Он также включает/извлекает соль в результирующую хэш-строку пароля, поэтому вам нужно только одно поле базы данных для хранения хэша.
спасибо, я перешел на пакет Bcrypt.net-next. Решение @JeolCrypto сработало для меня с существующим кодом.
Вы можете использовать bcrypt с этим кодом! В настоящее время Bcrypt является широко используемым решением для хеширования и намного лучше, чем MD5. Обязательно используйте функцию проверки, нечувствительную к атакам по времени: en.m.wikipedia.org/wiki/Временная_атака
Изменен код, изменено всего 3-4 строчки, и читается действительно чище. Удалены вспомогательные функции, такие как createhash и verifypassword. Спасибо.
@surpavan - Рад слышать, молодец!
Нашел причину как в stackoverflow.com/a/3866449/610928 & stackoverflow.com/a/19859753/610928