Trouble using CryptoJS parsing from decrypted format to string with most padding

I just cannot get anything to work the way I want with CryptoJS. I am trying to create inter-language operability for AES with Pksc7 padding ideally & CBC mode. BUT at this point I'll take almost any padding if it works. Now I'm not sure if I'm doing something wrong or if there is something wrong with my setup or CryptoJS. I have a somewhat large test im running. In the end I need a string output for my decrypted text but I'm not getting that. This VERY frustrating!

I have been up and down the docs many times and tried so many things so far with no usable output, see code below for trouble.

Here are some posts I've looked over for help this and this

Also I am running this in my Parse server cloud code.

Example from above posts works but does not decode back to string, also it only works with no padding- a no go for me...

Any help would really help my wits right now :)

 var CryptoJS = require("crypto-js");
  const keygen = require('secure-random-password');

/// MY EXAMPLE I WOULD LIKE TO GET WORKING - FROM HERE

    var keygenKey = keygen.randomPassword({length: 32, characters: [keygen.lower, keygen.upper, keygen.digits] });
    var keygenIV = keygen.randomPassword({length: 16, characters: [keygen.lower, keygen.upper, keygen.digits] });
    
    var key = CryptoJS.enc.Hex.parse("keygenKey");
    var iv = CryptoJS.enc.Hex.parse("keygenIV");
    var plainText = CryptoJS.enc.Hex.parse("Hello world");

    var encrypted = await CryptoJS.AES.encrypt(plainText, key, {
      // format: JsonFormatter, // Tried the json formatter from docs - made no difference
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    }).ciphertext.toString();

    var decrypted = await CryptoJS.AES.decrypt({ciphertext: encrypted}, key, {
      // format: JsonFormatter, // Tried the json formatter from docs - made no difference
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });

/// MY EXAMPLE I WOULD LIKE TO GET WORKING - TO HERE
    
    // Working - but I need padding so this is no good
    const examplePlain =  CryptoJS.enc.Hex.parse("Hello world");
    const exampleKey  = CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
    const exampleIV   = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f");
    const exampleEncrypted = CryptoJS.AES.encrypt(examplePlain, exampleKey, {iv: exampleIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding}).ciphertext.toString();
    const exampleDecrypted = CryptoJS.AES.decrypt({ciphertext: exampleEncrypted}, exampleKey, {iv: exampleIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding});
    
    var utf8String = decrypted.toString(CryptoJS.enc.Utf8);
    var hex = CryptoJS.enc.Hex.stringify(decrypted).toString();
    var words = CryptoJS.enc.Utf8.parse(decrypted);
    var utf8 = CryptoJS.enc.Utf8.stringify(decrypted);

    return {
      "keygenIV": keygenIV,
      "keygenKey": keygenKey,
      "encrypted": encrypted.toString(),
      "decrypted": decrypted,
      "test_hex": hex,
      "test_words": words,
      "test_utf8": utf8,
      "test_utf8String": utf8String,
      "example_encrypted": exampleEncrypted,
      "example_decrypted": exampleDecrypted.toString()
  }

The garbage I'm getting back

{
keygenIV: 23bfHtf9nmGSXg9G, 
keygenKey: B9PFLTwFC87z4WePzkcJLCMNvUnaXinV, 
encrypted: 95a3d7b2cd0bf5f35271afa9af359d5e, 
decrypted: {words: [487551153, 1344444817, 251806218, 1966320389, 418267123, -2081063279, -2093335598, 1651281808], sigBytes: -112, $super: {}}, 
test_hex: , 
test_words: {words: [], sigBytes: 0, $super: {}}, 
test_utf8: , 
test_utf8String: , 
example_encrypted: 0378aaa355b9, 
example_decrypted: 417e2f4a1f6192bf7b1a2ab3
}

Info about garbage:

{
keygenIV: 23bfHtf9nmGSXg9G, // Perfect
keygenKey: B9PFLTwFC87z4WePzkcJLCMNvUnaXinV, // Perfect
encrypted: 95a3d7b2cd0bf5f35271afa9af359d5e, //Perfect

// Can't figure out a way to get this object formatted as string
decrypted: {words: [487551153, 1344444817, 251806218, 1966320389, 418267123, -2081063279, -2093335598, 1651281808], sigBytes: -112, $super: {}},

test_hex: , 
test_words: {words: [], sigBytes: 0, $super: {}}, 
test_utf8: , 
test_utf8String: , 
example_encrypted: 0378aaa355b9, // Whatever...

example_decrypted: 417e2f4a1f6192bf7b1a2ab3 // This does not decode by utf8 I get error: Malformed UTF-8 data
}

Solution 1:

There are some encoding bugs in the code:

  • keygen.randomPassword() does not return a hex encoded string, nevertheless the hex encoder is used for parsing. A hex encoded key and IV can be generated e.g. as follows:

    var keygenKey = keygen.randomPassword({length: 64, characters: '0123456789abcdef' }); // 32 bytes key
    var keygenIV = keygen.randomPassword({length: 32, characters: '0123456789abcdef' }); // 16 bytes IV
    

    Also, when parsing, the quotation marks must be removed:

    var key = CryptoJS.enc.Hex.parse(keygenKey);
    var iv = CryptoJS.enc.Hex.parse(keygenIV);
    

    Note that CryptoJS supports the generation of a random key and IV, so secure-random-password is actually not necessary:

    var key = CryptoJS.lib.WordArray.random(32);
    var iv = CryptoJS.lib.WordArray.random(16);
    
  • The plaintext is also parsed with the hex encoder, here the Utf8 encoder must to be applied:

    var plainText = CryptoJS.enc.Utf8.parse("Hello world");
    
  • In the decrypt() call the ciphertext is passed hex encoded, instead a WordArray must be passed:

    var decrypted =  CryptoJS.AES.decrypt({ciphertext: CryptoJS.enc.Hex.parse(encrypted)},...
    

With these fixes the code works:

console.log({
    "keygenKey": keygenKey,
    "keygenIV": keygenIV,
    "encrypted": encrypted,
    "decrypted": decrypted.toString(CryptoJS.enc.Utf8),
});

gives for instance

{
  keygenKey: '10b361a633bfbc163d312d9fd52730d36d19f4db08a7a14481637eaeb662d175',
  keygenIV: 'bbfd9b4361a1d6f2bd2a81db1dd7e81b',
  encrypted: 'c0771e1f8146ddaf82ea2198e6a2749e',
  decrypted: 'Hello world'
}