2009-06-02 68 views
11

Estoy tratando de cifrar y descifrar un flujo de archivos a través de una toma de corriente utilizando RijndaelManaged, pero sigo chocando con la excepciónLongitud de los datos para descifrar es válido

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) 

La excepción se produce al final de la sentencia using en receiveFile, cuando se ha transferido todo el archivo.

He intentado buscar en la web, pero sólo encontraron respuestas a los problemas que surgen cuando se utiliza codificación el cifrado y descifrado de una sola cadena. Uso un FileStream, por lo que no especifico ninguna codificación para usar, así que ese no debería ser el problema. Estos son mis métodos:

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

Este es el método que utilizo para configurar las cifras. byte [] init es una matriz de bytes generada.

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

¿Alguien tiene una idea de lo que podría estar haciendo mal?

Respuesta

6

A mi me parece como que no va a enviar correctamente el bloque final. Es necesario que al menos FlushFinalBlock() el envío CryptoStream con el fin de asegurar que el bloque final (que el cuerpo receptor está buscando) se envía.

Por cierto, CipherMode.ECB is more than likely an epic fail en términos de seguridad para lo que está haciendo. Al menos use CipherMode.CBC (encadenamiento de bloque de cifrado) que en realidad usa el IV y hace que cada bloque dependa del anterior.

EDIT: Vaya, la corriente de cifrado se encuentra en modo de lectura. En ese caso, debe asegurarse de leer en EOF para que CryptoStream pueda tratar el bloque final, en lugar de detenerse después de readBytes. Probablemente sea más fácil de controlar si ejecuta la secuencia de cifrado en modo de escritura.

Una nota más: No se puede asumir que los bytes de bytes es igual a cabo. Las cifras de bloque tienen un tamaño de bloque fijo que procesan, y a menos que esté utilizando un modo de cifrado que convierta el cifrado de bloque en un cifrado de flujo, habrá un relleno que hace que el texto cifrado sea más largo que el texto sin formato.

+1

El FlushFinalBlock() es llamado en la sección de "cierre" de la instrucción using

using(stream) { // } // calls Close() -> FlushFinalBlock()
voy a cambiar el CipherMode, que acaba de entrar en él como un ejemplo para que sepa que no inicializar mi cifrado en cualquier "Manera rara. Los readBytes en sendFile() aún no se utilizan, olvidé eliminarlos. Leí hasta el final del archivo, por lo que este no debería ser el problema aquí. Pensé que
cipher.Padding = PaddingMode.ISO10126;
se estaba ocupando del relleno? ¿Qué puedo cambiar para que funcione? – Patrick

+0

Si el flujo de cifrado está en modo de lectura, el bloque final se perderá si lo descarta; tiene que leer realmente el final del archivo de su flujo fuente subyacente para producir el bloque final. –

+0

En respuesta a Jeffrey: Si intento llamar a stream.FlushFinalBlock(), dice NonSupportedException: FlushFinalBlock no se puede invocar dos veces en la misma transmisión. ¿Esto no significa que se ha leído (y enviado) un final de archivo? – Patrick

0
cipher.Mode = CipherMode.ECB; 

Argh! Rodar su propio código de seguridad casi siempre es una mala idea.

+1

????? ¿eh? Él está usando Rijndael? esto no es "tira tu propio". Sin embargo, hay un buen punto por hacer, que los desarrolladores deben tener cuidado sobre cómo usan el cifrado. – Cheeso

+0

BCE falla aquí porque cada bloque se cifra independientemente. –

+0

No importa qué CipherMode use, todavía obtengo la excepción "Longitud de datos ..." – Patrick

1

Después de la observación hecha por Jeffrey Hantin, he cambiado algunas líneas en 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; 
     } 
    } 
} 

y listo, ella trabaja (debido al relleno tan amable que yo no 'no importa preocuparse acerca de antes). No estoy seguro de qué sucede si Available devuelve 0 aunque no se hayan transferido todos los datos, pero tendré más adelante en ese caso. Gracias por su ayuda Jeffrey!

Atentamente.

0

mina i acaba de quitar el relleno y funciona

Comentadas esto - cipher.Padding = PaddingMode.ISO10126; Método

Cuestiones relacionadas