Хеширование паролей с помощью 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);
    }
}

Ответ 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();

    }