Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从结尾截断长度未知的x字节流?(.NET)_.net_Stream_Buffer_Cryptostream - Fatal编程技术网

从结尾截断长度未知的x字节流?(.NET)

从结尾截断长度未知的x字节流?(.NET),.net,stream,buffer,cryptostream,.net,Stream,Buffer,Cryptostream,我需要读取长度未知的流,不包括最后20个字节(散列数据)。设置大致如下: 源流(SHA1哈希最后20个字节)->SHA1哈希流(动态计算并在流结束时与嵌入式流哈希进行比较)->AES解密流->对数据进行填充 我不能在处理之前缓冲整个源流,因为它可能有很多GB,这一切都需要在运行中进行。源流不可查找。目前,SHA1流正在将最后20个字节读入它的缓冲区,这会破坏一切,我不知道有什么方法可以控制这种行为 我在考虑在源和SHA1流之间插入一个包装流,实现一个滚动缓冲区(?),将源流以4096字节的块形式

我需要读取长度未知的流,不包括最后20个字节(散列数据)。设置大致如下:

源流(SHA1哈希最后20个字节)->SHA1哈希流(动态计算并在流结束时与嵌入式流哈希进行比较)->AES解密流->对数据进行填充

我不能在处理之前缓冲整个源流,因为它可能有很多GB,这一切都需要在运行中进行。源流不可查找。目前,SHA1流正在将最后20个字节读入它的缓冲区,这会破坏一切,我不知道有什么方法可以控制这种行为

我在考虑在源和SHA1流之间插入一个包装流,实现一个滚动缓冲区(?),将源流以4096字节的块形式呈现给AES包装器,然后在上次读取之前20字节“伪造”流的结尾。然后,20字节的散列将通过属性公开

这是最好的解决方案吗?我将如何实施它

粗略的代码流程如下(从内存中,可能无法编译):

编辑:流格式如下:

[  StreamFormat  |  String  |  Required  ]
[  WrapperFlags  |  8 Bit BitArray  |  Required  ]
[  Sha1 Hashed Data Wrapper  |  Optional  ]
   [  AesIV  |  16 Bytes  |  Required if Aes Encrypted  ]
   [  Aes Encrypted Data Wrapper  |  Optional  ]
      [  Gzip Compressed Data Wrapper  |  Optional  ]
         [  Payload Data  |  Binary  |  Required  ]
      [  End Gzip Compressed Data  ]
   [  End Aes Encrypted Data  ]
[  End Sha1 Hashed Data  ]
[  Sha1HashValue  |  20 Bytes  |  Required if Sha1 Hashed  ]

我已经给你写了一个快速的小数据流,可以提前缓冲20个字节。我唯一正确覆盖的真正实现是
Read()
成员,您可能需要根据您的情况检查其他
Stream
成员。还有一个免费的测试类!奖金!我对它进行了更彻底的测试,但您可以根据自己的意愿调整这些测试用例。哦,顺便说一下,我没有测试长度小于20字节的流

测试用例 流类
公共类流,其在最终20字节之前:流
{
私有只读流sourceStream;
专用静态int tailbytescont=20;
最终20字节之前的公共流(流源流)
{
this.sourceStream=sourceStream;
}
公共字节[]TailBytes{get{return previousTailBuffer;}}
公共覆盖无效刷新()
{
sourceStream.Flush();
}
公共覆盖长寻道(长偏移,参见原始坐标系)
{
返回sourceStream.Seek(偏移量、原点);
}
公共覆盖无效设置长度(长值)
{
sourceStream.SetLength(值);
}
专用字节[]previousTailBuffer;
公共重写整型读取(字节[]缓冲区、整型偏移量、整型计数)
{
字节[]tailBuffer=新字节[tailbytescont];
int预期字节读取;
如果(previousTailBuffer==null)
expectedBytesRead=count+TailBytesCount;
其他的
expectedBytesRead=计数;
尝试
{
字节[]读取缓冲区=新字节[expectedBytesRead];
int actualBytesRead=sourceStream.Read(readBuffer、offset、expectedBytesRead);
如果(actualBytesRead==0)返回0;
如果(实际字节数<尾字节数)
{
int pickPreviousByteCount=尾字节计数-实际字节数;
如果(previousTailBuffer!=null)
{
int pickFromIndex=previousTailBuffer.Length-pickPreviousByteCount;
数组.Copy(previousTailBuffer,0,buffer,offset,count);
复制(previousTailBuffer、pickFromIndex、tailBuffer、0、pickPreviousByteCount);
}
复制(readBuffer,0,tailBuffer,pickPreviousByteCount,actualBytesRead);
返回实际字节数;
}
Copy(readBuffer,actualBytesRead-TailBytesCount,tailBuffer,0,TailBytesCount);
Copy(readBuffer,0,buffer,offset,actualBytesRead-TailBytesCount);
如果(实际字节读取<预期字节读取)
{
返回实际字节读取-尾字节计数;
}
返回计数;
}
最后
{
previousTailBuffer=tailBuffer;
}
}
公共重写无效写入(字节[]缓冲区、整数偏移量、整数计数)
{
写入(缓冲区、偏移量、计数);
}
公共覆盖布尔可读取
{
获取{return sourceStream.CanRead;}
}
公共覆盖布尔搜索
{
获取{return sourceStream.CanSeek;}
}
公共覆盖布尔可写
{
获取{return sourceStream.CanWrite;}
}
公共覆盖长长度
{
得到
{
if(sourceStream.Length
我已经为您编写了一个快速的小数据流,可以提前缓冲20个字节。我唯一正确覆盖的真正实现是
Read()
成员,您可能需要根据您的情况检查其他
Stream
成员。还有一个免费的测试类!奖金!我对它进行了更彻底的测试,但您可以根据自己的意愿调整这些测试用例。哦,顺便说一下,我没有测试长度小于20字节的流

测试用例 流类
公共类流,其在最终20字节之前:流
{
私有只读流sourceStream;
专用静态int tailbytescont=20;
最终20字节之前的公共流(流源流)
{
this.sourceStream=sourceStream;
}
公共字节[]TailBytes{get{return previousTailBuffer;}}
公共组织
[  StreamFormat  |  String  |  Required  ]
[  WrapperFlags  |  8 Bit BitArray  |  Required  ]
[  Sha1 Hashed Data Wrapper  |  Optional  ]
   [  AesIV  |  16 Bytes  |  Required if Aes Encrypted  ]
   [  Aes Encrypted Data Wrapper  |  Optional  ]
      [  Gzip Compressed Data Wrapper  |  Optional  ]
         [  Payload Data  |  Binary  |  Required  ]
      [  End Gzip Compressed Data  ]
   [  End Aes Encrypted Data  ]
[  End Sha1 Hashed Data  ]
[  Sha1HashValue  |  20 Bytes  |  Required if Sha1 Hashed  ]
[TestClass]
    public class TruncateStreamTests
    {
        [TestMethod]
        public void TestTruncateLast20Bytes()
        {
            string testInput = "This is a string.-- final 20 bytes --";
            string expectedOutput = "This is a string.";
            string testOutput;
            using (var testStream = new StreamWhichEndsBeforeFinal20Bytes(new MemoryStream(Encoding.ASCII.GetBytes(testInput))))
            using (var streamReader = new StreamReader(testStream, Encoding.ASCII))
            {
                testOutput = streamReader.ReadLine();
            }

            Assert.AreEqual(expectedOutput, testOutput);
        }

        [TestMethod]
        public void TestTruncateLast20BytesRead3BytesAtATime()
        {
            string testInput = "This is a really really really really really long string, longer than all the others\n\rit even has some carriage returns in it, etc.-- final 20 bytes --";
            string expectedOutput = "This is a really really really really really long string, longer than all the others\n\rit even has some carriage returns in it, etc.";
            StringBuilder testOutputBuilder = new StringBuilder();
            using (var testStream = new StreamWhichEndsBeforeFinal20Bytes(new MemoryStream(Encoding.ASCII.GetBytes(testInput))))
            {
                int bytesRead = 0;
                do
                {
                    byte[] buffer = new byte[3];
                    bytesRead = testStream.Read(buffer, 0, 3);
                    testOutputBuilder.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
                } while (bytesRead > 0);
            }
            Assert.AreEqual(expectedOutput, testOutputBuilder.ToString());
        }
    }
 public class StreamWhichEndsBeforeFinal20Bytes : Stream
    {
        private readonly Stream sourceStream;

        private static int TailBytesCount = 20;

        public StreamWhichEndsBeforeFinal20Bytes(Stream sourceStream)
        {
            this.sourceStream = sourceStream; 
        }

        public byte[] TailBytes { get { return previousTailBuffer; } }

        public override void Flush()
        {
            sourceStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return sourceStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            sourceStream.SetLength(value);
        }

        private byte[] previousTailBuffer;

        public override int Read(byte[] buffer, int offset, int count)
        {
            byte[] tailBuffer = new byte[TailBytesCount];
            int expectedBytesRead;

            if (previousTailBuffer == null)
                expectedBytesRead = count + TailBytesCount;
            else
                expectedBytesRead = count;

            try
            {
                byte[] readBuffer = new byte[expectedBytesRead];
                int actualBytesRead = sourceStream.Read(readBuffer, offset, expectedBytesRead);

                if (actualBytesRead == 0) return 0;

                if (actualBytesRead < TailBytesCount)
                {
                    int pickPreviousByteCount = TailBytesCount - actualBytesRead;

                    if (previousTailBuffer != null)
                    {
                        int pickFromIndex = previousTailBuffer.Length - pickPreviousByteCount;
                        Array.Copy(previousTailBuffer, 0, buffer, offset, count);
                        Array.Copy(previousTailBuffer, pickFromIndex, tailBuffer, 0, pickPreviousByteCount);
                    }

                    Array.Copy(readBuffer, 0, tailBuffer, pickPreviousByteCount, actualBytesRead);
                    return actualBytesRead;
                }

                Array.Copy(readBuffer, actualBytesRead - TailBytesCount, tailBuffer, 0, TailBytesCount);
                Array.Copy(readBuffer, 0, buffer, offset, actualBytesRead - TailBytesCount);

                if (actualBytesRead < expectedBytesRead)
                {
                    return actualBytesRead - TailBytesCount;
                }
                return count;
            }
            finally
            {
                previousTailBuffer = tailBuffer;
            }
        }


        public override void Write(byte[] buffer, int offset, int count)
        {
            sourceStream.Write(buffer, offset, count);
        }

        public override bool CanRead
        {
            get { return sourceStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return sourceStream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return sourceStream.CanWrite; }
        }

        public override long Length
        {
            get
            {
                if (sourceStream.Length < TailBytesCount) return sourceStream.Length;
                return sourceStream.Length - TailBytesCount;
            }
        }

        public override long Position
        {
            get { return sourceStream.Position; }
            set { sourceStream.Position = value; }
        }
    }