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



我正在尝试加密流并再次解密它。加密时,我首先将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);
                }

我怀疑存在涉及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"

最新更新