Хеширование паролей с помощью MD5 или sha-256 С#
Я пишу регистрационную форму для приложения, но все еще имею проблемы с тем, чтобы быть новым для С#.
Я ищу шифрование/хэш-пароли для md5 или sha-256, желательно sha-256.
Хорошие примеры? Я хочу, чтобы он мог получать информацию из "строкового пароля"; а затем хеш его и сохранить в переменной "строка hPassword;". Любые идеи?
Ответы
Ответ 1
Не используйте простой хэш или даже соленый хеш. Используйте какой-то метод укрепления ключа, например bcrypt (с помощью реализации .NET здесь) или PBKDF2 (с встроенной реализацией ).
Вот пример использования PBKDF2.
Чтобы сгенерировать ключ из вашего пароля...
string password = GetPasswordFromUserInput();
// specify that we want to randomly generate a 20-byte salt
using (var deriveBytes = new Rfc2898DeriveBytes(password, 20))
{
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
// save salt and key to database
}
И затем проверить, действительно ли пароль...
string password = GetPasswordFromUserInput();
byte[] salt, key;
// 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(key))
throw new InvalidOperationException("Password is invalid!");
}
Ответ 2
Вы захотите использовать пространство имен System.Security.Cryptography
; в частности, MD5
class или SHA256
class.
Немного отрисовываем код на этой странице и, зная, что оба класса имеют один и тот же базовый класс (HashAlgorithm
), вы можете использовать такую функцию:
public string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes);
}
Тогда вы можете назвать это так (для MD5):
string hPassword = ComputeHash(password, new MD5CryptoServiceProvider());
Или для SHA256:
string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider());
Изменить: добавление поддержки солей
Как отметил dtb в комментариях, этот код был бы сильнее, если бы он включал возможность добавления salt. Если вы не знакомы с этим, соль представляет собой набор случайных битов, которые включаются в качестве входных данных для функции хэширования, что значительно сокращает атаки словаря на хешированный пароль (например, используя радужная таблица). Здесь измененная версия функции ComputeHash
, которая поддерживает соль:
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
return BitConverter.ToString(hashedBytes);
}
Надеюсь, что это было полезно!
Ответ 3
Вы всегда должны солить пароль перед хэшированием при их хранении в базе данных.
Рекомендуемые столбцы базы данных:
- PasswordSalt: int
- PasswordHash: двоичный код (20)
Большинство сообщений, которые вы найдете в Интернете, будут говорить о кодировании ASCII соли и хэша, но это не нужно и только добавлять ненужные вычисления. Также, если вы используете SHA-1, тогда выход будет только 20 байтов, поэтому ваше хеш-поле в базе данных должно быть только 20 байтов. Я понимаю, что вы спрашивали о SHA-256, но, если у вас нет веской причины, использование SHA-1 с солью будет достаточным для большинства бизнес-практик. Если вы настаиваете на SHA-256, то в хэш-поле в базе данных должно быть 32 байта.
Ниже приведены несколько функций, которые будут генерировать соль, вычислять хэш и проверять хэш против пароля.
Функция соли ниже порождает криптографически сильную соль как целое из 4 криптографически созданных случайных байтов.
private int GenerateSaltForPassword()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] saltBytes = new byte[4];
rng.GetNonZeroBytes(saltBytes);
return (((int)saltBytes[0]) << 24) + (((int)saltBytes[1]) << 16) + (((int)saltBytes[2]) << 8) + ((int)saltBytes[3]);
}
Затем пароль можно испортить, используя соль с приведенной ниже функцией. Соль объединяется с паролем, а затем вычисляется хеш.
private byte[] ComputePasswordHash(string password, int salt)
{
byte[] saltBytes = new byte[4];
saltBytes[0] = (byte)(salt >> 24);
saltBytes[1] = (byte)(salt >> 16);
saltBytes[2] = (byte)(salt >> 8);
saltBytes[3] = (byte)(salt);
byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password);
byte[] preHashed = new byte[saltBytes.Length + passwordBytes.Length];
System.Buffer.BlockCopy(passwordBytes, 0, preHashed, 0, passwordBytes.Length);
System.Buffer.BlockCopy(saltBytes, 0, preHashed, passwordBytes.Length, saltBytes.Length);
SHA1 sha1 = SHA1.Create();
return sha1.ComputeHash(preHashed);
}
Проверка пароля может быть выполнена просто путем вычисления хеша, а затем сравнения с ожидаемым хешем.
private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash)
{
byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt);
return hashedPassword.SequenceEqual(correctPasswordHash);
}
Ответ 4
Если вы собираетесь хранить хешированные пароли, используйте bcrypt вместо SHA-256. Проблема в том, что SHA-256 оптимизирован для скорости, что упрощает атаку грубой силы на пароли, если кто-то получит доступ к вашей базе данных.
Прочтите эту статью: Достаточно с таблицами Rainbow: что вам нужно знать о безопасных схемах паролей и ответить на предыдущий вопрос SO.
Некоторые цитаты из статьи:
Проблема в том, что MD5 работает быстро. Так же и его современные конкуренты, такие как SHA1 и SHA256. Скорость - это цель дизайна современного безопасного хэша, поскольку хеши являются строительным блоком почти каждой криптосистемы и обычно получают запрос по требованию для каждого пакета или для каждого сообщения.
Скорость - это именно то, чего вы не хотите в хэш-функции пароля.
Наконец, мы узнали, что если мы хотим безопасно хранить пароли, у нас есть три разумных варианта: схема PHKs MD5, схема Bcrypt Provos-Mazieres и SRP. Мы узнали, что правильным выбором является Bcrypt.
Ответ 5
PBKDF2 использует HMACSHA1....... если вы хотите более современную реализацию HMACSHA256 или HMACSHA512 и все еще хотите растягивать клавиши, чтобы сделать алгоритм медленнее, я предлагаю этот API: https://sourceforge.net/projects/pwdtknet/
Ответ 6
Вот полная реализация персистентности, не знающая класс SecuredPassword
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public class SecuredPassword
{
private const int saltSize = 256;
private readonly byte[] hash;
private readonly byte[] salt;
public byte[] Hash
{
get { return hash; }
}
public byte[] Salt
{
get { return salt; }
}
public SecuredPassword(string plainPassword)
{
if (string.IsNullOrWhiteSpace(plainPassword))
return;
using (var deriveBytes = new Rfc2898DeriveBytes(plainPassword, saltSize))
{
salt = deriveBytes.Salt;
hash = deriveBytes.GetBytes(saltSize);
}
}
public SecuredPassword(byte[] hash, byte[] salt)
{
this.hash = hash;
this.salt = salt;
}
public bool Verify(string password)
{
if (string.IsNullOrWhiteSpace(password))
return false;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(saltSize);
return newKey.SequenceEqual(hash);
}
}
}
И тесты:
public class SecuredPasswordTests
{
[Test]
public void IsHashed_AsExpected()
{
var securedPassword = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.EqualTo("password"));
Assert.That(securedPassword.Hash.Length, Is.EqualTo(256));
}
[Test]
public void Generates_Unique_Salt()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Salt, Is.Not.Null);
Assert.That(securedPassword2.Salt, Is.Not.Null);
Assert.That(securedPassword.Salt, Is.Not.EqualTo(securedPassword2.Salt));
}
[Test]
public void Generates_Unique_Hash()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.Null);
Assert.That(securedPassword2.Hash, Is.Not.Null);
Assert.That(securedPassword.Hash, Is.Not.EqualTo(securedPassword2.Hash));
}
[Test]
public void Verify_WhenMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("password");
Assert.That(result, Is.True);
}
[Test]
public void Verify_WhenDifferent_ReturnsFalse()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("Password");
Assert.That(result, Is.False);
}
[Test]
public void Verify_WhenRehydrated_AndMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password123");
var rehydrated = new SecuredPassword(securedPassword.Hash, securedPassword.Salt);
var result = rehydrated.Verify("password123");
Assert.That(result, Is.True);
}
[Test]
public void Constructor_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(null));
}
[Test]
public void Constructor_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(string.Empty));
}
[Test]
public void Verify_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(null));
}
[Test]
public void Verify_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(string.Empty));
}
[Test]
public void Verify_When_Null_Password_ReturnsFalse()
{
Assert.That(new SecuredPassword("password").Verify(null), Is.False);
}
}
Ответ 7
Класс System.Security.Cryptography.SHA256 должен выполнить трюк:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256.aspx
Ответ 8
Пожалуйста, используйте это, так как у меня такие же проблемы, но я могу решить, будет ли это фрагмент кода litle
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
StringBuilder hex = new StringBuilder(hashedBytes.Length * 2);
foreach (byte b in hashedBytes)
hex.AppendFormat("{0:X2}", b);
return hex.ToString();
}