Can a CryptoStream leave the base Stream open?
Asked Answered
A

5

25

I create a MemoryStream, pass it to CryptoStream for writing. I want the CryptoStream to encrypt, and leave the MemoryStream open for me to then read into something else. But as soon as CryptoStream is disposed, it disposes of MemoryStream too.

Can CryptoStream leave the base MemoryStream open somehow?

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);
        }
    }
    // Here, I'm still within the MemoryStream block, so I expect
    // MemoryStream to still be usable.
    scratch.Position = 0;    // Throws ObjectDisposedException
    byte[] scratchBytes = new byte[scratch.Length];
    scratch.Read(scratchBytes,0,scratchBytes.Length);
    return Convert.ToBase64String(scratchBytes);
}
Apiculture answered 1/11, 2013 at 22:47 Comment(1)
Why are you using streams in the first place? Just call encryptor.TransformFinalBlock on the input bytes. Streams are mostly useful for incremental encryption/decryption but not when you have the full data available at the same time.Neurotomy
E
10

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();
}
Erminois answered 1/11, 2013 at 22:54 Comment(4)
To the person who gave me a -1 what about my answer did you consider "not useful" to warrant a -1? If there is incorrect information in my post please inform me so I can correct or remove it. I guess the fact that the way the code is written the inner try-finally is unnecessary. I will remove it. The only other thing I can think of is the fact that aes and scratch get disposed before myCryptoStream. I will correct that also.Erminois
What if you want to write a method that returns the base stream after it has been encrypted and dispose the CryptoStream before returning the base stream? Does it matter if you dispose the CryptoStream? or is it fine to just dispose the BaseStream when your done with it?Chesterton
Downvoted: Doesn't address how to return the base stream from the method in which the CryptoStream is usedCornerstone
CryptoStream Dispose method will still dispose the underlying stream. you still get duplicated dispose. You are just deferring it to the finally block. The correct answer is below with .NET version 4.7.2 new constructor overload.Tippet
B
20

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 MS Learn
CryptoStream.Dispose(bool disposing)

This constructor is not present in .NET Standard 2.0, but the underlying field can be set with reflection (thanks @StuartLC for linking this answer in the comments):

using CryptoStream s = new(scratch, encryptor, CryptoStreamMode.Write)
var prop = s.GetType().GetField("leaveOpen", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
prop.SetValue(s, true);

This is a filthy workaround and I wouldn't recommend it unless you know the systems your software will run on have the relevant version of .NET installed.

Bound answered 15/6, 2018 at 15:41 Comment(3)
+1 For some weird reason, I wasn't able to invoke this overload of the constructor, however, I was able to set the _leaveOpen flag via Reflection with the same effectReceivership
@Receivership were you writing a .NET Standard 2.0 library by any chance? The additional constructor is not part of .NET Standard 2.0 as it would break backwards compatibility with .NET Framework 4.6.1 where the constructor does not exist.Kastner
@Receivership Thanks for the tip! It's a terribly dirty workaround, but since we're not gonna migrate soon to .Net Core and our software is guaranteed to run at 4.7.2 and above, it's the only way to get this working. I don't get why this wasn't added prior anyway. There's enough scenarios where you actually have to encrypt but continue using the stream (since the CryptoStream doesn't allow seeking).Cerveny
F
15

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.

Forme answered 2/11, 2013 at 15:39 Comment(0)
E
10

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();
}
Erminois answered 1/11, 2013 at 22:54 Comment(4)
To the person who gave me a -1 what about my answer did you consider "not useful" to warrant a -1? If there is incorrect information in my post please inform me so I can correct or remove it. I guess the fact that the way the code is written the inner try-finally is unnecessary. I will remove it. The only other thing I can think of is the fact that aes and scratch get disposed before myCryptoStream. I will correct that also.Erminois
What if you want to write a method that returns the base stream after it has been encrypted and dispose the CryptoStream before returning the base stream? Does it matter if you dispose the CryptoStream? or is it fine to just dispose the BaseStream when your done with it?Chesterton
Downvoted: Doesn't address how to return the base stream from the method in which the CryptoStream is usedCornerstone
CryptoStream Dispose method will still dispose the underlying stream. you still get duplicated dispose. You are just deferring it to the finally block. The correct answer is below with .NET version 4.7.2 new constructor overload.Tippet
W
7

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 );
    }
}
Wreckfish answered 21/1, 2015 at 1:12 Comment(1)
it works, but i have a question: as MSDN mention disposing Type: System.Boolean true to release both managed and unmanaged resources; false to release only unmanaged resources. But as i know, Stream objects is unmanaged objects, so how can this solution prevent CryptoStream not release underlying stream ?Nestle
A
4

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);
        }
    }
}
Apiculture answered 2/11, 2013 at 14:14 Comment(1)
scrach.Flush() is indeed unnessasary, from the msdn "Overrides the Stream.Flush method so that no action is performed."Erminois

© 2022 - 2024 — McMap. All rights reserved.