簡體   English   中英

將 IV 添加到 CryptoStream 的開頭

[英]Add IV to beginning of 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 => streams.Sum(s => s.Length);

    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 的MemoryStreamCryptoStream並返回組合。 請參閱xanatos關於如何執行此操作的答案(如果需要,您可能仍需要填寫Seek功能)。

CryptoStream只能處理密文。 由於您無論如何都需要更改接收器處的代碼,如果您想解密,您不妨重構為InputStream


如果您需要保留當前設計,則可以使用 hack。 首先使用沒有填充的 ECB 模式“解密”IV。 由於單個塊密碼調用總是成功,結果將是一個數據塊 - 當使用CipherStream加密時 - 再次變成 IV。

腳步:

  1. 在一個數組中生成 16 個隨機字節,這將是真正的 IV;
  2. 使用沒有填充的 ECB 和用於CipherStream的密鑰解密16 字節 IV;
  3. 使用密鑰和全零的 16 字節 IV 初始化CipherStream
  4. 使用CipherStream加密“解密”IV;
  5. 輸入其余的明文。

您將需要創建一個InputStream ,它首先接收解密的 IV(作為MemoryStream ),然后是明文(作為FileStream ),這樣才可行。 同樣,也請參閱 xanatos關於如何執行此操作的答案 或者在好的 ol' StackOverflow 上查看例如這個組合器這個HugeStream 然后使用組合流作為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 */

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM