Esta es la respuesta más completa que encomtrarás dar, pero primero estúdiala detenidamente y valora si se adapta a tu proyecto.
Solución:
El IV debe ser aleatorio y único para cada ejecución de su método de encriptación. derivándolo de la key/mensaje o codificarlo de forma rígida no es lo suficientemente seguro. El IV puede generarse dentro de este método, en lugar de pasarse a él, y escribirse en el flujo de salida antes de los datos cifrados.
Al descifrar, el IV se puede leer desde la entrada antes de los datos cifrados.
Al cifrar, genere su IV y agréguelo al texto cifrado (algo como esto)
using (var aes= new AesCryptoServiceProvider()
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
)
var input = Encoding.UTF8.GetBytes(originalPayload);
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
//Prepend IV to data
//tBinaryWriter.Write(iv); This is the original broken code, it encrypts the iv
cipherStream.Write(iv); //Write iv to the plain stream (not tested though)
tBinaryWriter.Write(input);
tCryptoStream.FlushFinalBlock();
string encryptedPayload = Convert.ToBase64String(cipherStream.ToArray());
Al descifrar esto, obtenga los primeros 16 bytes y utilícelos en el flujo criptográfico
var aes= new AesCryptoServiceProvider()
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
;
//get first 16 bytes of IV and use it to decrypt
var iv = new byte[16];
Array.Copy(input, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
//Decrypt Cipher Text from Message
binaryWriter.Write(
input,
iv.Length,
input.Length - iv.Length
);
return Encoding.Default.GetString(ms.ToArray());
Modifiqué su método de descifrado de la siguiente manera y funciona:
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
using (var provider = new AesCryptoServiceProvider())
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (var sr = new StreamReader(cs))
return sr.ReadToEnd();
El problema con su implementación es que está leyendo demasiados bytes en el CryptoStream
. Realmente necesitas leer encryptedText.Length - 16
. Usando un StreamReader
simplifica esto, ya que ya no necesita preocuparse por las compensaciones en ningún lugar.