首页 > 解决方案 > C# System.Security.Cryptography - 使用不正确的密钥解密时引发异常 - 填充如何工作?

问题描述

我一直在尝试通过在线文档和示例在 c# 中了解更多关于 System.Security.Cryptography 的信息。到目前为止,我已经能够成功地加密/解密文本和文件;但是,当我尝试使用不正确的密钥解密时抛出异常。

Blockquote System.Security.Cryptography.CryptographicException:“填充无效,无法删除。”

加密

private static byte[] EncryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
    {
        byte[] output;

        using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
        {
            byte[] encrypted;
            byte[] salt = new byte[PBKDF2_SaltBytes];
            int maxKeySize = GetLegalKeySizes(algorithm).Max();

            _rng.GetBytes(salt); //
            using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
            {
                algorithm.Key = pbkdf2.GetBytes(maxKeySize);
            }

            //algorithm.Padding = PaddingMode.PKCS7;

            using (ICryptoTransform cryptoTransform = algorithm.CreateEncryptor())
            {
                using (MemoryStream inputStream = new MemoryStream(inputBytes), transformedStream = new MemoryStream())
                {
                    using (CryptoStream cryptoStream = new CryptoStream(inputStream, cryptoTransform, CryptoStreamMode.Read))
                    {
                        cryptoStream.CopyTo(transformedStream);
                    }

                    encrypted = transformedStream.ToArray();
                }
            }

            output = new byte[salt.Length + algorithm.IV.Length + encrypted.Length];
            Buffer.BlockCopy(salt, 0, output, 0, salt.Length);
            Buffer.BlockCopy(algorithm.IV, 0, output, salt.Length, algorithm.IV.Length);
            Buffer.BlockCopy(encrypted, 0, output, salt.Length + algorithm.IV.Length, encrypted.Length);
        }

        return output;
    }

解密

private static byte[] DecryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
    {
        using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
        {
            byte[] salt = new byte[PBKDF2_SaltBytes];
            byte[] iv = new byte[algorithm.IV.Length];
            byte[] encryptedData = new byte[inputBytes.Length - salt.Length - iv.Length];

            int maxKeySize = GetLegalKeySizes(algorithm).Max();
            Buffer.BlockCopy(inputBytes, 0, salt, 0, salt.Length);
            Buffer.BlockCopy(inputBytes, salt.Length, iv, 0, iv.Length);
            Buffer.BlockCopy(inputBytes, salt.Length + iv.Length, encryptedData, 0, encryptedData.Length);

            algorithm.IV = iv;
            //algorithm.Padding = PaddingMode.PKCS7;

            using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
            {
                algorithm.Key = pbkdf2.GetBytes(maxKeySize);
            }

            using(ICryptoTransform cryptoTransform = algorithm.CreateDecryptor())
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(encryptedData, 0, encryptedData.Length);
                        cryptoStream.FlushFinalBlock();                         
                    }
                    return memoryStream.ToArray();
                }
            }
        }
    }

SymmetricAlgorithmFactory 返回一个 SymmetricAlgorithm 实例

private static SymmetricAlgorithm SymmetricAlgorithmFactory(SymmetricAlgorithms symmetricAlgorithm)
    {
        switch (symmetricAlgorithm)
        {
            case SymmetricAlgorithms.AES:
                return new AesCryptoServiceProvider();
            case SymmetricAlgorithms.DES:
                return new DESCryptoServiceProvider();
            case SymmetricAlgorithms.RC2:
                return new RC2CryptoServiceProvider();
            case SymmetricAlgorithms.Rijndael:
                return new RijndaelManaged();
            case SymmetricAlgorithms.TripleDES:
                return new TripleDESCryptoServiceProvider();
            default:
                throw new Exception("The provided Symmetric algorithm is not supported.");
        }
    }

public enum SymmetricAlgorithms
{
    AES,
    DES,
    RC2,
    Rijndael,
    TripleDES
}

额外类属性

private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
    private const int PBKDF2_SaltBytes = 8; //PBKDF2 recommends 64 bits minimum. 64 / 8 = 8 bytes
    private const int PBKDF2_Iterations = 10000;
    private static Encoding encoding = Encoding.UTF32;

使用 AES 进行测试,然后我尝试更改填充属性并得到一些不同的消息

PaddingMode.None- 加密方法抛出异常

Blockquote System.Security.Cryptography.CryptographicException: '输入数据不是一个完整的块。'

PaddingMode.PKCS7 + PaddingMode.ANSIX923 + PaddingMode.ISO10126- 解密方法抛出异常

Blockquote System.Security.Cryptography.CryptographicException:“填充无效,无法删除。”

PaddingMode.Zeros- 这似乎适用于正确和不正确的密钥

谁能帮助我理解填充以及为什么某些模式会失败?会不会是每个不同的对称算法都需要不同的填充?

标签: c#encryption

解决方案


维基百科有一篇关于加密填充的文章可能会有所帮助。

块密码以块大小的块加密数据。大多数消息不是块的确切数量,因此需要填充以将最后一个块填充到正确的大小。解密时,填充会自动删除。

在您的情况下,解密无法识别填充和抱怨。

可能的原因有很多。不正确的键会破坏一切,包括填充。实际上,任何解密错误都会影响填充,从而导致此问题。

显然,如果加密函数应用一种类型的填充,而解密函数需要不同的类型,那么你会遇到这个问题。检查双方是否使用相同的填充。除非您有充分的理由,否则请使用 PKCS#7。

当您设置PaddingMode.None加密方法时没有添加填充,因此最后一个块未填充,因此大小错误。因此,加密方对其进行了标记。该模式仅对已经是整数块的固定大小的消息有用。

PaddingMode.Zeros如果最后一个块上有尾随零,则可能会起作用。问题是它会丢失原始消息中的所有尾随零(如果存在)。不建议。

使用多种填充模式是非常不寻常的;一个就足够了。怀疑是解密方式选错了三者的模式先试试。


推荐阅读