2011-02-22 15 views
6

Soy bastante nuevo en C#, así que tenga paciencia conmigo. Sé que esta pregunta se hizo mucho si las veces, pero no pude encontrar una respuesta a mi problema.C#: error de AES: el relleno no es válido y no se puede eliminar. La misma clave y todo, ayuda

Estoy guardando algunos datos y antes de escribirlo en un archivo lo convierto en binario y lo almaceno en una matriz, que cifro y luego escribo en un archivo. Encripto los datos en fragmentos (32 bytes). De la misma manera, leo los datos en fragmentos de 32 bytes y luego los descifro y luego los repito hasta el final del archivo. Pero cuando se trata de descifrado el siguiente error se lanza:

Padding is invalid and cannot be removed.

Puedo usar el mismo iv llave y (hardcoded sólo hasta que lo consigo trabajo)

Aquí está mi código de encriptación, que funciona sin problemas:

 //result 
     byte[] data = new byte[32]; 

     //setup encryption (AES) 
     SymmetricAlgorithm aes = Aes.Create(); 
     byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9,50}; 
     byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 }; 
     ICryptoTransform encryptor = aes.CreateEncryptor(key, iv); 

     FileStream fStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false); 

     //prepare data to write (byte array 'data') ... 

     //encrypt 
       MemoryStream m = new MemoryStream(); 
       using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write)) 
        c.Write(data, 0, data.Length); 
       data = m.ToArray(); 
       fStream.Write(data, 0, data.Length); 

Y aquí es mi código de descifrado:

FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false); 

      //setup encryption (AES) 
      SymmetricAlgorithm aes = Aes.Create(); 
      byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 }; 
      byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 }; 
      ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); 

      //result 
      byte[] data = new byte[32]; 

      //loop for reading the whole file ... 
      int len = fStream.Read(data, 0, 32); 

      //decrypt 
       MemoryStream m = new MemoryStream(); 
       using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write)) 
        c.Write(data, 0, data.Length); //The exception is thrown in this line     
       data = m.ToArray(); 

       //using the decrypted data and then looping back to reading and decrypting... 

intenté todo lo que pude t Hink of (que no es mucho porque soy muy nuevo en la criptografía), busqué en todas partes y no pude encontrar una solución a mi problema. También me ayudé con el libro C# en Nutshell.

Si alguien tiene ideas sobre por qué esto podría suceder, estaré muy agradecido porque no tengo ideas.

Gracias por su tiempo y sus respuestas.

EDIT: Parece que el tamaño de los datos cifrados es de 48 bytes (12 bytes más que el original). ¿Por qué es así? Pensé que solo agrega bytes si no son un múltiplo del tamaño del bloque (16 bytes, mis datos son 32 bytes). Los datos son siempre más grandes y con un aumento constante (necesito saberlo para leer y descifrar correctamente).

Nota: No puedo usar directamente otras transmisiones porque necesito tener control sobre el formato de salida y creo que también es más seguro y más rápido encriptar en la memoria.

+0

Creo que Ben está aquí, se puede comprobar el archivo para ver la salida de los datos encriptados (es decir, es el mayor archivo binario de 32 bytes?). Querrá leer todo el archivo y descifrarlo. – SwDevMan81

+0

otro @Ben: 48 bytes es 16 bytes más que su entrada, no 12. ¿Qué le impide simplemente usar la longitud de la secuencia cifrada? Solo debería tomar un par de bytes adicionales para guardar la longitud en cualquier envoltura/envoltura de estructura/envoltura en la que estén incrustados los datos cifrados. –

+0

@ SwDevMan81 Comprobé la matriz después del cifrado y justo antes de escribir en el archivo y tenía 48 bytes de tamaño, sí, 16 más, no 12 =). No puedo porque a veces los archivos pueden ser muy grandes y necesito solo una porción especial de datos de una ubicación que voy a calcular y, por lo tanto, debo ser capaz de descifrar el archivo en forma de fragmento. @Ben Voigt, si siempre (para el mismo tamaño de porción) crea el mismo "incremento" en el tamaño, entonces puedo adaptarme, pero encuentro un desperdicio de espacio ya que estas cosas se suman rápidamente. – Ben

Respuesta

6

Basado en su edit:

EDIT: It seems that the size of the encrypted data is 48 bytes (12 bytes more than the original). Why is that so? I thought that it only adds bytes if they are not a multiple of the block size (16 bytes, my data is 32 bytes). Is data always larger, and with constant increase (I need to know that in order to properly read and decrypt).

Si los datos cifrados es de 48 bytes, de que es 16 bytes más grande que su matriz original. Esto tiene sentido porque el algoritmo con pad los datos porque el valor predeterminado es PKCS7 (incluso si el tamaño coincide con el tamaño del bloque, ya que rellena el siguiente múltiplo del tamaño de bloque). Si desea mantener exactamente 32 bytes, sólo cambia el Padding a None

aes.Padding = PaddingMode.None; 
+0

Muchas gracias, esto resolvió mi problema. ¿Así que siempre se ajusta solo hasta el próximo múltiplo de tamaño de bloque? Porque 'KeithS' publicó: _Si su mensaje no es un múltiplo par de 16 bytes ..._ ¿Tiene que ser par? Por cierto, es simplemente aes.Padding = ... – Ben

+0

@Ben - Se rellenará hasta el siguiente tamaño de bloque, por lo que siempre se va a agregar si está configurado. Actualicé el código, gracias. Me alegro de esto ayudó. – SwDevMan81

+0

Bueno, parece que me olvidé de ejecutar este método y parecía que estaba funcionando, pero de hecho no lo es, sigue siendo el mismo problema. También depuré de nuevo y los datos aún se expanden a 48 bytes. – Ben

3

Parece que está tratando la longitud del texto sin formato como la longitud del texto cifrado. Esa no es una suposición segura.

¿Por qué está copiando entre FileStream y MemoryStream, se puede pasar un FileStream directamente al encriptador/desencriptador.

En PKCS7, hay un mínimo de un byte de relleno (para almacenar el número de bytes de relleno). Por lo tanto, el tamaño de salida será Ceil16(input.Length + 1) o (input.Length & ~15) + 1.

+0

Bueno, en su algoritmo está desplegando su propia matriz de bytes de exactamente 32 bytes, que es un múltiplo par del tamaño del bloque, por lo que el texto cifrado coincidirá exactamente con la matriz de bytes de entrada. El problema es que para hacerlo, ha hecho su propio "relleno", lo que confunde el paso de descifrado. – KeithS

+0

@Keith: ¿Y está absolutamente seguro de que el encriptador no genera un bloque de encabezado extra ni nada de eso? –

+0

Entonces, si creara una matriz de 31 bytes, ¿podría pasar con 32 bytes después del descifrado? – Ben

1

En resumen, AES encripta los mensajes en bloques de 16 bytes. Si su mensaje no es un múltiplo par de 16 bytes, el algoritmo debe ser un poco diferente para el último bloque; específicamente, el último bloque debe ser "rellenado" con un valor conocido por el algoritmo como un valor de relleno (generalmente cero, a veces algo más como un valor de carácter de espacio).

Lo hace usted mismo, al colocar los datos en una matriz de bytes de longitud fija. Ha rellenado los datos usted mismo, pero el desencriptador ahora está intentando eliminar el último bloque y obtener valores de bytes que no reconoce como el relleno que habría agregado su homólogo de cifrado.

La clave no es para rellenar el mensaje. Puede usar la clase BitConverter para generar matrices de bytes desde y hacia IConvertible types (tipos de valores y cadenas), y luego usar eso en lugar de rodar su propia matriz de bytes. Luego, cuando descifre, puede leer desde la secuencia de descifrado hasta la longitud del texto cifrado, pero no espere que haya tantos bytes reales en el resultado descifrado.

Cuestiones relacionadas