Search code examples
javascriptphpencryptioncryptojsphp-openssl

CryptoJS and openssl_decrypt not produce same result


I am trying to implement AES 256 bit encryption with string on php and JavaScript. For jasvascript I a musing CryptoJS and php I use openssl_decrypt/enecrypt.

Below is the code in JS for encryption and decryption.

JavaScript

function aes_encrypt(str_to_encrypt){
  if(str_to_encrypt==null)
   return "";

   var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
   var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");


  var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
  var encryptedString = encrypted.toString();
  return  encryptedString;
}

function aes_decrypt(str_to_decrypt){
  if(str_to_decrypt==null)
   return "";

   var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
   var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");

  var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
  var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
  return decryptedString;
}

And in php the code is

PHP

class Crypto_AES256
{
  public $key = "0123456789abcdef0123456789abcdef";
  public $iv =  "abcdef9876543210abcdef9876543210";


  public  $encrypt_method = 'AES-256-CBC';

  function __construct ()
  {
     $this->key = hex2bin($this->key);
     $this->iv = hex2bin($this->iv);
    
  }

  public function encrypt ( $string )
  {
    if ( $encrypted = base64_encode( openssl_encrypt ( $string, $this->encrypt_method, $this->key, 0, $this->iv ) ) )
    {
      return $encrypted;
    }
    else
    {
      return false;
    }
  }
  public function decrypt ($string)
  {

    if ( $decrypted = openssl_decrypt ( base64_decode ( $string ), $this->encrypt_method, $this->key, 0, $this->iv ) )
    {
      return $decrypted;
    }
    else
    {
      return false;
    }
  }
}

But the result of encryption at JavaScript side is not same as php, I need to produce same encrypted and encrypted result at both JavaScript and php. What could be the problem.


Solution

  • Both codes differ in two ways:

    • The PHP code applies AES-256, but since only a 16 bytes key is used (because of the hex decoding), PHP automatically pads it with 0 values to a length of 32 bytes. In the CryptoJS code, the key length determines the mode, thus AES-128 is applied. So that both codes produce the same result, the key must be extended in the CryptoJS code analogously to the PHP code, or AES-128 must be used in the PHP code.
    • In the PHP code, openssl_encrypt() returns the ciphertext Base64 encoded by default, so the ciphertext is currently Base64 encoded twice. Therefore remove the explicit base64_encode() or use OPENSSL_RAW_DATA as the 4th parameter so that the raw data is returned. Similarly for openssl_decrypt().

    When these issues are fixed, both codes provide the same ciphertext on my machine. Note that a static IV is insecure (see also the comment), but you probably only do this for testing purposes.


    Example: The following code uses your unmodified CryptoJS code, i.e. AES-128:

    function aes_encrypt(str_to_encrypt){
        if(str_to_encrypt==null)
            return "";
    
        var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
        var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
    
        var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
        var encryptedString = encrypted.toString();
        return  encryptedString;
    }
    
    function aes_decrypt(str_to_decrypt){
        if(str_to_decrypt==null)
            return "";
    
        var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
        var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
    
        var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
        var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
        return decryptedString;
    }
    
    var ciphertext = aes_encrypt('The quick brown fox jumps over the lazy dog');
    var decrypted = aes_decrypt(ciphertext);
    console.log(ciphertext.replace(/(.{56})/g,'$1\n'));
    console.log(decrypted);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

    The PHP code returns the same ciphertext if AES-128-CBC is applied and if the OPENSSL_RAW_DATA flag is set as the 4th parameter in openssl_encrypt() and openssl_decrypt().