2009-06-02 9 views
11

sto cercando di cifrare e decifrare un flusso di file su un socket utilizzando RijndaelManaged, ma continuo a sbattere contro l'eccezioneLunghezza dei dati per decriptare è valido

CryptographicException: Length of the data to decrypt is invalid. 
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 
    at System.Security.Cryptography.CryptoStream.FlushFinalBlock() 
    at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 

L'eccezione viene generata al termine della l'istruzione using in receiveFile, quando l'intero file è stato trasferito.

Ho provato a cercare sul Web, ma ho trovato solo le risposte ai problemi che sorgono quando si utilizza la codifica durante la crittografia e la decrittografia di una singola stringa. Io uso un FileStream, quindi non specificare alcuna codifica da utilizzare, quindi non dovrebbe essere il problema. Questi sono i miei metodi:

private void transferFile(FileInfo file, long position, long readBytes) 
{ 
    // transfer on socket stream 
    Stream stream = new FileStream(file.FullName, FileMode.Open); 
    if (position > 0) 
    { 
     stream.Seek(position, SeekOrigin.Begin); 
    } 
    // if this should be encrypted, wrap the encryptor stream 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamEncryptor, CryptoStreamMode.Read); 
    } 
    using (stream) 
    { 
     int read; 
     byte[] array = new byte[8096]; 
     while ((read = stream.Read(array, 0, array.Length)) > 0) 
     { 
      streamSocket.Send(array, 0, read, SocketFlags.None); 
      position += read; 
     } 
    } 
} 

private void receiveFile(FileInfo transferFile) 
{ 
    byte[] array = new byte[8096]; 
    // receive file 
    Stream stream = new FileStream(transferFile.FullName, FileMode.Append); 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamDecryptor, CryptoStreamMode.Write); 
    } 
    using (stream) 
    { 
     long position = new FileInfo(transferFile.Path).Length; 
     while (position < transferFile.Length) 
     { 
      int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
      int read = position < array.Length 
         ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
         : streamSocket.Receive(array, SocketFlags.None); 
      stream.Write(array, 0, read); 
      position += read; 
     } 
    } 
} 

Questo è il metodo che utilizzo per impostare i codici. byte [] init è un array di byte generato.

private void setupStreamCipher(byte[] init) 
{ 
    RijndaelManaged cipher = new RijndaelManaged(); 
    cipher.KeySize = cipher.BlockSize = 256; // bit size 
    cipher.Mode = CipherMode.ECB; 
    cipher.Padding = PaddingMode.ISO10126; 
    byte[] keyBytes = new byte[32]; 
    byte[] ivBytes = new byte[32]; 

    Array.Copy(init, keyBytes, 32); 
    Array.Copy(init, 32, ivBytes, 0, 32); 

    streamEncryptor = cipher.CreateEncryptor(keyBytes, ivBytes); 
    streamDecryptor = cipher.CreateDecryptor(keyBytes, ivBytes); 
} 

Qualcuno ha un'idea in quello che potrei fare male?

risposta

6

Mi sembra che tu non stia inviando correttamente il blocco finale. È necessario almeno lo FlushFinalBlock() l'invio CryptoStream per garantire che venga inviato il blocco finale (che il flusso di ricezione sta cercando).

A proposito, CipherMode.ECB is more than likely an epic fail in termini di sicurezza per quello che stai facendo. Almeno utilizzare CipherMode.CBC (concatenamento a blocchi di codice) che utilizza effettivamente l'IV e rende ciascun blocco dipendente dal precedente.

MODIFICA: Whoops, il flusso di codifica è in modalità di lettura. In tal caso è necessario assicurarsi di leggere su EOF in modo che CryptoStream possa gestire il blocco finale, anziché arrestarsi dopo lo readBytes. Probabilmente è più facile da controllare se si esegue il flusso di codifica in modalità di scrittura.

Ancora una nota: non si può assumere che i byte siano uguali ai byte in uscita. I cifrari a blocchi hanno una dimensione di blocco fissa che elaborano e, a meno che non si utilizzi una modalità di crittografia che converte il codice a blocchi in un codice di flusso, ci sarà un riempimento che rende il testo cifrato più lungo del testo in chiaro.

+1

Il FlushFinalBlock() viene chiamato nella sezione "chiusura" della istruzione using

using(stream) { // } // calls Close() -> FlushFinalBlock()
cambierò il CipherMode, ho appena entrato come un esempio in modo da sapere che io non inizializzare la mia cifra in qualsiasi modo "strano". I readBytes in sendFile() non sono ancora stati utilizzati, ho dimenticato di rimuoverlo. Ho letto fino alla fine del file, quindi questo non dovrebbe essere il problema qui. Pensavo che il
cipher.Padding = PaddingMode.ISO10126;
si stesse prendendo cura delle imbottiture? Cosa posso cambiare per farlo funzionare? – Patrick

+0

Se il flusso di cifratura è in modalità lettura, il blocco finale andrà perso se lo smaltisci; deve effettivamente leggere la fine del file dal suo stream sorgente sottostante per produrre il blocco finale. –

+0

In risposta a Jeffrey: Se provo a chiamare stream.FlushFinalBlock(), dice NonSupportedException: FlushFinalBlock non può essere chiamato due volte nello stesso flusso. Questo non significa che una fine del file è stata letta (e inviata)? – Patrick

0
cipher.Mode = CipherMode.ECB; 

Argh! Tirare il proprio codice di sicurezza è quasi sempre una cattiva idea.

+1

????? eh? Sta usando Rijndael? questo non è "tira il tuo". Tuttavia, c'è un buon punto da fare, che gli sviluppatori devono fare attenzione a come usano la crittografia. – Cheeso

+0

ECB non riesce qui perché ogni blocco è crittografato in modo indipendente. –

+0

Non importa quale CipherMode utilizzo, ottengo ancora l'eccezione "Lunghezza dei dati ..." ... – Patrick

1

Dopo il commento fatto da Jeffrey Hantin, ho cambiato alcune linee in ReceiveFile a

using (stream) { 
    FileInfo finfo = new FileInfo(transferFile.Path); 
    long position = finfo.Length; 
    while (position < transferFile.Length) { 
     int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
     int read = position < array.Length 
        ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
        : streamSocket.Receive(array, SocketFlags.None); 
     stream.Write(array, 0, read); 
     position += read; 
    } 
} 

->

using (stream) { 
    int read = array.Length; 
    while ((read = streamSocket.Receive(array, read, SocketFlags.None)) > 0) { 
     stream.Write(array, 0, read); 
     if ((read = streamSocket.Available) == 0) { 
      break; 
     } 
    } 
} 

E voilà, lavora (a causa della imbottitura mai così gentile che io didn non preoccuparti di prima). Non sono sicuro di cosa succede se Disponibile restituisce 0 anche se tutti i dati non sono stati trasferiti, ma in questo caso lo proverò più avanti. Grazie per il tuo aiuto Jeffrey!

Saluti.

0

la mia ho appena tolto l'imbottitura e funziona

Commentate questo fuori - cipher.Padding = PaddingMode.ISO10126; Metodo

Problemi correlati