Проблема с зашифрованным паролем в C# с использованием Rfc2898DeriveBytes и MSSQL
Я сделал систему входа в систему для своего приложения, однако она работает крайне непоследовательно.
Иногда пароль будет работать, но иногда он говорит, что это неверно. Я на 100% уверен, что набираю правильно.
Для хранения паролей я генерирую случайную соль, хеширую пароль с солью и сохраняю хешированный пароль и соль вместе с именем пользователя в базе данных.
Чтобы аутентифицировать пользователя, я выбираю хешированный пароль и соль на основе данного имени пользователя. Затем я хэширую их попытку пароля с помощью соли и проверяю, совпадает ли она с их оригинальным хешированным паролем, что позволяет им войти в систему, если это так.
Мой код выглядит следующим образом:
private const int NumberOfRounds = 5000, SaltLength = 32;
public static byte[] GenerateSalt()
{
using (var rng = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[SaltLength];
rng.GetBytes(randomNumber);
return randomNumber;
}
}
public static byte[] HashPassword(byte[] passwordToHash, byte[] salt)
{
using (var deriveBytes = new Rfc2898DeriveBytes(passwordToHash, salt, numberOfRounds))
{
return deriveBytes.GetBytes(32);
}
}
public static bool IsPasswordValid(string inputPassword, string hashedPassword, string salt)
{
byte[] potentialValidPassword = HashPassword(Encoding.Unicode.GetBytes(inputPassword),
Encoding.Unicode.GetBytes(salt));
string potentialAsString = Encoding.Unicode.GetString(potentialValidPassword);
return Encoding.Unicode.GetBytes(hashedPassword).SequenceEqual(potentialValidPassword) ||
hashedPassword.Equals(potentialAsString);
}
Причина, по которой моя проверка сравнивает и значение байтового массива, и строковое значение, заключается в том, что иногда сравнение байтовых значений не удается, но строковое значение работает.
Мой код для вставки пользователя в базу данных выглядит следующим образом
public SecurityReturnMessage AddUser(string username, string password)
{
byte[] salt = PasswordManagement.GenerateSalt();
byte[] hashedPassword = PasswordManagement.HashPassword(Encoding.Unicode.GetBytes(password), salt);
if (conn.State != ConnectionState.Open)
conn.Open();
int result;
using (IDbCommand comm = conn.CreateCommand())
{
comm.CommandText = "usp_IFRS_SEC_USER_INSERT";
comm.CommandType = CommandType.StoredProcedure;
SqlParameter hashPwd =new SqlParameter("@hashpwd", SqlDbType.NVarChar)
{
Value = Encoding.Unicode.GetString(hashedPassword)
};
SqlParameter saltParameter = new SqlParameter("@salt", SqlDbType.NVarChar)
{
Value = Encoding.Unicode.GetString(salt)
};
comm.Parameters.Add(Extensions.CreateParameter(comm, "@user", username));
comm.Parameters.Add(hashPwd);
comm.Parameters.Add(saltParameter);
var returnVal = comm.CreateParameter();
returnVal.Direction = ParameterDirection.ReturnValue;
comm.Parameters.Add(returnVal);
comm.ExecuteNonQuery();
result = (int)returnVal.Value;
}
if (conn.State != ConnectionState.Closed)
conn.Close();
return (SecurityReturnMessage)result;
}
Если бы кто-нибудь мог помочь мне с этим, я был бы чрезвычайно благодарен.
1 ответ
Я предлагаю вам использовать преобразования Base64, как показано ниже, при преобразовании значений из байтов в строку и наоборот.
string base64 = Convert.ToBase64String(bytes);
byte[] bytes = Convert.FromBase64String(base64);
Таким образом, вы можете быть уверены, что данные Unicode не будут потеряны или повреждены в процессе преобразования, что может быть веской причиной несогласованности вашего кода.