33

如何使用密码生成 Rijndael KEY 和 IV?密钥长度必须为 256 位。

4

6 回答 6

62

我认为您正在寻找基于密码的密钥派生。有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);
    }
}

确保您更改saltiterations参数。

于 2011-06-30T07:11:43.637 回答
44

这是我在互联网上找到的即插即用代码。它只是工作:

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();
}
于 2011-07-01T05:50:48.397 回答
9

IV 必须是随机的(不需要是不可预知的随机,只要足够随机以至于它们不会被重复使用)。

至于从密码生成密钥,您正在寻找一个密钥派生函数,现在至少有三个不错的选择(PBKDF2、bcrypt、scrypt),使用非迭代哈希,正如之前的海报所建议的那样,通常会导致到不安全的系统。

也使用 AES 或 Rijndael,这不完全一样。使用不属于 AES 的 Rijndael 组合以后可能会成为互操作性的噩梦,而且这些功能组合的安全性无论如何都没有得到很好的研究。

于 2011-06-26T20:31:44.207 回答
4

IV 必须是随机的(通常与加密数据一起传递),并且可以通过多种方式导出密钥:只需将密码填充到密钥的长度(如果密码短于 32 个字符)或 (哪个更可靠)通过使用 SHA2 散列算法或使用一些更复杂的方法来导出密钥。

于 2011-06-26T09:50:42.377 回答
2

使用这个Rfc2898DeriveBytes 类

不过,建议您的安全级别已下降/受到密码长度/强度的限制。所以,不要这样做。

于 2011-07-01T22:59:43.163 回答
0

其他答案包含更多信息。这个问题的答案,你只想生成密钥和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);
于 2020-02-07T20:12:15.277 回答