Mensaje de error
Malformed UTF-8 data
CryptoJS — thrown by .toString(CryptoJS.enc.Utf8) after decrypt
Qué significa
CryptoJS descifró el texto cifrado en bytes que no son UTF-8 válido, y la conversión a cadena falló. Como el AES de CryptoJS (modo CBC) no tiene comprobación de integridad, una clave incorrecta no hace fallar el descifrado: solo produce bytes aleatorios, y el decodificador UTF-8 es lo primero que lo nota. La misma clave incorrecta también puede dar una cadena vacía en lugar de este error (cuando la comprobación del relleno falla limpiamente por casualidad), así que ambos síntomas tienen un mismo diagnóstico.
Por qué ocurre
Clave o frase de contraseña incorrecta
frecuenteDescifrar con una clave distinta de la que se usó para cifrar produce una salida pseudoaleatoria. El síntoma que obtengas depende de la longitud del mensaje: en nuestra prueba, aproximadamente uno de cada ocho descifrados con la clave incorrecta lanzó este error con un mensaje de 29 bytes, cerca de la mitad con uno de 100 bytes y todos con uno de 300 bytes; casi todos los demás devolvieron "". En cualquier caso, la sospechosa es la clave.
Derivación de claves distinta en cada lado
frecuenteCryptoJS.AES.encrypt(msg, "passphrase") deriva la clave y el IV mediante una KDF compatible con OpenSSL con una sal aleatoria. Si el otro lado trata la frase de contraseña como una clave en bruto (o si otra plataforma descifra sin esa KDF), las claves derivadas difieren y la salida es basura.
Texto cifrado dañado durante el transporte
ocasionalUn Base64 estropeado por la codificación URL (el + se convierte en un espacio), un truncamiento al guardarlo o una doble codificación cambian los bytes del texto cifrado; el descifrado CBC de bytes alterados produce basura sin ningún error en la capa criptográfica.
Cómo solucionarlo
- 1.
Hacer explícito y simétrico el manejo de la clave
Evita la ambigüedad de la KDF basada en la frase de contraseña: pasa una clave real como WordArray y un IV explícito, de la misma forma en ambos lados.
js const key = CryptoJS.enc.Hex.parse(hexKey); // 32-byte key, both sides const iv = CryptoJS.enc.Hex.parse(hexIv); // 16-byte IV, both sides const ct = CryptoJS.AES.encrypt(plaintext, key, { iv }); const pt = CryptoJS.AES.decrypt(ct.toString(), key, { iv }) .toString(CryptoJS.enc.Utf8); - 2.
Tratar la salida vacía como el mismo fallo
Comprueba el resultado y lanza un error de verdad: las cadenas vacías silenciosas ocultan durante meses los errores por clave incorrecta.
js const pt = CryptoJS.AES.decrypt(ct, key, { iv }).toString(CryptoJS.enc.Utf8); if (!pt) throw new Error("decryption failed: wrong key/IV or corrupted ciphertext"); - 3.
Preferir un esquema autenticado
El AES-GCM de WebCrypto autentica el texto cifrado: una clave incorrecta provoca un fallo visible y determinista en lugar de producir basura, lo que elimina por completo esta clase de error.
js const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct); // wrong key → OperationError, never silent garbage