Search code examples
javascriptphpcryptojsphp-openssl

Match PHPs openssl_encrypt with blank IV in JavaScript


How can I match the output of openssl_encrypt in JavaScript, when no IV is declared in PHP and a non-compliant key length is used?

PHP

php -r '$value = openssl_encrypt("test", "AES-128-CBC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); echo $value;'

/u+5lB/VwbMX9U1YY4cnCQ==

JavaScript

iv  = CryptoJS.enc.Utf8.parse("");
var encrypted = CryptoJS.AES.encrypt("test", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
console.log(encrypted.toString());

U2FsdGVkX19Cn1f6x8C/rJdfwzsZk5m5WWCUrR4z3U4=

console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));

l1/DOxmTmblZYJStHjPdTg==

I can only modify the Javascript code and I need a way to encrypt a string so that it can be decrypted by PHP's openssl_decrypt. It works fine obviously if the string it's encrypted from PHP.

I understand that by not declaring a IV renders the code less secure, but in my particular case it's not a big issue.

I've read in other topics that PHP defaults the padding to Pkcs7, which is why I added it to the JS method.

My current theory is that either the default IV of PHP is the issue or the output of the JS function needs to be further processed. As you can see I tried the Base64.stringify method, but the results are still different.

The sample key I'm using here is of the same length of the actual key.

I'm using https://github.com/sytelus/CryptoJS/blob/master/rollups/aes.js (whatever I use in JS, it needs to be easily distributed like a standalone file, with a relatively small footprint)


Solution

  • There are some issues with your code:

    • If you want to use an existing key, then you need to provide a WordArray object to CryptoJS.<cipher>.encrypt. That means it has to be parsed in some way. If you simply provide a string as a "key", then CryptoJS will assume that it is a password, generate a random salt and use EVP_BytesToKey to derive the key and IV from password and salt.
    • Your "key" is 29 characters long. AES only supports three key sizes: 16, 24 and 32 bytes. Since you've used AES-128 in PHP, you need to provide a 16 byte key in CryptoJS so that AES-128 is automatically selected. Remember: a key is supposed to be randomly chosen and be indistinguishable from random noise so that it has some kind of security. If you must print the key in some way, use Hex or Base64 encoding.

    Full example:

    var iv  = CryptoJS.enc.Utf8.parse("");
    var key = CryptoJS.enc.Utf8.parse("aaaaaaaaaaaaaaaa");
    var encrypted = CryptoJS.AES.encrypt("test", key, {
        iv: iv, 
        mode: CryptoJS.mode.CBC, 
        padding: CryptoJS.pad.Pkcs7
    });
    
    console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>