C# 链式GZipStream/deflatestStream和加密流(AES)在读取时中断
我想压缩然后加密我的数据,为了提高速度(不必写入字节数组并返回),我决定将用于压缩和加密的流链接在一起 当我写入(压缩和加密)数据时,它工作得很好,但当我尝试读取数据(解压缩和解密)时,读取操作中断-只需调用read one读取0个字节,因为第一次读取总是返回0。下面代码中的循环几乎可以工作,除了在某一点上,Read停止返回大于0的任何内容,即使仍有数据要读取 最后几个字节之前的所有内容都得到了完美的解压缩和解密 对于相同的纯文本,发生这种情况时剩余的字节数保持不变;例如,某个字符串总是9个字节,而另一个字符串总是1个字节 以下是相关的加密和解密代码;有没有关于可能出现什么问题的想法 加密:C# 链式GZipStream/deflatestStream和加密流(AES)在读取时中断,c#,.net,encryption,c#-4.0,compression,C#,.net,Encryption,C# 4.0,Compression,我想压缩然后加密我的数据,为了提高速度(不必写入字节数组并返回),我决定将用于压缩和加密的流链接在一起 当我写入(压缩和加密)数据时,它工作得很好,但当我尝试读取数据(解压缩和解密)时,读取操作中断-只需调用read one读取0个字节,因为第一次读取总是返回0。下面代码中的循环几乎可以工作,除了在某一点上,Read停止返回大于0的任何内容,即使仍有数据要读取 最后几个字节之前的所有内容都得到了完美的解压缩和解密 对于相同的纯文本,发生这种情况时剩余的字节数保持不变;例如,某个字符串总是9个字节
// 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();
解密:
// 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();
我最近在从远程流读取数据时遇到了类似的问题 对我来说,解决方案是将在单个调用中读取整个流的行更改为while循环:
远程流并不总是能够返回请求的数据量,因此请在读取足够字节的同时尝试读取流。问题在于以下几行:
csEncrypt.FlushFinalBlock();
如果删除该选项,代码将正常工作
原因是,当您写入DeflateStream
时,并非所有数据都会写入底层流。只有通过使用块离开,显式或隐式调用Close()
或Dispose()
时,才会发生这种情况
因此,在您的代码中,会发生以下情况:
您可以Write()
将所有数据写入DeflateStream
,而DeflateStream又将大部分数据写入底层加密流
您可以调用csEncrypt.FlushFinalBlock()
,这将关闭加密流
您使用DeflateStream
的块离开,该块尝试将其余数据写入已关闭的加密流
您可以使用加密流的块离开,该块将调用FlushFinalBlock()
,如果它尚未调用的话
正确的事件顺序是:
您可以Write()
将所有数据写入DeflateStream
,而DeflateStream又将大部分数据写入底层加密流
您可以使用DeflateStream
的块离开,该块将其余数据写入已关闭的加密流
您可以使用调用FlushFinalBlock()
的加密流的块离开
尽管我预计写入一个封闭流会失败,但会出现异常。我不确定为什么不会发生这种情况。每次都会从字节0覆盖。您需要添加一个偏移量变量,该变量从0开始,每次读取的字节数都会增加,否则您会一直覆盖缓冲区的开始部分;你说得对-我用以下内容替换了循环:intoffset=0;而(offset
不幸的是,这并没有解决问题;我还剩下1个字节,它挂起了。(回想起来,如果错误的循环是罪魁祸首的话,解密后的数据就不完美了)。是的,当我看到它无法解决挂起字节的问题时,我将其作为答案删除,并将其作为注释添加。我只是想这可能会让你省去第二轮的挠头,不知道为什么缓冲区坏了:)这可能很滑稽。。。是否可以尝试添加一个bytesRead
变量,该变量捕获Read的值,然后仅在该值为正时运行逻辑etc?我想知道它是否在结尾处返回负数(iirc这将是一个有效的终止符,因为从内存中它只是声明非正数是数据的结尾,尽管我最近没有检查关于这一点的文档)尝试过,而且它从不返回负值:<当前文档似乎表明它只返回读取的字节数或0以完成-尽管这有点奇怪,因为我在第一次读取调用时总是得到0(后续调用在挂起之前返回正值)。相反,这一行似乎是写入所有底层数据所必需的。当我删除它时,加密数据会变小,读取时会得到System.Security.Cryptography.CryptographyException,这无疑是因为加密数据无效。@cervellous,对我来说就是这样。例外说明了什么?尝试的一个选项是在处理DeflateStream
之后,但在处理加密流之前调用FlushFinalBlock()
。好吧,您的建议(在处理DeflateStream之后移动刷新/数据处理部分)奏效了!不知道为什么。很抱歉,我在先前的评论中不屑一顾。