Как сгенерировать Rijndael KEY и IV с использованием ключевой фразы?

Как сгенерировать Rijndael KEY и IV с использованием ключевой фразы? Длина ключа должна быть в 256 битах.

5 ответов

Решение

Это код Plug and Play, который я нашел в интернете. Это просто работает:

using System.IO;
using System.Security.Cryptography;

private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

public static byte[] Encrypt(byte[] plain, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(plain, 0, plain.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}

public static byte[] Decrypt(byte[] cipher, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(cipher, 0, cipher.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}

Я думаю, что вы ищете для получения ключа на основе пароля. Есть Rfc2898DeriveBytes класс, который реализует это.

Rfc2898DeriveBytes принимает пароль, соль и счетчик итераций, а затем генерирует ключи посредством обращений к GetBytes метод.

RFC 2898 включает методы для создания ключа и вектора инициализации (IV) из пароля и соли. Вы можете использовать PBKDF2, функцию получения ключа на основе пароля, для получения ключей с использованием псевдослучайной функции, которая позволяет генерировать ключи практически неограниченной длины. Класс Rfc2898DeriveBytes можно использовать для создания производного ключа из базового ключа и других параметров. В функции получения ключа на основе пароля базовый ключ представляет собой пароль, а другие параметры представляют собой солт-значение и счетчик итераций.

Для получения дополнительной информации о PBKDF2 см. RFC 2898, "PKCS #5: Спецификация криптографии на основе пароля, версия 2.0".

Пример:

public static byte[] CreateKey(string password)
{
    var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };

    const int Iterations = 9872;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
        return rfc2898DeriveBytes.GetBytes(32);
}

Ты можешь использовать DeriveBytes в любом симметричном алгоритме, а не только Rijndael,
Пример:

public static SymmetricAlgorithm InitSymmetric(SymmetricAlgorithm algorithm, string password, int keyBitLength)
{
    var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };

    const int Iterations = 234;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
    {
        if (!algorithm.ValidKeySize(keyBitLength))
            throw new InvalidOperationException("Invalid size key");

        algorithm.Key = rfc2898DeriveBytes.GetBytes(keyBitLength / 8);
        algorithm.IV = rfc2898DeriveBytes.GetBytes(algorithm.BlockSize / 8);
        return algorithm;
    }
}

private static byte[] Transform(byte[] bytes, Func<ICryptoTransform> selectCryptoTransform)
{
    using (var memoryStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memoryStream, selectCryptoTransform(), CryptoStreamMode.Write))
            cryptoStream.Write(bytes, 0, bytes.Length);
        return memoryStream.ToArray();
    }
}

Использование:

public static void Main()
{
    using (var rijndael = InitSymmetric(Rijndael.Create(), "TestPassword", 256))
    {
        var text = "Some text to encrypt";
        var bytes = Encoding.UTF8.GetBytes(text);

        var encryptedBytes = Transform(bytes, rijndael.CreateEncryptor);
        var decryptedBytes = Transform(encryptedBytes, rijndael.CreateDecryptor);

        var decryptedText = Encoding.UTF8.GetString(decryptedBytes);
        Debug.Assert(text == decryptedText);
    }
}

Убедитесь, что вы измените salt а также iterations параметры.

IV должен быть случайным (не должен быть непредсказуемым, просто случайным, чтобы их нельзя было использовать повторно).

Что касается генерации ключа из пароля, вы ищете функцию получения ключа, для которой в настоящее время есть по крайней мере три хороших варианта (PBKDF2, bcrypt, scrypt), с использованием не повторяющегося хэша, как предыдущий плакат предлагает чаще, чем не ведет небезопасных систем.

Также используйте AES или Rijndael, это не совсем то же самое. Использование комбинации Rijndael, не являющейся частью AES, может позже стать кошмаром для взаимодействия, и безопасность комбинации этих функций в любом случае недостаточно изучена.

IV должен быть случайным (обычно вы передаете его вместе с зашифрованными данными), и ключ может быть получен несколькими способами: просто добавьте пароль к длине ключа (если пароль короче 32 символов) или (что более надежно) получить ключ с помощью алгоритма хэширования SHA2 или использовать более сложный способ.

Используйте этот класс Rfc2898DeriveBytes.

Однако, совет советую, ваш уровень безопасности упал / ограничился длиной / силой парольной фразы. Так что не делай этого.

Другие ответы содержат гораздо больше информации. Ответ на этот вопрос, когда вы хотите сгенерировать только ключ и IV с использованием парольной фразы:

        // use something more random in real life
        var salt =  new byte[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

        string password = "my-password";
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt);
        var key = pdb.GetBytes(32);
        var iv = pdb.GetBytes(16);
Другие вопросы по тегам