使用CryptoStream时为什么会出现PKCS7填充错误?

时间:2013-12-01 22:06:06

标签: c# encryption mono

我正在尝试加密流并再次解密。加密时,我首先将salt和IV(8和16字节)存储到目标流中。解密时,我在调用CopyTo()的行中出现填充错误。该课程的完整来源可以在Gist

中找到

加密的相关代码段是:

// Set position to start of stream.
                encryptedOutStream.Seek (0, SeekOrigin.Begin);

                // Store the salt in the output stream. The salt is not a secret. Salt is used to generate different keys for identical passwords.
                var keyInfo = GenerateKey (password);
                encryptedOutStream.Write (keyInfo.Salt, 0, keyInfo.Salt.Length);

                // Store the IV in the output stream. The IV is randomly generated if not set explicitly. It is not a secret and used to create 
                // different encrypted output for identical plaintext input when using CBC cipher mode.
                encryptedOutStream.Write (aesAlgo.IV, 0, aesAlgo.IV.Length);

                // Let the algorithm know our key.
                aesAlgo.Key = keyInfo.Key;

                // Get an encrypting ICryptoTransform interface from the algorithm.
                using(var cryptoTransform = aesAlgo.CreateEncryptor ())
                // Pump the input stream through a crypto stream wrapping a memory stream.
                using(var encryptionStream = new CryptoStream(encryptedOutStream, cryptoTransform, CryptoStreamMode.Write))
                {
                    plainInStream.CopyTo (encryptionStream);
                }

和解密:

// Read the salt.
                byte[] salt = new byte[8];
                encryptedInStream.Read (salt, 0, 8);

                // Read the IV.
                byte[] iv = new byte[16];
                encryptedInStream.Read (iv, 0, 16);
                aesAlgo.IV = iv;

                // Generate the key from the password and the salt.
                var keyInfo = GenerateKey (password, salt);
                aesAlgo.Key = keyInfo.Key;

                // Get a decrypting ICryptoTransform interface from the algorithm.
                using(var cryptoTransform = aesAlgo.CreateDecryptor ())
                // Pump the input stream through a crypto stream wrapping a memory stream.
                using(var decryptionStream = new CryptoStream(encryptedInStream, cryptoTransform, CryptoStreamMode.Read))
                {
                    decryptionStream.CopyTo (decryptedOutStream);
                }

1 个答案:

答案 0 :(得分:0)

我怀疑涉及EncryptStringDecryptString方法的问题,特别是行:

 encryptedString = Encoding.UTF8.GetString(encryptedOutStream.ToArray());

 using (var encryptedInStream = new MemoryStream(Encoding.UTF8.GetBytes(s)))

实际上,此代码错误地尝试在密文上使用文本编码,密文是二进制数据。当二进制数据不是合法的UTF8序列,破坏密文并引入填充问题时,这将引入错误。相反,需要使用二进制编码方法(最简单的是,base64)。

要更正此问题,请将以上行更改为:

 // change line 281:
 //  encryptedString = Encoding.UTF8.GetString(encryptedOutStream.ToArray());
 // to:
 encryptedString = Convert.ToBase64String(encryptedOutStream.ToArray());

// change line 251:
//  using (var encryptedInStream = new MemoryStream(Encoding.UTF8.GetBytes(s)))
// to:
using (var encryptedInStream = new MemoryStream(Convert.FromBase64String(s)))

通过此更改,转换似乎有效。使用密码“password”加密然后解密明文“payload”的简单驱动程序将打印所需的输出:

string password = "password";
SymmetricCrypto c = new SymmetricCrypto();
string ct = c.EncryptString("payload", password);
Console.WriteLine(ct); // prints sLSZfzVQGCoML29... (ciphertext will vary)
string dt = c.DecryptString(ct, password);
Console.WriteLine(dt); // prints "payload"