C#System.Security.Cryptography-使用错误密钥解密时引发异常-填充如何工作?

C#System.Security.Cryptography-使用错误密钥解密时引发异常-填充如何工作?,c#,encryption,C#,Encryption,我一直试图通过在线文档和示例学习更多关于c#中System.Security.Cryptography的知识。到目前为止,我已经能够成功地加密/解密文本和文件;但是,当我试图用错误的密钥解密时,引发了一个异常 Blockquote System.Security.Cryptography.CryptographyException:“填充无效,无法删除。” 加密 private static byte[] EncryptData(SymmetricAlgorithms symmetricAlgo

我一直试图通过在线文档和示例学习更多关于c#中System.Security.Cryptography的知识。到目前为止,我已经能够成功地加密/解密文本和文件;但是,当我试图用错误的密钥解密时,引发了一个异常

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

加密

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();
                }
            }
        }
    }
SymmetricalGorthmFactory返回SymmetricalGorthm实例

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进行测试,然后我尝试更改padding属性并得到一些不同的消息

PaddingMode.None
-加密方法引发异常

Blockquote System.Security.Cryptography.CryptographyException:“输入数据不是完整的块。”

PaddingMode.PKCS7+PaddingMode.ANSIX923+PaddingMode.ISO10126
-解密方法引发异常

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

PaddingMode.Zeros
-这似乎适用于正确和错误的键


有谁能帮助我理解填充以及某些模式失败的原因吗?在这种情况下,每种不同的对称算法都需要不同的填充吗?

维基百科有一篇文章可能会有所帮助

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

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

可能的原因有很多。不正确的钥匙会损坏一切,包括填充物。事实上,任何解密错误都会阻塞填充,从而导致此问题

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

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

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


使用多种填充模式是非常不寻常的;一个就够了。我怀疑解密方法选择了三种模式中的错误模式来尝试第一种模式。

维基百科有一篇文章可能会有所帮助

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

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

可能的原因有很多。不正确的钥匙会损坏一切,包括填充物。事实上,任何解密错误都会阻塞填充,从而导致此问题

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

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

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


使用多种填充模式是非常不寻常的;一个就够了。我怀疑解密方法首先选择了三种模式中的错误模式进行尝试。

在我的示例中,您可以看到我在哪里注释了我在这两种模式中设置填充的位置,并且设置了相同的位置。我的理解是PaddingMode.PKCS7是默认的吗?我想知道是不是我的实现造成了问题?PaddingMode.Zeros不会删除解密时的填充,因为它是不明确的。(Vs在数据不明确时删除数据)在我的示例中,您可以看到我在哪里注释掉了im在这两种情况下设置填充的位置,并且设置了相同的位置。我的理解是PaddingMode.PKCS7是默认的吗?我想知道是不是我的实现造成了问题?PaddingMode.Zeros不会删除解密时的填充,因为它是不明确的。(Vs在不明确的情况下删除数据)认为我从中找到了答案,在使用错误的密码时可能会发生这种行为,在使用身份验证时可以检查密码是否正确认为我从中找到了答案,在使用错误的密码时可能会发生这种行为,使用身份验证时,您可以检查密码是否正确