Search code examples
javascriptphpencryptionmcryptcryptojs

Javascript AES Encryption returning too many characters


I am trying to implement a PHP encryption script into JavaScript. My PHP script returns a 128 character string, while my Javascript based one returns 160 characters. The first 128 characters of the JavaScript based version match the PHP based version.

function pkcs5_pad ($text, $blocksize){
  $pad = $blocksize - (strlen($text) % $blocksize);
  return $text . str_repeat(chr($pad), $pad);
}
$skey = "somekey";
$ivKey = "someIVKey";
$input = "empid=xxxxxx;timestamp=Sat, 19 Nov 2016 00:33:03 UTC";

try {
  $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,'cbc');
  echo strlen($input) . "\n";
  $input = pkcs5_pad($input, $size);
  echo strlen($input) . "\n";
  $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

  $key = pack('H*', $skey);
  $iv = pack('H*', $ivKey);
  # The key size used is 16, 24 or 32 bytes - for AES-128, 192 and 256 respectively

  if (mcrypt_generic_init($cipher, $key, $iv) != -1){
    $encrypted = mcrypt_generic($cipher, $input);
    mcrypt_generic_deinit($cipher);
    $encryptedString = bin2hex($encrypted);
  }
  echo $encryptedString . "\n";
  echo strlen($encryptedString) . "\n";
} catch (Exception $ex) {
  echo $ex->getMessage();
}

The length strlen($encryptedString) here gives me 128 characters.

My JavaScript based version uses CryptoJS to create looks like this

var aesKey = "somekey";
var ivKey = "someIVKey";

function pkcs5_pad (text, blocksize){
  console.log(text.length);
  var pad = blocksize - (text.length % blocksize);
  console.log("pad:" + pad);
  return text + str_repeat(chr(pad), pad);
}

input = "empid=xxxxxx;timestamp=Sat, 19 Nov 2016 00:33:03 UTC";

var size = 16;
console.log(input.length);
var input = pkcs5_pad(input, size);
console.log('"' + input + '"');
console.log(input.length);

var key = CryptoJS.enc.Hex.parse(aesKey);
var iv = CryptoJS.enc.Hex.parse(aesIV);

var encryptedString = CryptoJS.AES.encrypt(input,key,{iv: iv});
console.log(encryptedString.ciphertext.toString().length);
encryptedString = encryptedString.ciphertext.toString();

Everything matches, including the string lenght before and after pkcs5_pad. I'm using some additional JavaScript code from locutus.io to call str_repeat, and chr. Here encryptedString.ciphertext.toString().length returns 160 characters and the first 128 match that of my PHP script.

My understanding is that version 3 of CryptoJS uses CBC mode, but I've also set the mode explicitly to CBC to no avail. I've also returned the encrypted string as hex

encryptedString = encryptedString.ciphertext.toString(CryptoJS.enc.Hex);

Where am I going wrong?

EDIT The output of the PHP version is

86b1c9874069129d0852eade01eb753a176a1c6155c4af3ac447ae0a5350b92c3447f95be9c4f8cdbf14503696bcaa16e6307c1605a2cac503239db9d1ac6fb3

The output of the JavaScript version is

86b1c9874069129d0852eade01eb753a176a1c6155c4af3ac447ae0a5350b92c3447f95be9c4f8cdbf14503696bcaa16e6307c1605a2cac503239db9d1ac6fb33051208849788f8a90db1cbe2494cac7


Solution

  • The extra 32 characters are hex encoding of 16 bytes and that is the padding. The Java is adding padding, the PHP is not.

    Note that mcrypt does not use standard PKCS#7 (née PKCS#5) padding.

    Good encryption libraries will have a padding option and add the padding on encryption and remove it on decryption. You should not have to do your own padding.