15

Since Mcrypt is deprecated, I want to use OpenSSL instead in my code since we already using php 7.0.17 in our server and there's no tell when they upgrade it.

Some third party API (hosted on PHP 5.x probably and using mcrypt), is taking encrypted data. They've provided methods which they are using to encrypt/decrypt strings.

Here are they

$secret = 'a0a7e7997b6d5fcd55f4b5c32611b87c' ;

public function encrypt128($str)
    {
        $block = mcrypt_get_block_size("rijndael_128", "ecb");
        $pad   = $block - (strlen($str) % $block);
        $str .= str_repeat(chr($pad), $pad);

        return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $secret, $str, MCRYPT_MODE_ECB));
    }

public function decrypt128($str)
    {
        $str = base64_decode($str);
        $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $secret, $str, MCRYPT_MODE_ECB);

        $len = strlen($str);
        $pad = ord($str[$len - 1]);

        return substr($str, 0, strlen($str) - $pad);
    }

using these methods string small1 if encrypted becomes v7IXp5vVaFVXXlt/MN8BVw==


We want to use openssl_encrypt in our side such that if we encrypt same string with OpenSSL it must give same results as Mcrypt. I've researched that mcrypt using rijndael-128 Mode ecb should be compatible with OpenSSL aes-128-ecb.

For last few hours, I've been trying to make my own method to encrypt strings serving same result by using OpenSSL. So far I've come to this

public function sslEncrypt128($str)
{
    $secret = 'a0a7e7997b6d5fcd55f4b5c32611b87c';
    return base64_encode(openssl_encrypt($str, 'aes-128-ecb', $secret, OPENSSL_RAW_DATA));
}

But it produces different string SxJ3+EdaeItZx3/EwGTUbw== for same as above input. I don't know if it is flag's problem or padding's, any pointers will be welcome.

I've added the code here to test online https://3v4l.org/v2J2N

Thanks in advance.

jww
  • 97,681
  • 90
  • 411
  • 885
Jamshad Ahmad
  • 410
  • 1
  • 6
  • 16
  • 1
    ECB mode has some serious security problems. Be **very** sure that it is appropriate for your specific application. Generally CBC mode or CTR mode will be more secure. See [Electronic Codebook](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.290) for an illustration (literally) of the problem. – rossum Jul 20 '17 at 15:25
  • Also see [Upgrading my encryption library from Mcrypt to OpenSSL](http://stackoverflow.com/q/43329513/608639), [Replace Mcrypt with OpenSSL](http://stackoverflow.com/q/9993909/608639) and [Preparing for removal of Mcrypt in PHP 7.2](http://stackoverflow.com/q/42696657/608639), [Use openssl_encrypt to replace Mcrypt for 3DES-ECB encryption](https://stackoverflow.com/q/39467008/608639) (it uses ECM mode). – jww Jul 20 '17 at 15:30
  • Note: Base64 data is fine for computers but for developers to understand anything from the data a per-byte representation is needed, hex meets that need, Base64 does not. – zaph Jul 20 '17 at 16:51
  • 2
    In your code example I was able to produce the same output in sslEncrypt128 by changing `aes-128-ecb` to `aes-256-ecb`. – Michael Butler Aug 29 '17 at 17:51
  • Thanks @MichaelButler. It solved my problem. `MCRYPT_RIJNDAEL_128` with `MCRYPT_MODE_ECB` mode is same as open ssl `aes-256-ecb`. You can Post your finding as Answer and I will accept it. – Jamshad Ahmad Aug 31 '17 at 09:14

3 Answers3

11

Here is what worked for me:

<?php

$str = 'Content';
if (strlen($str) % 16) {
    $str = str_pad($str, strlen($str) + 16 - strlen($str) % 16, "\0");
}

$key = 'KEY';
if (strlen($key) % 16) {
    $key = str_pad($key, strlen($key) + 16 - strlen($key) % 16, "\0");
}

$res1 = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
echo strToHex($res1) . ' | mcrypt_encrypt';

echo "<hr>";
echo strToHex(openssl_decrypt($res1, "aes-128-ecb", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING)) . ' | openssl_decrypt';

echo "<hr>";

$res2 = openssl_encrypt($str, "aes-128-ecb", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
echo strToHex($res2) . ' | openssl_encrypt';

echo "<hr>";
echo strToHex(openssl_decrypt($res2, "aes-128-ecb", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING)) . ' | openssl_decrypt';


function strToHex($string) {
    $hex = '';
    for ($i = 0; $i < strlen($string); $i++) {
        $ord     = ord($string[$i]);
        $hexCode = dechex($ord);
        $hex     .= substr('0' . $hexCode, -2);
    }

    return strToUpper($hex);
}
Roland Soós
  • 3,125
  • 4
  • 36
  • 49
8

In your specific example I've found that by changing aes-128-ecb to aes-256-ecb, it produces the same output as the legacy mcrypt_encrypt.

Michael Butler
  • 6,079
  • 3
  • 38
  • 46
  • 2
    I believe Mcrypt provides *block sizes* of 128, 192 and 256 bits. AES only provides the 128 block size. My guess is, `MCRYPT_RIJNDAEL_128` is probably referring to Rijndael with 128-bit block size, which is AES. The key size is a different matter, and that is what the `256` denotes in OpenSSL's `AES-256-CBC`. If using a Standard Cryptographic Algorithm Name (SCAN), then the Mcrypt algorithm name for the cipher instance would be similar to `Rijndael-128(256)/CBC`. – jww Feb 15 '18 at 09:00
2

Most likely the key was expected to be used as hex (it already is in hex format) not as a string to be converted to hex.


mcrypt:

mcrypt does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding but the padding is being explicitly added prior to mcrypt.

The encryption v7IXp5vVaFVXXlt/MN8BVw== is the correct encryption based on PKCS#7 padding. ECB mode and the key as a string.

See: mcrypt - AES CALCULATOR.

In hex, notice the data padding is clearly visible:
key: 6130613765373939376236643566636435356634623563333236313162383763
data: 736D616C6C310A0A0A0A0A0A0A0A0A0A
encrypted: BFB217A79BD56855575E5B7F30DF0157

In Base64:
encrypted: v7IXp5vVaFVXXlt/MN8BVw==


OpenSSL:

Notice the key is 256-bits but the OpenSSL call with "aes-128-ecb" seems to imply a 128-but key. So the keys don't match.

See: OpenSSL - AES CALCULATOR

In hex, notice the data padding is clearly visible:
key: 61306137653739393762366435666364
data: 736D616C6C310A0A0A0A0A0A0A0A0A0A
encrypted: 4B1277F8475A788B59C77FC4C064D46F

In Base64:
encrypted: SxJ3+EdaeItZx3/EwGTUbw==

zaph
  • 111,848
  • 21
  • 189
  • 228