Подтвердить что ты не робот

Может ли CryptoStream оставить базовый поток открытым?

Я создаю MemoryStream, передаю его CryptoStream для записи. Я хочу, чтобы CryptoStream зашифровал и оставил MemoryStream открытым для меня, чтобы затем прочитать что-то еще. Но как только CryptoStream располагается, он также имеет значение MemoryStream.

Может ли CryptoStream оставить базу MemoryStream как-то открыта?

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);
}
4b9b3361

Ответ 1

Вы можете, но не сможете использовать с помощью операторов. Вам нужно будет вручную управлять удалением объекта, и вам также нужно будет вызвать FlushFinialBlock(), чтобы убедиться, что все данные были выписаны в базовый поток перед началом работы в теме.

Как только вы закончите работу с потоком, вы можете затем распоряжаться всеми ресурсами, которые вы ожидаете в конце блока в конце.

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();
}

Ответ 2

В качестве второго решения вы можете создать объект WrapperStream, который просто передает каждый вызов, за исключением Dispose/Close. Создайте обертку вокруг вашего потока памяти, передайте обертку криптовому потоку, и теперь закрытие криптопотока не касается потока памяти.

Ответ 3

Оказывается, нет необходимости разрывать использование блока {} в try {} finally {}... В конечном счете вам просто нужно использовать FlushFinalBlock() внутри оператора using и вложить что-нибудь еще внутри там по мере необходимости.

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);
        }
    }
}

Ответ 4

Начиная с .NET 4.7.2, существует второй конструктор с добавленным параметром bool, который называется leaveOpen. Если для этого параметра установлено значение true, метод удаления CryptoStream не будет вызывать удаление в базовом потоке.

Кроме того, другой конструктор без параметра leaveOpen просто перенаправляет параметры в новый конструктор с leaveOpen значением false для leaveOpen конструктора.

MSDN
CryptoStream.Dispose(bool disposing)

Ответ 5

Мое простое решение:

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 );
    }
}

Ответ 6

Как ответил @CyberBasti. Я считаю, что лучшим решением является переопределить метод dispose и вызвать базу с помощью false.

Я заглянул в справочный источник, и это показывает, что это лучшее решение, потому что вы все равно можете использовать ключевое слово using и сделать организованный код:)

btw Я отвечаю, потому что не могу комментировать xD