【问题标题】:Is there any way to improve my AES encryption code in C#?有什么方法可以改进我在 C# 中的 AES 加密代码?
【发布时间】:2010-12-30 08:52:54
【问题描述】:

我有这段用于 AES 加密的代码,有人可以验证这段代码是否正确吗?效果很好,但我更关心算法的实现。

// Plaintext value to be encrypted.

//Passphrase from which a pseudo-random password will be derived.
//The derived password will be used to generate the encryption key.

//Password can be any string. In this example we assume that this passphrase is an ASCII string.

//Salt value used along with passphrase to generate password.
//Salt can be any string. In this example we assume that salt is an ASCII string.

//HashAlgorithm used to generate password. Allowed values are: "MD5" and "SHA1".
//SHA1 hashes are a bit slower, but more secure than MD5 hashes.

//PasswordIterations used to generate password. One or two iterations should be enough.

//InitialVector (or IV). This value is required to encrypt the first block of plaintext data.
//For RijndaelManaged class IV must be exactly 16 ASCII characters long.

//KeySize. Allowed values are: 128, 192, and 256.
//Longer keys are more secure than shorter keys.

//Encrypted value formatted as a base64-encoded string.


public static string Encrypt(string PlainText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;
    ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
    MemoryStream MemStream = new MemoryStream();
    CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
    CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
    CryptoStream.FlushFinalBlock();
    byte[] CipherTextBytes = MemStream.ToArray();
    MemStream.Close();
    CryptoStream.Close();
    return Convert.ToBase64String(CipherTextBytes);
}

public static string Decrypt(string CipherText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;
    ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes);
    MemoryStream MemStream = new MemoryStream(CipherTextBytes);
    CryptoStream cryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read);
    byte[] PlainTextBytes = new byte[CipherTextBytes.Length];
    int ByteCount = cryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
    MemStream.Close();
    cryptoStream.Close();
    return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
}

我不是专业安全人员或专业程序员,我开始学习并且喜欢理解它

我的计划是构建一个需要最少输入但遵循标准的 AES 加密函数,以便在 PHP 和 JavaScript 等其他语言中轻松制作等效函数!

谢谢

【问题讨论】:

    标签: c# .net encryption aes


    【解决方案1】:

    一些通用的cmets。第一个是围绕IV。通常,您希望 IV 是随机的。通过将其限制为(可显示的)ASCII,您在某种程度上限制了可能的值。一般来说,您最好 a) 使用 GenerateIV() 方法,b) 将 IV 值添加到密文中,这样 c) 您不需要将其作为参数传递给任一函数。

    第二条评论是,一般来说,您需要计划未来对加密需求的变化。最好在密文旁边嵌入一些版本控制或参数信息,并避免在代码中嵌入特定的加密设置。例如,如果以后您决定将密码的迭代次数加倍,您可能仍希望解密使用旧设置加密的值(或警告用户该值不再可访问)。

    您还可以将大量决策推送到您的配置文件中。例如,您可以在今天映射到 RijndaelManaged 的​​ create a name for your crypto 提供程序(例如 MyAppSymmetricCrypto),但在以后可以更改为当时合适的任何内容。

    【讨论】:

    • 关于 IV 的 cmets 也适用于 Salt。
    【解决方案2】:

    除了@Damien_The_Unbeliever 的回答

    MS 建议使用Rfc2898DeriveBytes 而不是PasswordDeriveBytes。 (然后您可以从方法参数中删除“HashAlgorithm”)。

    您可以根据密码计算 IV:

    var bytes = new Rfc2898DeriveBytes(password, salt, iterations);
    var key = bytes.GetBytes(keySize);
    var iv = bytes.GetBytes(ivSize);
    

    您应该检查输入值是否存在无效值/范围。

    MemoryStreamCryptoStream 类包装在using 语句中。

    【讨论】:

    • 我个人不建议对多条消息使用相同的 IV,即使两者使用相同的密码也是如此。 Same IV + Same key 显示两条消息是否以相同(N = 块大小)字节开头。这可能是也可能不是有意义的披露,但对于一般的加密解决方案,应该避免。
    • 我的意思是 var iv = bytes.GetBytes(ivSize); “iv”是我代码中的“InitialVectorBytes”吗?我的代码中的“ivSize”是什么?
    • @Damien_The_Unbeliever ,谢谢分配,再次不确定如何实施您的建议!
    猜你喜欢
    • 2011-08-19
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    相关资源
    最近更新 更多