javascriptreactjscryptojs

Upgraded crypto-js from 3.1.9-1 to 4.2.0 in my react frontend project, my old code is throwing malformed UTF-8 error and not working as expected


This is my code and it was working fine with old version of crypto-js. With the upgrade the encrypted string from frontend is not getting decrypted in my java springboot backend. The encrypted string from backend is not getting decrypted in frontend and giving malformed UTF-8 error.

const iterationCount=1000;
const keySize= 128/32;

function generateKey(salt, passPhrase) {
  const key = CryptoJS.PBKDF2(
    passPhrase,
    CryptoJS.enc.Hex.parse(salt),
    { keySize, iterations: iterationCount });
  return key;
}

function encrypt(salt, iv, plainText) {
  const passPhrase = process.env.ENCRYPT_SECRET;
  const key = this.generateKey(salt, passPhrase);
  const encrypted = CryptoJS.AES.encrypt(
    plainText,
    key,
    {
      iv: CryptoJS.enc.Hex.parse(iv),
      mode: CryptoJS.mode.CTR,
      padding: CryptoJS.pad.NoPadding
    });
  return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

function decrypt(salt, iv, cipherText) {
  const passPhrase = process.env.DECRYPT_SECRET;
  const key = this.generateKey(salt, passPhrase);
  const decrypted = CryptoJS.AES.decrypt(
    cipherText,
    key,
    {
      iv: CryptoJS.enc.Hex.parse(iv),
      mode: CryptoJS.mode.CTR,
      padding: CryptoJS.pad.NoPadding
    });
  return decrypted.toString(CryptoJS.enc.Utf8);
}

I tried multiple ways of resolving this but nothing worked so far. Anyone faced a similar issue?


Solution

  • The PBKDF2 default values have changed from version 4.1.1 to 4.2.0, s. here: The default digest has changed from SHA1 to SHA256, the default iteration count from 1 to 250,000.
    Thus, for backward compatibility, the default values from 3.1.9-1 (or 4.1.1) must be applied in the code for version 4.2.0.

    Since the iteration counter is already set in the code, only the digest needs to be explicitly set to SHA1:

    ...
    const key = CryptoJS.PBKDF2(
        passPhrase,
        CryptoJS.enc.Hex.parse(salt),
        { keySize, iterations: iterationCount, hasher: CryptoJS.algo.SHA1 }); // fix
    ...
    

    Security: An iteration count of 1000 is far too low for PBKDF2 these days. Nowadays, a few 100,000 is common. For salt and IV, you should use values that are randomly generated for each encryption (instead of static values).