我對看起來像.NET CryptoStream
類的怪異感到困惑:它的Dispose()
方法讀取密文末尾以尋找填充它不應該,並且因此拋出CryprographicException
。.NET CryptoStream在Dispose()中讀取密文的末尾並炸燬
下面的C#程序加密的幾個字節,調整大小密文陣列,使得有更多的(無義)密文的結束後的字節,然後嘗試解密它。要點如下:
- 密文是8個字節,一個3DES密碼塊。由於我只寫入6個字節到
CryptoStream
,而它使用PaddingMode.PKCS7
(默認值),所以塊中剩下的兩個字節填充了填充值0x02。 - 密文數組隨後調整大小爲16個字節,即兩個3DES塊。第二個塊是未初始化的廢話,而不是有效的密碼輸出。
- 解密時,我從
CryptoStream
剛剛讀取6個字節;我不要求它解密成廢話部分,我不依靠它識別填充當它達到明文結束弄清楚。
的問題是,當的Dispose()
解密CryptoStream
被調用(在using
塊結束時自動),得到了一個CryptographicException
與消息‘壞數據’。它的堆棧跟蹤顯示它正在執行CryptoStream.FlushFinalBlock()
,並且從ciphertextStream
消耗了所有16個字節,而不僅僅是對應於實際加密數據的8個字節。
如果我刪除調整大小的ciphertext
陣列線,該程序正常工作。如果我在解密之前做了tripleDes.Padding = PaddingMode.None
,程序也能正常工作 - 但基本上使填充字節成爲明文的一部分,所以我寧願不這樣做。很明顯,問題在於填充相關;據我所知,它是解密的第二塊,並期待在它的末尾找到有效的PKCS7
-樣式的填充。
由於我只從CryptoStream
閱讀到需要一個塊進行解密,並且塊是一個正確的填充最終塊,然後我關閉CryptoStream
不讀任何更多,爲什麼流認爲,需要讀取另一個塊並尋找更多的填充?爲什麼它甚至試圖消耗更多的輸入作爲其Dispose()
的一部分?
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Test
{
class Program
{
static void Main(string[] args)
{
byte[] plaintext = { 0, 1, 2, 3, 4 };
using (SymmetricAlgorithm tripleDes = TripleDESCryptoServiceProvider.Create())
{
// Encrypt the plaintext
byte[] ciphertext;
using (MemoryStream ciphertextStream = new MemoryStream())
{
using (ICryptoTransform encryptor = tripleDes.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.WriteByte((byte)plaintext.Length);
cryptoStream.Write(plaintext, 0, plaintext.Length);
cryptoStream.FlushFinalBlock();
}
}
ciphertext = ciphertextStream.ToArray();
}
// *** Add some non-ciphertext garbage to the end ***
Array.Resize(ref ciphertext, ciphertext.Length + 8);
// Now decrypt it again
byte[] decryptedPlaintext;
using (MemoryStream ciphertextStream = new MemoryStream(ciphertext, false))
{
using (ICryptoTransform decryptor = tripleDes.CreateDecryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, decryptor, CryptoStreamMode.Read))
{
int length = cryptoStream.ReadByte();
decryptedPlaintext = new byte[length];
int i = 0;
while (i < length)
{
int bytesRead = cryptoStream.Read(decryptedPlaintext, i, (length - i));
if (bytesRead == 0) break;
else i += bytesRead;
}
} // CryptographicException: "Bad Data"
}
}
System.Diagnostics.Debug.Assert(decryptedPlaintext.SequenceEqual(plaintext));
}
}
}
}
難道不應該知道基於這樣的事實,我讀一個塊後關閉流,而一個塊正確填充的最後一塊?我從來沒有要求它讀取垃圾,這令人窒息。有沒有辦法告訴它底層流的其餘部分不是密文,應該忽略? (其實我是想有一些未加密的數據在那裏,不是垃圾。) – Wyzard
@Wyzard:它只知道的是,你沒看過你把它的一切。我建議你設置一個普通的流,同時保存密文和未加密的數據。讀取密文並通過CryptoStream管道。從普通流直接讀取未加密的數據。 – rossum
問題不在於爲什麼它在垃圾上窒息,這就是爲什麼它首先讀*垃圾。請注意,垃圾是* not *塊填充的一部分,它是* past *第一個塊上的填充。我也有類似的情況 - 我的文件末尾沒有垃圾,但我有50MB的數據,我不希望被篡改到內存中 - 我只想在幾個塊中進行流式傳輸。但是當我關閉CypherStream時,它堅持讀我過去所要求的地方。 – Mud