2

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.

CodeDezk
  • 1,230
  • 1
  • 12
  • 40
  • 1
    1. Do not hardcode IVs, they should be generated dynamically per-input and distributed alongside the ciphertext. 2. Without example data there's not a whole lot that anyone can do other than offer guesses. – Sammitch Feb 09 '21 at 20:11

1 Answers1

3

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().

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thanks for the feedback, now the issue has resolved. I extended the key length to 32 on php and JS as I have to use 256 bit key. And I have one doubt about iv, that if static iv is insecure, do I need to pass system generated iv to decryption function. My ultimate purpose is store the data(string) in DB in encrypted(by php or JS) format. Later access it and decrypt(from JS) for further use. – CodeDezk Feb 10 '21 at 09:49
  • Usually a _random_ IV is generated for _each_ encryption. The IV is not secret and is sent unencrypted to the recipient along with the ciphertext. So that only one value has to be sent, both data are commonly concatenated in the order _IV | ciphertext_ on byte level. Since the length of the IV is known (corresponds to the block size, i.e. 16 bytes for AES), the receiver can separate IV and ciphertext and perform the decryption. – Topaco Feb 10 '21 at 10:02
  • That means `aes_encrypt()` will return IV+ciphertext, and I can store it in DB, and while decrypt I have to pass only encrypted data from DB, not IV separately. – CodeDezk Feb 10 '21 at 14:40
  • 1
    _That means aes_encrypt() will return IV+ciphertext, and I can store it in DB:_ yes, _and while decrypt I have to pass only encrypted data from DB, not IV separately:_ no, for decryption, e.g. with `aes_decrypt()`, IV+ciphertext is passed from the DB to `aes_decrypt()`, there IV and ciphertext are separated and then decryption is performed. – Topaco Feb 10 '21 at 14:57