¿Cómo puedo cifrar un archivo con .NET y tener el mismo tamaño de archivo que el archivo original?

10

Estoy usando .NET y he escrito una rutina asombrosa (xD) que encripta y desencripta un archivo usando AES256 CBC. Funciona perfectamente, pero ahora me dijeron que el archivo cifrado debe ser del mismo tamaño que el descifrado. (Hay un problema con algunas otras API, no es mi culpa, lo juro).

Entonces, he intentado todo lo que puedo encontrar, pero nada funciona. .NET Framework tiene un CipherMode.CTS que se ve exactamente como lo que necesito, pero desafortunadamente no está soportado actualmente.

Intenté establecer el relleno en ninguno, pero claro que tengo errores porque el tamaño del bloque es menor de lo que se supone que es.

Ideas?

Editar: He logrado resolver este problema de dos maneras:

Con .NET API     SymmetricAlgorithm alg = new RijndaelManaged ();     alg.Mode = CipherMode.CFB;     alg.Padding = PaddingMode.None;     alg.FeedbackSize = 8;

Con las API de BouncyCastle Cifrado IBufferedCipher = nuevo CtsBlockCipher (nuevo CbcBlockCipher (nuevo AesFastEngine ()))

Espero que esto pueda ayudar a otros con el mismo problema :)

    
pregunta Fabio 30.10.2012 - 17:31
fuente

3 respuestas

10

Incluso si usa CTS, todavía necesita un vector de inicialización (IV) que DEBE (Insisto, DEBE ) generarse de nuevo para cada archivo con un generador de números pseudoaleatorios criptográficamente sólido. Por lo tanto, no podrá ajustar todo sin aumentar el tamaño. Esto es inevitable, siempre y cuando utilice un cifrado de bloque "normal".

Además, si necesita cifrado, entonces probablemente necesite integridad, es decir, un MAC , que, intrínsecamente, requiere un poco de espacio extra.

Lo que podrías intentar es comprimir el archivo antes de cifrarlo ( System.IO.Compression.DeflateStream ); Esto podría darle el espacio de respiración adicional que necesita (no de una forma garantizada , pero funcionará, especialmente si el archivo que necesita cifrar es un archivo XML o algo así, con una gran cantidad de estructura).

    
respondido por el Thomas Pornin 30.10.2012 - 17:49
fuente
5

Como escribió Thomas, para cifrar de forma segura el archivo, debe tener un IV además del archivo cifrado. Para que un IV sea seguro, debe cumplir dos condiciones:

  1. El mismo IV y el mismo par de teclas no se usan para dos mensajes diferentes.
  2. Un atacante no puede predecir el IV utilizado para un mensaje de su elección cifrado con la misma clave que un mensaje que el atacante quiere descifrar.

Estas dos condiciones generalmente se cumplen mediante el uso de un IV aleatorio diferente por mensaje, lo que en su caso requeriría agregar el IV al archivo cifrado, aumentando así el tamaño del archivo. Pero si ciertas condiciones son ciertas, puede usar las propiedades del archivo (ruta, nombre, fecha y hora) como IV (para no tener que agregar el IV al archivo cifrado). Las condiciones son:

  1. La clave de cifrado es única por máquina. Esto asegura que la misma IV nunca se usa con la misma clave de cifrado, ya que no se puede tener Dos archivos en la misma máquina con las mismas propiedades de archivo.
  2. La clave de cifrado es única para cada usuario. Esto garantiza que un atacante no pueda montar un ataque de texto sin formato elegido para descifrar los archivos de otros usuarios ya que las claves son diferentes.

Si se cumplen estas dos condiciones, puede usar un modo de operación de cifrado de flujo (por ejemplo, CTR, OFB, CFB) y usar las propiedades del archivo como IV.

    
respondido por el David Wachtfogel 30.10.2012 - 19:46
fuente
2

Hay algunas opciones:

  1. Comprueba si TripleDESCryptoServiceProvider admite CTS.
  2. Convierta AES en modo CBC en un cifrado de flujo.

Para el último, simplemente encripte bloques de ceros y xor texto sin formato con el cipherstream resultante:

// work out what the padding length needs to be
int paddingSize = cipher.BlockSize - (message.Length % cipher.BlockSize);
int paddedSize = message.Length + paddingSize;

// build a block of zeros
byte[] zeros = new byte[paddedSize];

// create a keystream from the key and IV, using the zeros as a plaintext
byte[] keystream = EncryptAES(zeros, key, iv);

// produce the ciphertext by xoring the plaintext and the keystream
byte[] ciphertext = new byte[message.Length];
for (int i = 0; i < message.Length; i++)
    ciphertext[i] = message[i] ^ keystream[i];

Esto funciona tanto como el algoritmo de cifrado y descifrado: simplemente introduzca el texto cifrado con la misma clave y IV, y obtendrá el texto llano original. Pero, como señala Thomas Pornin, aún tendrás que enviar un IV único con cada mensaje.

    
respondido por el Polynomial 30.10.2012 - 17:45
fuente

Lea otras preguntas en las etiquetas