将IV添加到CryptoStream的开头

我正在现有的文件管理程序中实现本地加密。

我可以找到的大部分示例代码(如Microsoft)都演示了如何直接写入文件,但我需要做的是提供一个在程序中其他地方使用的流:

CryptoStream GetEncryptStream(string filename) { var rjndl = new RijndaelManaged(); rjndl.KeySize = 256; rjndl.BlockSize = 256; rjndl.Mode = CipherMode.CBC; rjndl.Padding = PaddingMode.PKCS7; // Open read stream of unencrypted source fileStream: var fileStream = new FileStream(filename, FileMode.Open); /* Get key and iv */ var transform = rjndl.CreateEncryptor(key, iv); // CryptoStream in *read* mode: var cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read); /* What can I do here to insert the unencrypted IV at the start of the stream so that the first X bytes returned by cryptoStream.Read are the IV, before the bytes of the encrypted file are returned? */ return cryptoStream; // Return CryptoStream to be consumed elsewhere } 

我的问题在最后一行的注释中概述但是一个:如何将IV添加到CryptoStream的开头,这样它将是读取CryptoStream时返回的第一个X字节,给定控制何时实际启动读取流并写入文件超出了我的代码范围?

好的…现在您的问题很明显,它“非常”容易……遗憾的是,.NET不包含合并两个Stream的类,但我们可以轻松创建它。 MergedStream是一个只读,仅向前多流合并。

你使用像:

 var mergedStream = new MergedStream(new Stream { new MemoryStream(iv), cryptoStream, } 

现在……当有人试图从MergedStream读取时,首先会消耗包含IV的MemoryStream ,然后将使用cryptoStream

 public class MergedStream : Stream { private Stream[] streams; private int position = 0; private int currentStream = 0; public MergedStream(Stream[] streams) { this.streams = streams; } public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotImplementedException(); public override long Position { get => position; set => throw new NotSupportedException(); } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { if (streams == null) { throw new ObjectDisposedException(nameof(MergedStream)); } if (currentStream >= streams.Length) { return 0; } int read; while (true) { read = streams[currentStream].Read(buffer, offset, count); position += read; if (read != 0) { break; } currentStream++; if (currentStream == streams.Length) { break; } } return read; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { try { if (disposing && streams != null) { for (int i = 0; i < streams.Length; i++) { streams[i].Close(); } } } finally { streams = null; } } } 

使用CryptoStream在两个本地方之间进行通信并不是一个好的设计。 您应该使用通用InputStream或管道(用于进程间通信)。 然后,您可以为IV和CryptoStream组合MemoryStream并返回组合。 请参阅xanatos关于如何执行此操作的答案 (如果需要,您可能仍需要填写Seekfunction)。

CryptoStream只能处理密文。 因为你需要在接收器上更改代码,如果你想要解密,你也可以重构到InputStream


如果您需要保留当前的设计,那么可以使用黑客。 首先使用ECB模式“解密”IV而不填充。 由于单个分组密码呼叫总是成功,结果将是一个数据块 – 当使用CipherStream加密时 – 再次变为IV。

脚步:

  1. 在数组中生成16个随机字节,这将是真正的IV;
  2. 使用ECB 解密 16字节IV而不填充和用于CipherStream的密钥;
  3. 使用密钥和全零,16字节IV初始化CipherStream ;
  4. 使用CipherStream加密“解密”IV;
  5. 输入明文的其余部分。

您需要创建一个首先接收解密的IV(作为MemoryStream )然后接收明文(作为FileStream )的InputStream ,以实现此目的。 再次, 也看到xanatos的答案如何做到这一点。 或者看看这个组合器和这个HugeStream就好了’StackOverflow。 然后使用组合流作为CipherInputStream源。

但不用说,像这样的黑客应该在最方便的时候记录和删除。


笔记:

  • 这个技巧不适用于任何模式; 它适用于CBC模式,但其他模式可能使用不同的IV;
  • 请注意, OutputStream通常对加密更有意义,设计可能还有其他问题。

感谢那些花时间回答的人。 最后我意识到我必须知道缓冲代码中的IV长度,没有办法绕过它,所以选择保持简单:

加密方法(伪代码):

 /* Get key and IV */ outFileStream.Write(IV); // Write IV to output stream var transform = rijndaelManaged.CreateEncryptor(key, iv); // CryptoStream in read mode: var cryptoStream = new CryptoStream(inFileStream, transform, CryptoStreamMode.Read); do { cryptoStream.Read(chunk, 0, blockSize); // Get and encrypt chunk outFileStream.Write(chunk); // Write chunk } while (chunk.Length > 0) /* Cleanup */ 

解密方法(伪码):

 /* Get key */ var iv = inFileStream.Read(ivLength); // Get IV from input stream var transform = rijndaelManaged.CreateDecryptor(key, iv); // CryptoStream in write mode: var cryptoStream = new CryptoStream(outFileStream, transform, CryptoStreamMode.Write); do { inFileStream.Read(chunk, 0, blockSize); // Get chunk cryptoStream.Write(chunk); // Decrypt and write chunk } while (chunk.Length > 0) /* Cleanup */