7

As you guys probably know, the extension mcrypt will be deprecated on php 7.1.

I use to maintain a "legacy" application that I want to migrate eventually to this version so I ran the tests and verified that I can't get 100% of coverage anymore, since there's a piece of code that use the following code:

$key = 'sA*(DH';

// initialization vector
$iv = md5(md5($key));
$output = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string,     MCRYPT_MODE_CBC, $iv));

I tried to port this piece of code to openssl_encrypt using this code

$key = md5('sA*(DH');
$iv = md5($key);
echo base64_encode(openssl_encrypt($data, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv));

But I have 2 problems with this:

  1. The IV lenght should be 16 chars (and md5 gives me 32), so I get a PHP Warning
  2. The output it's not the same (even if I truncate to 16 chars)

Anyone had similar problems (or know how to fix it?)

BTW: I'm using the dev master version of PHP (supposed to be 7.1.0 alpha 3).

William Okano
  • 370
  • 1
  • 3
  • 10

3 Answers3

3

Yet another tested solution taking and returning ANSI text to replace Mcrypt function with the openssl_encrypt() and openssl_decrypt():

//Return encrypted string
public function stringEncrypt ($plainText, $cryptKey = '7R7zX2Urc7qvjhkr') {

  $cipher   = 'aes-128-cbc';

  if (in_array($cipher, openssl_get_cipher_methods()))
  {
    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = openssl_random_pseudo_bytes($ivlen);
    $ciphertext_raw = openssl_encrypt(
      $plainText, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext_raw, $cryptKey, $as_binary=true);
    $encodedText = base64_encode( $iv.$hmac.$ciphertext_raw );
  }

  return $encodedText;
}


//Return decrypted string
public function stringDecrypt ($encodedText, $cryptKey = '7R7zX2Urc7qvjhkr') {

  $c = base64_decode($encodedText);
  $cipher   = 'aes-128-cbc';

  if (in_array($cipher, openssl_get_cipher_methods()))
  {
    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = substr($c, 0, $ivlen);
    $hmac = substr($c, $ivlen, $sha2len=32);
    $ivlenSha2len = $ivlen+$sha2len;
    $ciphertext_raw = substr($c, $ivlen+$sha2len);
    $plainText = openssl_decrypt(
      $ciphertext_raw, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
  }

  return $plainText;
}

More read in openssl documentation

Nicolas Bouvrette
  • 4,295
  • 1
  • 39
  • 53
Roman
  • 19,236
  • 15
  • 93
  • 97
1

You should really get out of the habit of using md5 for anything.

$iv = openssl_random_pseudo_bytes(16);
$key = substr(hash('sha256', 'sA*(DH'), 0, 32)

mcrypt_encrypt and openssl_encrypt will not output the same crypttext given the same plaintext and key.

also, mcrypt is deprecated in PHP 7.1, not removed...so you can update to 7.1 without changing from mcrypt to openssl ... but it is a good idea to remove mcrypt in general.

Olivier De Meulder
  • 2,493
  • 3
  • 25
  • 30
croaten
  • 21
  • 1
  • Thanks for the answer, but the original code is not mine and I really don't use md5 for nothing actually. Gonna try this, thanks. – William Okano Nov 28 '16 at 20:40
1

There are 2 problems :

  1. MCrypt uses zero padding while Openssl uses by default PKCS#7
  2. Openssl needs the input string to be of proper length (multiple of block length)

To solve this problems :

  1. add OPENSSL_ZERO_PADDING flag to openssl_encrypt/openssl_decrypt
  2. if input string length is not multiple of block length then append to the input string zero chars "\0" [aka chr(0)];

That being said this should solve the problem:

// key/iv in ASCII binary data, $str base64
function decrypt_stuff($key, $str, $iv) {
    // $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
    $plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
    return $plaintext_dec;
}

// key/iv in ascii binary data, $str ascii
function encrypt_stuff($key, $str, $iv) {
    // $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_CBC, $iv));
    if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
    $ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
    return $ciphertext;
}
Mihai B.
  • 111
  • 4
  • Hi, thanks for you answer. Have you tried with $iv with 32 bytes long? The error I keep getting is that the IV is longer than the 16 bytes required and that the IV will be truncated. – William Okano Apr 04 '18 at 13:24
  • The IV should have the same length with the block (in our case that is 16bytes) ... More details see here : https://security.stackexchange.com/questions/90848/encrypting-using-aes-256-can-i-use-256-bits-iv – Mihai B. Apr 04 '18 at 15:07