oaep - encriptacion aes c#
¿El relleno no es válido y no se puede eliminar? (11)
He buscado en línea qué significa esta excepción en relación con mi programa, pero no puedo encontrar una solución o la razón por la que está sucediendo en mi programa específico. He estado usando el ejemplo proporcionado por mi msdn para encriptar y descifrar un XmlDocument usando el algoritmo de Rijndael. El cifrado funciona bien, pero cuando intento descifrar, obtengo la siguiente excepción:
El relleno no es válido y no se puede quitar
¿Alguien puede decirme qué puedo hacer para resolver este problema? Mi código a continuación es donde obtengo la clave y otros datos. Si cryptoMode es falso, llamará al método de descifrado, que es donde ocurre la excepción:
public void Cryptography(XmlDocument doc, bool cryptographyMode)
{
RijndaelManaged key = null;
try
{
// Create a new Rijndael key.
key = new RijndaelManaged();
const string passwordBytes = "Password1234"; //password here
byte[] saltBytes = Encoding.UTF8.GetBytes("SaltBytes");
Rfc2898DeriveBytes p = new Rfc2898DeriveBytes(passwordBytes, saltBytes);
// sizes are devided by 8 because [ 1 byte = 8 bits ]
key.IV = p.GetBytes(key.BlockSize/8);
key.Key = p.GetBytes(key.KeySize/8);
if (cryptographyMode)
{
Ecrypt(doc, "Content", key);
}
else
{
Decrypt(doc, key);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Clear the key.
if (key != null)
{
key.Clear();
}
}
}
private void Decrypt(XmlDocument doc, SymmetricAlgorithm alg)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("Doc");
if (alg == null)
throw new ArgumentNullException("alg");
// Find the EncryptedData element in the XmlDocument.
XmlElement encryptedElement = doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;
// If the EncryptedData element was not found, throw an exception.
if (encryptedElement == null)
{
throw new XmlException("The EncryptedData element was not found.");
}
// Create an EncryptedData object and populate it.
EncryptedData edElement = new EncryptedData();
edElement.LoadXml(encryptedElement);
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml();
// Decrypt the element using the symmetric key.
byte[] rgbOutput = exml.DecryptData(edElement, alg); <---- I GET THE EXCEPTION HERE
// Replace the encryptedData element with the plaintext XML element.
exml.ReplaceData(encryptedElement, rgbOutput);
}
A tiempos de guerra serios, finalmente resolví el problema.
(Nota: uso AES estándar como algoritmo simétrico. Esta respuesta puede no ser adecuada para todos).
- Cambiar la clase de algoritmo. Reemplace la clase
RijndaelManaged
aAESManaged
one. - No establezca explícitamente el
KeySize
de la clase de algoritmo, los dejó predeterminados.
(Este es el paso muy importante. Creo que hay un error en la propiedad KeySize).
Aquí hay una lista que desea verificar qué argumento puede haber perdido:
- Llave
(matriz de bytes, la longitud debe ser exactamente uno de 16, 24, 32 bytes para diferentes tamaños de clave). - IV
(matriz de bytes, 16 bytes) - CipherMode
(Uno de CBC, CFB, CTS, BCE, OFB) - PaddingMode
(Uno de ANSIX923, ISO10126, Ninguno, PKCS7, Ceros)
Asegúrese de que las claves que utiliza para cifrar y descifrar son las mismas . El método de relleno, incluso si no se ha establecido explícitamente, debería permitir el descifrado / cifrado adecuado (si no se establece, será el mismo). Sin embargo, si por algún motivo utiliza un conjunto diferente de claves para el descifrado que las utilizadas para el cifrado , obtendrá este error:
El relleno no es válido y no se puede quitar
Si está utilizando algún algoritmo para generar claves dinámicamente que no funcionarán. Deben ser iguales tanto para el cifrado como para el descifrado. Una forma común es hacer que la persona que llama proporcione las claves en el constructor de la clase de métodos de cifrado, para evitar que el proceso de cifrado / descifrado tenga alguna mano en la creación de estos elementos. Se enfoca en la tarea en cuestión (encriptación y descifrado de datos) y requiere que la persona que llama proporcione la key
iv
y la key
.
Encontré este error de relleno cuando editaba manualmente las cadenas encriptadas en el archivo (usando el bloc de notas) porque quería probar cómo se comportaría la función de descifrado si mi contenido cifrado se alterara manualmente.
La solución para mí fue colocar un
try
decryption stuff....
catch
inform decryption will not be carried out.
end try
Como dije, mi error de relleno fue porque estaba tipeando manualmente el texto descifrado usando el bloc de notas. Puede ser mi respuesta puede guiarlo a su solución.
La solución que solucionó el problema fue que inadvertidamente apliqué diferentes claves a los métodos de cifrado y descifrado.
Me encontré con este error al intentar pasar una ruta de archivo no encriptada al método Decrypt. La solución fue comprobar si el archivo pasado se cifra primero antes de intentar descifrar
if (Sec.IsFileEncrypted(e.File.FullName))
{
var stream = Sec.Decrypt(e.File.FullName);
}
else
{
// non-encrypted scenario
}
Mi problema era que la contraseña del cifrado no coincidía con la contraseña del descifrado ... por lo que arrojó este error ... un poco engañoso.
Otro escenario, una vez más para el beneficio de las personas que buscan.
Para mí, este error ocurrió durante el método Dispose () que enmascaró un error anterior no relacionado con el cifrado.
Una vez que el otro componente fue reparado, esta excepción desapareció.
Para el beneficio de las personas que buscan, puede valer la pena verificar la entrada que se descifra. En mi caso, la información que se enviaba para el descifrado entraba (erróneamente) como una cadena vacía. Resultó en el error de relleno.
Esto puede estar relacionado con la respuesta de Rossum, pero pensó que valía la pena mencionarla.
Rijndael / AES es una cifra de bloque. Encripta los datos en bloques de 128 bits (16 caracteres). El relleno criptográfico se usa para asegurarse de que el último bloque del mensaje sea siempre del tamaño correcto.
El método de descifrado espera el relleno predeterminado y no lo encuentra. Como dice @NetSquirrel, debe establecer explícitamente el relleno tanto para el cifrado como para el descifrado. A menos que tenga una razón para hacer lo contrario, use el relleno PKCS # 7.
Si se utilizan la misma clave y el mismo vector de inicialización para codificar y decodificar, este problema no proviene de la decodificación de datos, sino de la codificación de datos.
Después de llamar al método Write en un objeto CryptoStream, SIEMPRE debe llamar al método FlushFinalBlock antes del método Close.
La documentación de MSDN en el método CryptoStream.FlushFinalBlock dice:
" Llamar al método Close llamará a FlushFinalBlock ... "
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs.110).aspx
Esto está mal. El método Calling Close simplemente cierra el CryptoStream y el flujo de salida.
Si no llama a FlushFinalBlock antes de Cerrar después de escribir datos para encriptar, al descifrar datos, una llamada al método Read o CopyTo en su objeto CryptoStream generará una excepción CryptographicException (mensaje: "El relleno no es válido y no se puede eliminar").
Esto es probablemente cierto para todos los algoritmos de cifrado derivados de SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES), aunque acabo de verificar que para AesManaged y MemoryStream como salida Stream.
Por lo tanto, si recibe esta excepción CryptographicException en el descifrado, lea el valor de la propiedad de la Longitud del flujo de salida después de que haya escrito sus datos para cifrarlos, luego llame a FlushFinalBlock y vuelva a leer su valor. Si ha cambiado, usted sabe que llamar a FlushFinalBlock NO es opcional.
Y no necesita realizar ningún relleno mediante programación, o elegir otro valor de propiedad de relleno. El relleno es el trabajo del método FlushFinalBlock.
.........
Comentario adicional para Kevin:
Sí, CryptoStream llama a FlushFinalBlock antes de llamar a Close, pero ya es demasiado tarde: cuando se llama al método CryptoStream Close, la transmisión de salida también se cierra.
Si su flujo de salida es un MemoryStream, no puede leer sus datos después de que se cierre. Entonces, debe llamar a FlushFinalBlock en su CryptoStream antes de usar los datos encriptados escritos en MemoryStream.
Si su flujo de salida es un FileStream, las cosas son peores porque la escritura está almacenada en el búfer. La consecuencia es que los últimos bytes escritos pueden no escribirse en el archivo si cierra la secuencia de salida antes de llamar a Flush en FileStream. Entonces, antes de llamar a Close on CryptoStream, primero debe llamar a FlushFinalBlock en su CryptoStream y luego llamar a Flush en su FileStream.
Yo tenía el mismo error. En mi caso, fue porque he almacenado los datos cifrados en una base de datos SQL. La tabla en la que se almacenan los datos tiene un tipo de datos binario (1000). Al recuperar los datos de la base de datos, descifraría estos 1000 bytes, mientras que en realidad habría 400 bytes. Entonces, eliminando los cero finales (600) del resultado, se solucionó el problema.