C#指定的填充模式对此算法无效

时间:2018-03-22 15:41:12

标签: c# security encryption asp.net-core aes

我已经看过其他一些有关此问题的帖子,但我不确定如何将它们应用到我的场景中。奇怪的是,我昨晚成功地运行了这个,当我今天回到它时,它正在抛出CryptographicException Specified padding mode is not valid for this algorithm.

其他帖子引用了IV的处理方式,但似乎包含在AesManaged类中。

我需要在此处调整以解决此问题?

public class SymetricEncryptionHelper
{
    private static int Rfc2898KeygenIterations = 100;
    private static int AesKeySizeInBits = 128;

    /// <summary>
    /// Creates an encrypted version of the plain text string and it's salt
    /// </summary>
    /// <param name="plainTextString"></param>
    /// <param name="encryptionPassword"></param>
    /// <returns>Tuple of String, String (encrypted string, salt)</returns>
    public static Tuple<string, string> Encrypt(string plainTextString, string encryptionPassword)
    {
        byte[] Salt = new byte[16];
        var rnd = new Random();
        rnd.NextBytes(Salt);
        byte[] rawPlaintext = Encoding.Unicode.GetBytes(plainTextString);
        byte[] cipherText = null;
        using (Aes aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.KeySize = AesKeySizeInBits;
            int KeyStrengthInBytes = aes.KeySize / 8;
            var rfc2898 = new Rfc2898DeriveBytes(encryptionPassword, Salt, Rfc2898KeygenIterations);
            aes.Key = rfc2898.GetBytes(KeyStrengthInBytes);
            aes.IV = rfc2898.GetBytes(KeyStrengthInBytes);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(rawPlaintext, 0, rawPlaintext.Length);
                }
                cipherText = ms.ToArray();
            }
        }
        return new Tuple<string, string>(Encoding.Unicode.GetString(cipherText), Encoding.Unicode.GetString(Salt));
    }

    /// <summary>
    /// Returns the decrypted string 
    /// </summary>
    /// <param name="cipherTextString"></param>
    /// <param name="cipherSalt"></param>
    /// <param name="encryptionPassword"></param>
    /// <returns>String</returns>
    public static string Decrypt(string cipherTextString, string cipherSalt, string encryptionPassword)
    {
        byte[] Salt = Encoding.Unicode.GetBytes(cipherSalt);
        byte[] cipherText = Encoding.Unicode.GetBytes(cipherTextString);
        byte[] plainText = null;
        using (Aes aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.KeySize = AesKeySizeInBits;
            int KeyStrengthInBytes = aes.KeySize / 8;
            var rfc2898 = new Rfc2898DeriveBytes(encryptionPassword, Salt, Rfc2898KeygenIterations);
            aes.Key = rfc2898.GetBytes(KeyStrengthInBytes);
            aes.IV = rfc2898.GetBytes(KeyStrengthInBytes);

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherText, 0, cipherText.Length);
                }
                plainText = ms.ToArray();
            }
        }
        return Encoding.Unicode.GetString(plainText);
    }
}

1 个答案:

答案 0 :(得分:2)

您无法使用Encoding.Unicode.GetString将随机字节转换为字符,因为并非所有组合都是有效的UTF-16代码点序列。遇到非法序列时,解码器将回退给您U+FFFD REPLACEMENT CHARACTER,修改原始字节并确保解密失败。这是一段简单的代码来说明问题:

byte[] b = new byte[4];
var r = new Random();
while (true) {
    r.NextBytes(b);
    var s = Encoding.Unicode.GetString(b);
    var expected = b;
    var actual = Encoding.Unicode.GetBytes(s);
    if (!actual.SequenceEqual(expected)) {
        Console.WriteLine(
            $"Fail: expected {BitConverter.ToString(expected)}, " +
            $"got {BitConverter.ToString(actual)}"
        );
        break;
    }
}

这应该很快就会出现

  

失败:预期3B-DC-5C-52,得到FD-FF-5C-52

一个简单的解决方法是使用Convert.ToBase64StringConvert.FromBase64String代替,因为这会进行往返。