我想压缩然后加密我的数据,并且为了提高速度(不必写入字节数组并返回),决定将用于压缩和加密的流链接在一起。
当我写入(压缩和加密)数据时,它工作得很好,但是当我尝试读取数据(解压缩和解密)时,Read操作中断 - 简单地调用Read只读取0个字节,因为第一个Read总是返回0.循环,如下面的代码几乎可以工作,除了在某一点,Read停止返回任何> 0,即使仍有数据需要读取。
在最后几个字节之前的所有内容都被完全解压缩和解密。
发生这种情况时剩余的字节数对于相同的明文保持不变;例如,某个字符串总是9个字节,但另一个字符串总是1个字节。
以下是相关的加密和解密代码;关于什么可能出错的任何想法?
加密
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (DeflateStream zip = new DeflateStream(csEncrypt, CompressionMode.Compress, true))
{
zip.Write(stringBytes, 0, stringBytes.Length);
csEncrypt.FlushFinalBlock();
解密:
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream())
{
// Writes the actual data (sans prepended headers) to the stream
msDecrypt.Write(stringBytes, prependLength, stringBytes.Length - prependLength);
// Reset position to prepare for read
msDecrypt.Position = 0;
// init buffer to read to
byte[] buffer = new byte[originalSize];
using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (DeflateStream zip = new DeflateStream(csDecrypt, CompressionMode.Decompress))
{
// Hangs with "offset" at a small, deterministic number away from originalSize (I've gotten 9 less and 1 less for different strings)
// Loop fixed as per advice
int offset = 0;
while (offset < originalSize)
{
int read = zip.Read(buffer, offset, originalSize - offset);
if (read > 0)
offset += read;
else if (read < 0)
Console.WriteLine(read); // Catch it if it happens.
}
// Hangs with "left" at a small, deterministic number (I've gotten 9 and 1 for different strings)
/*
for (int left = buffer.Length; left > 0; )
left -= zip.Read(buffer, 0, left);
*/
解决方案(修改加密):
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (DeflateStream zip = new DeflateStream(csEncrypt, CompressionMode.Compress, true))
zip.Write(stringBytes, 0, stringBytes.Length);
//Flush after DeflateStream is disposed.
csEncrypt.FlushFinalBlock();
答案 0 :(得分:4)
问题在于以下几行:
csEncrypt.FlushFinalBlock();
如果删除它,代码将起作用。
原因是当您写入DeflateStream
时,并非所有数据都写入基础流。仅当您通过离开Close()
块明确或隐式地致电Dispose()
或using
时,才会发生这种情况。
因此,在您的代码中,会发生这种情况:
Write()
DeflateStream
CryptoStream
的所有数据,然后将大部分数据写入基础csEncrypt.FlushFinalBlock()
。CryptoStream
,关闭using
。DeflateStream
的{{1}}块,该块会尝试将其余数据写入已关闭的CryptoStream
。using
的{{1}}块,如果尚未调用,则会调用CryptoStream
。正确的事件顺序是:
FlushFinalBlock()
Write()
DeflateStream
的所有数据,然后将大部分数据写入基础CryptoStream
。using
的{{1}}块,其中将其余数据写入已关闭的DeflateStream
。CryptoStream
的{{1}}块,该using
块会调用CryptoStream
。虽然我希望写入封闭流会因异常而失败。我不确定为什么不发生这种情况。
答案 1 :(得分:0)
我最近在从远程流中读取时遇到了类似的问题。
对我来说,解决方案是在循环时将单个调用中读取整个流的行更改为:
zip.Write(stringBytes, 0, stringBytes.Length);
远程流并不总是可以返回请求的数据量,因此在读取足够的字节时尝试读取流。