Can a CryptoStream leave the base Stream open?

痴心易碎 提交于 2019-11-28 10:45:59

You can but you will not be able to use using statements. You will need to manually manage the disposing of the object and you will also need to call FlushFinialBlock() to make sure all the data was written out to the underlying stream before working on it.

Once all you are done working with the stream you can then dispose all of the resources you where waiting on in the finally block at the end.

MemoryStream scratch = null;
AesManaged aes = null;
CryptoStream myCryptoStream = null;
try
{
    scratch = new MemoryStream();
    aes = new AesManaged();

    // <snip>
    // Set some aes parameters, including Key, IV, etc.
    // </snip>
    ICryptoTransform encryptor = aes.CreateEncryptor();
    myCryptoStream = new CryptoStream(scratch, encryptor, CryptoStreamMode.Write);
    myCryptoStream.Write(someByteArray, 0, someByteArray.Length);

    //Flush the data out so it is fully written to the underlying stream.
    myCryptoStream.FlushFinalBlock();

    scratch.Position = 0; 
    byte[] scratchBytes = new byte[scratch.Length];
    scratch.Read(scratchBytes,0,scratchBytes.Length);
    return Convert.ToBase64String(scratchBytes);
}
finally
{
    //Dispose all of the disposeable objects we created in reverse order.

    if(myCryptoStream != null)
        myCryptoStream.Dispose();

    if(aes != null)
        aes.Dispose();

    if(scratch != null)
        scratch.Dispose();
}

As a second solution, you can make a WrapperStream object that simply passes every call along except for Dispose / Close. Make a wrapper around your memory stream, hand the wrapper to the crypto stream, and now closing the crypto stream does not touch the memory stream.

As of .NET 4.7.2, there is a second constructor with an added bool parameter called leaveOpen. If this is set to true then the CryptoStream's dispose method will not call dispose on the underlying stream.

Additionally, the other constructor without the leaveOpen parameter simply forwards the parameters to the new constructor with leaveOpen set to false.

MSDN
CryptoStream.Dispose(bool disposing)

It turns out, there isn't any need to break apart the using {} block into try{}finally{} ... Ultimately, you just have to use FlushFinalBlock() inside the using statement, and nest anything else inside there as necessary.

using (MemoryStream scratch = new MemoryStream())
{
    using (AesManaged aes = new AesManaged())
    {
        // <snip>
        // Set some aes parameters, including Key, IV, etc.
        // </snip>
        ICryptoTransform encryptor = aes.CreateEncryptor();
        using (CryptoStream myCryptoStream = new CryptoStream(scratch, encryptor, CryptoStreamMode.Write))
        {
            myCryptoStream.Write(someByteArray, 0, someByteArray.Length);
            myCryptoStream.FlushFinalBlock();
            scratch.Flush();   // not sure if this is necessary
            byte[] scratchBytes = scratch.ToArray();
            return Convert.ToBase64String(scratchBytes);
        }
    }
}

My simple solution:

class NotClosingCryptoStream : CryptoStream
{
    public NotClosingCryptoStream( Stream stream, ICryptoTransform transform, CryptoStreamMode mode )
        : base( stream, transform, mode )
    {
    }

    protected override void Dispose( bool disposing )
    {
        if( !HasFlushedFinalBlock )
            FlushFinalBlock();

        base.Dispose( false );
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!