2014-06-12 9 views
6

Se ho un CryptoStream che voglio passare di nuovo per l'utente, l'approccio ingenuo sarebbeÈ possibile restituire un CryptoStream e disporre ancora di tutto correttamente?

public Stream GetDecryptedFileStream(string inputFile, byte[] key, byte[] iv) 
{ 
    var fsCrypt = new FileStream(inputFile, FileMode.Open, FileAccess.Read, FileShare.Read); 
    var rmCrypto = new RijndaelManaged(); 
    var transform = rmCrypto.CreateDecryptor(key, iv); 
    var cs = new CryptoStream(fsCrypt, transform, CryptoStreamMode.Read); 

    return cs; 
} 

So che quando mi dispongo il CryptoStream sottostante FileStreamwill also be disposed. Il problema che sto eseguendo è cosa devo fare con rmCrypto e transform? RijndaelManaged e ICryptoTransform sono classi usa e getta, ma lo smaltimento del flusso non dispone di questi due oggetti.

Qual è il modo corretto di gestire questa situazione?

+1

Secondo il metodo, '' rmCrypto' e transform' devono essere smaltiti dopo il ritorno da 'GetDecryptedFileStream' in quanto sono variabili locali nel metodo. –

+1

@YuvalItzchakov Uscire dall'ambito non significa essere smaltiti. Il GC non finalizzerà questi due oggetti finché non verrà rilasciato l'ultimo riferimento a 'cs'. –

+0

Oh, non sono riuscito a vedere il tuo 'CryptoStream' li accetta come parametro. –

risposta

5

Vorrei prendere in considerazione la creazione della propria classe che avvolge il flusso e quindi è possibile gestire lo smaltimento di questi. Qualcosa su questa linea (mi dispiace, non conosco il tipo di oggetto trasformato in cima alla mia testa).

public CryptoStreamWrapper : Stream, IDisposable 
{ 
    public CryptoStreamWrapper(CryptoStream stream, RijndaelManaged rmCrypto, IDisposable transform) 
    { 
     this.transform = transform; 
     this.rmCrypto = rmCrypto; 
     this.stream = stream; 
    } 

    public void Dispose() 
    { 
     this.transform.Dispose(); 
     this.rmCrypto.Dispose(); 
     this.stream.Dispose(); 
    } 
} 
+3

In realtà, è molto simile alla soluzione che ho trovato dopo aver fatto la domanda dopo "chiedendo alla mia papera". Puoi derivare 'CryptoStream'. Posterò una risposta –

+0

@ScottChamberlain: Adoro il fatto che stai usando il ducking di gomma! – Ian

+0

Ottima soluzione, stava per pubblicare quella cosa esatta –

5

Ian mi ha battuto il concetto di base (go upvote him), ma CryptoStream sé non è sigillato in modo è banale per fare una classe derivata che avvolge le cose che devono essere smaltiti.

/// <summary> 
/// Creates a class that creates a <see cref="CryptoStream"/> and wraps the disposing action of all the associated objects 
/// </summary> 
class ReturnableCryptoStream : CryptoStream 
{ 
    private readonly ICryptoTransform _transform; 
    private readonly IDisposable _algorithm; 

    public ReturnableCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode) 
     : this(stream, transform, mode, null) 
    { 
    } 

    public ReturnableCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode, IDisposable algorithm) 
     : base(stream, transform, mode) 
    { 
     _transform = transform; 
     _algorithm = algorithm; 
    } 

    protected override void Dispose(bool disposing) 
    { 
     base.Dispose(disposing); 
     if (disposing) 
     { 
      if (_transform != null) 
       _transform.Dispose(); 

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

Usato come

public Stream GetDecryptedFileStream(string inputFile, byte[] key, byte[] iv) 
{ 
    var fsCrypt = new FileStream(inputFile, FileMode.Open, FileAccess.Read, FileShare.Read); 
    var rmCrypto = new RijndaelManaged(); 
    var transform = rmCrypto.CreateDecryptor(key, iv); 
    var cs = new ReturnableCryptoStream(fsCrypt, transform, CryptoStreamMode.Read, rmCrypto); 

    return cs; 
} 
+0

Apprezzate la nota di credito nella vostra risposta :) – Ian

+0

Pensandoci, anche questo è più pulito, dato che non è necessario implementare tutti gli altri membri - come Lunghezza, ReadByte() ecc. – Ian

+0

Sto pensando che potrebbe essere più corretto di trasformare.Dispose() dopo la base.Dispose(), dal momento che altrimenti si sta disponendo che la classe base abbia ancora un riferimento e potrebbe scegliere di effettuare chiamate durante il suo processo di smaltimento. Non sono sicuro di quale sia il campo dell'algoritmo (?) Se è l'oggetto originale che ha creato ICryptoTransform, è possibile eliminarlo non appena viene creato ICryptoTransform, quindi non è necessario conservare un riferimento per lo smaltimento in un secondo momento . – redcalx