Mensagem de erro
Malformed UTF-8 data
CryptoJS — thrown by .toString(CryptoJS.enc.Utf8) after decrypt
O que significa
O CryptoJS descriptografou o texto cifrado em bytes que não são UTF-8 válido, e a conversão para string falhou. Como o AES do CryptoJS (modo CBC) não tem verificação de integridade, uma chave errada não faz a descriptografia falhar — ela só produz bytes aleatórios, e o decodificador UTF-8 é o primeiro a perceber. A mesma chave errada também pode resultar em uma string vazia em vez desse erro (quando a verificação do padding, por acaso, falha de forma limpa), então os dois sintomas têm o mesmo diagnóstico.
Por que acontece
Chave ou frase secreta errada
comumDescriptografar com uma chave diferente da usada para criptografar produz uma saída pseudoaleatória. O sintoma que você recebe depende do tamanho da mensagem: no nosso teste, cerca de uma em cada oito descriptografias com a chave errada lançou esse erro para uma mensagem de 29 bytes, cerca de metade para uma de 100 bytes e todas para 300 bytes; quase todas as outras retornaram "" — de qualquer forma, a suspeita é a chave.
Divergência na derivação de chave entre os dois lados
comumO CryptoJS.AES.encrypt(msg, "passphrase") deriva chave+IV por uma KDF compatível com o OpenSSL, com um salt aleatório. Se o outro lado trata a frase secreta como uma chave bruta (ou outra plataforma descriptografa sem essa KDF), as chaves derivadas são diferentes e a saída é lixo.
Texto cifrado corrompido no transporte
ocasionalBase64 corrompido pela codificação de URL (o + vira espaço), truncamento no armazenamento ou codificação dupla alteram os bytes do texto cifrado; a descriptografia CBC de bytes alterados produz lixo sem nenhum erro na camada criptográfica.
Como corrigir
- 1.
Tornar o tratamento da chave explícito e simétrico
Evite a ambiguidade da KDF da frase secreta: passe uma chave de verdade como WordArray mais um IV explícito, de forma idêntica nos dois 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 a saída vazia como a mesma falha
Verifique o resultado e exponha um erro real — strings vazias silenciosas escondem bugs de chave errada por meses.
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 um esquema autenticado
O AES-GCM do WebCrypto autentica o texto cifrado: uma chave errada falha de forma explícita e determinística em vez de produzir lixo — o que elimina essa classe de erro por completo.
js const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct); // wrong key → OperationError, never silent garbage