2013-11-01 7 views
18

Creare un MemoryStream, passarlo a CryptoStream per la scrittura. Voglio il CryptoStream da crittografare e lasciare aperto il MemoryStream per poi leggere in qualcos'altro. Ma non appena CryptoStream viene smaltito, dispone anche di MemoryStream.Un CryptoStream può lasciare aperto il flusso di base?

Can CryptoStream lasciare la base MemoryStream aprire in qualche modo?

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

Perché stai utilizzando gli stream in primo luogo? Basta chiamare 'encryptor.TransformFinalBlock' sui byte di input. Gli stream sono utili soprattutto per la crittografia/decrittografia incrementale, ma non quando i dati sono disponibili allo stesso tempo. – CodesInChaos

risposta

9

È possibile, ma non sarà in grado di utilizzare utilizzando le istruzioni. Dovrai gestire manualmente lo smaltimento dell'oggetto e dovrai anche chiamare FlushFinialBlock() per assicurarti che tutti i dati siano stati scritti nello stream sottostante prima di lavorarci sopra.

Una volta terminato di lavorare con lo stream, è possibile disporre di tutte le risorse in attesa nel blocco finale alla fine.

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

Alla persona che mi ha dato un -1 per quanto riguarda la mia risposta, ha ritenuto "non utile" giustificare un -1? Se nel mio post sono presenti informazioni errate, mi informi che posso correggerlo o rimuoverlo. Immagino che il modo in cui il codice è scritto all'interno di "try-finally" non sia necessario. Lo rimuoverò. L'unica altra cosa che posso pensare è che 'aes' e' scratch' vengono eliminati prima di 'myCryptoStream'. Lo correggerò anche io. –

+1

Cosa succede se si desidera scrivere un metodo che restituisce il flusso di base dopo che è stato crittografato e disponga il CryptoStream prima di restituire il flusso di base? Importa se si dispone di CryptoStream? o va bene disporre lo BaseStream quando hai finito? –

+0

Downvoted: Non risolve come restituire il flusso di base dal metodo in cui viene utilizzato CryptoStream – Hammerite

6

Si scopre, non v'è alcuna necessità di spezzare il utilizzando {} bloccare in try {} {} finalmente ... In definitiva, basta usare FlushFinalBlock() all'interno della istruzione using, e annida qualsiasi cosa al suo interno, se necessario.

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

'scrach.Flush()' è effettivamente unnessasary, [dal msdn] (http://msdn.microsoft.com/ it-us/library/system.io.memorystream.flush.aspx) "* Sostituisce il metodo Stream.Flush in modo che non venga eseguita alcuna azione. *" –

11

Come una seconda soluzione, si può fare un oggetto WrapperStream che passa semplicemente ogni chiamata insieme ad eccezione di smaltimento/Chiudi. Crea un wrapper attorno al tuo flusso di memoria, passa il wrapper al flusso crittografico e ora chiudendo il flusso crittografico non tocca il flusso di memoria.

5

La mia soluzione semplice:

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

funziona, ma ho una domanda: come MSDN menziona 'smaltimento Tipo: sistema.Boolean true per rilasciare risorse sia gestite che non gestite; false per rilasciare solo risorse non gestite'. Ma come so, gli oggetti Stream non sono oggetti gestiti, quindi come può questa soluzione impedire a CryptoStream di non rilasciare il flusso sottostante? – Gintama

0

Come rispose @CyberBasti. Penso che la soluzione migliore sia sovrascrivere il metodo di eliminazione e invocare la base con false.

ho guardato nel reference source e lo dimostra di essere la soluzione migliore, perché è ancora possibile utilizzare la parola chiave using e rendere il codice organizzata :)

btw sto facendo una risposta perché non posso commentare xD