将IV添加到CryptoStream的开头

时间:2018-05-21 09:29:45

标签: c# encryption aes

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

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

<body onload="startGame()">
<input type="text" name="Box" id="Box" value="" placeholder="Write whatever"/>
<button onclick="myGameArea.myFunction()">Submit</button>
</body>

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

3 个答案:

答案 0 :(得分:4)

好的......既然你的问题很明显,那就是&#34;相当&#34;简单......遗憾的是,.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;
        }
    }
}

答案 1 :(得分:1)

使用CryptoStream在两个当地方之间进行通信并不是一个好的设计。您应该使用通用InputStream或管道(用于进程间通信)。然后,您可以将MemoryStream与IV CryptoStream合并,然后返回组合。请参阅answer of xanatos了解如何执行此操作(如果需要,您可能仍需要填写Seek功能。)

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

如果您需要保留当前的设计,那么就有可用的黑客攻击。首先&#34;解密&#34; IV使用ECB模式而不填充。作为单个分组密码,始终成功,结果将是一个数据块 - 当使用CipherStream加密时 - 再次变为IV。

步骤:

  1. 在数组中生成16个随机字节,这将是真正的IV;
  2. 使用ECB解密 16字节IV而不使用填充和用于CipherStream的密钥;
  3. 使用密钥和全零,16字节IV初始化CipherStream;
  4. 加密&#34;解密&#34; IV使用CipherStream;
  5. 输入明文的其余部分。
  6. 您需要创建一个InputStream,它首先接收解密的IV(作为MemoryStream),然后创建明文(作为FileStream),以实现此目的。再次,also see the answer of xanatos如何做到这一点。或者参见例如this combinerthis HugeStream关于好的&#39;堆栈溢出。然后使用组合流作为CipherInputStream的源。

    但不用说,像这样的黑客应该在很方便的情况下记录和删除。

    注意:

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

答案 2 :(得分:-1)

感谢那些花时间回答的人。最后我意识到我必须知道缓冲代码中的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 */