Replacing mcrypt_encrypt using MCRYPT_RIJNDAEL_256 with openssl_encrypt

10,034

Solution 1

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

Solution 2

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.

Solution 3

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;
}
Share:
10,034
William Okano
Author by

William Okano

Updated on July 21, 2022

Comments

  • William Okano
    William Okano almost 2 years

    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
    William Okano over 7 years
    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
    William Okano about 6 years
    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.
  • Admin
    Admin about 6 years
    The IV should have the same length with the block (in our case that is 16bytes) ... More details see here : security.stackexchange.com/questions/90848/…