Search code examples
cryptojs

Unable to decrypt AES-256 (CBC) - Malformed Utf-8 data


I've gone through several CrytoJS examples but haven't been able to make decryption work for me. I need to decode something for which a key (13 characters in length, just in case it's important) and iv (16 length after Base64 decode) I've been provided. I found this PHP example that works perfectly (these won't work if you run them - the key has been changed, of course :) ):

$encrypted = urldecode('6x5Mex1DqqFK0Z9F%2F6W1FNB55UM%2Baoyjw6NgTwDDmHpVytJXNyQsA3J8jh3R4qt5MbKITzIORGLtgmfknUNLaBhBaBO%2F8GiFPT8A6wVgtQ6YOXkJHG8S9VKYgrN9MCeT0e8mbWMNfkkJ0ips4K87ZNzC6Lb5dbT1%2BRMw%2BpmCP7M8%2FwiBMdJZ%2FdINblg%2FapHtaqv6TUBRukB%2FEJWgGFB86h5fJ8VNZKssWtx35gLo6IHai6GTQpyS%2FNeq5ZbX%2BJwcnCKOzudHsdAyxk%2BQYltupFNBXBzxxW%2Fiixtew7uNtRJQfy2epcx0SOqRGKQfJrRReFWvldJWSKz%2Bv0aRMdZWCVRHovcwPLdXNWSK%2Brs7G0LCnjji4RyqzU9DTy%2BijA1Xx3xVmOxXysCkvqXNg2P0LJ0O0qLJF9lfQb%2FrrPH3h3lB0a7737CTzQh5kuqK%2FNabfV8qLNyV6cJ5oQWMfXw4LRxDIeK9Me8Fnb2K44uC39Q%3D');

$iv = urldecode('MkQ4NUFGNkVBNjIwMEY5OQ%3D%3D');

$secretKey = "ABCDEFGHIJKL";

// decrypt the body... 
$decrypted = trim(
    mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
        substr(sha1($secretKey), 0, 32),
        base64_decode($encrypted),
        MCRYPT_MODE_CBC, 
        base64_decode($iv)
    ), "\0..\32");

print("Decrypted: $decrypted");

And now I'm trying to do something similar in CryptoJS, but keep getting the error Malformed Utf-8 data:

var encrypted = decodeURIComponent('6x5Mex1DqqFK0Z9F%2F6W1FNB55UM%2Baoyjw6NgTwDDmHpVytJXNyQsA3J8jh3R4qt5MbKITzIORGLtgmfknUNLaBhBaBO%2F8GiFPT8A6wVgtQ6YOXkJHG8S9VKYgrN9MCeT0e8mbWMNfkkJ0ips4K87ZNzC6Lb5dbT1%2BRMw%2BpmCP7M8%2FwiBMdJZ%2FdINblg%2FapHtaqv6TUBRukB%2FEJWgGFB86h5fJ8VNZKssWtx35gLo6IHai6GTQpyS%2FNeq5ZbX%2BJwcnCKOzudHsdAyxk%2BQYltupFNBXBzxxW%2Fiixtew7uNtRJQfy2epcx0SOqRGKQfJrRReFWvldJWSKz%2Bv0aRMdZWCVRHovcwPLdXNWSK%2Brs7G0LCnjji4RyqzU9DTy%2BijA1Xx3xVmOxXysCkvqXNg2P0LJ0O0qLJF9lfQb%2FrrPH3h3lB0a7737CTzQh5kuqK%2FNabfV8qLNyV6cJ5oQWMfXw4LRxDIeK9Me8Fnb2K44uC39Q%3D');
var iv = decodeURIComponent('MkQ4NUFGNkVBNjIwMEY5OQ%3D%3D');


var encrypted = CryptoJS.enc.Base64.parse(encrypted);
var ive = CryptoJS.enc.Base64.parse(iv);

var secretOne = "CLICKBANKINFS";
var secretTwo = CryptoJS.SHA1(secretOne);
var secretThree = secretTwo.toString().substring(0,32);

var decrypted = CryptoJS.AES.decrypt(encrypted, secretThree, 
    { 
        iv: ive,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }
);

console.log(decrypted.toString(CryptoJS.enc.Utf8));

The encrypted text and iv are both Base64 encoded. I'm not sure how this is to be handled exactly. I've seen several implementations of CryptoJS decryption and have tried several combinations but nothing worked for me.

Some help, please? :)


Solution

  • Change this:

    var secretThree = secretTwo.toString().substring(0, 32));
    
    var decrypted = CryptoJS.AES.decrypt(encrypted, secretThree, 
    

    To:

    var secretThree = CryptoJS.enc.Utf8.parse(secretTwo.toString().substring(0, 32)));
    
    var decrypted = CryptoJS.AES.decrypt({ciphertext: encrypted}, secretThree, 
    
    1. The first parameter has to be an object or a base64-encoded JavaScript string. Passing just encrypted makes the decrypt call silently fail. You can either do what I have above or remove the following line from your to leave the ciphertext as a base64-encoded JavaScript string:

      var encrypted = CryptoJS.enc.Base64.parse(encrypted);
      
    2. JavaScript in general has very strange support for byte arrays, so the CryptoJS wrappers are required for everything to work properly. Even secretThree, which is entirely ASCII [0-9a-f].