Reputation: 566
I am migrating all the functions of my project that encrypt data and work with mcrypt to openssl.
Making tests I find that, encrypting the same data with the same keys I get different results.
When decrypting I get the correct result with boths functions; the problem is that I share this information with an external provider and only successfully decrypt the data if I encrypt with mcrypt.
This is the test code:
// Configuration.
$data = 'FOO';
$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';
// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data), MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);
// Encrytp & decrypt with openssl.
$encOpenSSL = bin2hex(openssl_encrypt(utf8_encode($data), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv));
$decOpenSSL = openssl_decrypt(hex2bin($encOpenSSL), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv);
// Result.
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSL <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "openssl decrypt: $decOpenSSL";
Result:
mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: b2f4b9aeb07e1ca4
mcrypt decrypt: FOO
openssl decrypt: FOO
Does anyone know why it is because they get different results?
Thank you!
Upvotes: 2
Views: 3054
Reputation: 49141
The difference is that mcrypt_encrypt
/ mcrypt_decrypt
uses Zero-Padding and openssl_encrypt
/ openssl_decrypt
uses PKCS7-Padding. This can be easily verified by applying Zero-Padding for openssl
: For this, PKCS7-Padding must be disabled with the flag OPENSSL_ZERO_PADDING
(important: despite the name, this flag doesn't mean that Zero-Padding is used, but that no padding is applied at all), and the plaintext must be padded with 0
-values to the next integer multiple of the blocksize (8
bytes for Triple-DES) unless the length already corresponds to an integer multiple of the blocksize:
<?php
function zeroPadding($data, $size) {
$oversize = strlen($data) % $size;
return $oversize == 0 ? $data : ($data . str_repeat("\0", $size - $oversize));
}
// Something is wronguration.
$data = 'FOO';
$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';
// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data), MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);
// Encrytp & decrypt with openssl.
$encOpenSSLZeroPadding = bin2hex(openssl_encrypt(utf8_encode(zeroPadding($data, 8)), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv));
$decOpenSSLZeroPadding = openssl_decrypt(hex2bin($encOpenSSLZeroPadding), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
// Result.
echo "data padded: " . bin2hex(zeroPadding($data, 8)) . "<br>";
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSLZeroPadding <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "mcrypt decrypt: " . bin2hex($decMcrypt) . "<br>";
echo "openssl decrypt: $decOpenSSLZeroPadding" . "<br>";
echo "openssl decrypt: " . bin2hex($decOpenSSLZeroPadding);
with the following output:
data padded: 464f4f0000000000
mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: 3f9bd8d5f844ff67
mcrypt decrypt: FOO
mcrypt decrypt: 464f4f0000000000
openssl decrypt: FOO
openssl decrypt: 464f4f0000000000
Instead of using a Zero-Padding in the openssl
-context, a PKCS7-Padding could be used in the mcrypt
-context. No matter which of the two variants is used, with identical padding mcrypt
and openssl
results are identical!
It should be noted (see the hexadecimal output) that mcrypt
doesn't remove the Zero-Padding during decryption (unlike openssl
which removes the PKCS7-Padding during decryption). Also, Zero-Padding is unreliable compared to PKCS7-Padding. If the requirements allow it (which in your case is probably not the case because of the external provider), PKCS7-Padding should therefore be used.
Furthermore, both posted variants use the same algorithm, namely Triple-DES in CBC-mode. Triple-DES has a blocksize of 8
bytes and a keysize of 24
bytes. Triple-DES is not identical to DES, but is based on DES in the sense that it consists of three DES-runs (encryption-decryption-encryption = ede). mcrypt
specifies Triple-DES/CBC with two parameters, MCRYPT_3DES
(Triple-DES) and MCRYPT_MODE_CBC
(CBC-mode), while openssl
uses only one parameter, DES-EDE3-CBC
.
There are several Keying-Options for Triple-DES. 3TDEA
uses three independent DES keys and is specified in the context of openssl
with DES-EDE3-CBC
, which expects a 24
bytes key, 2TDEA
uses two independent keys and can be specified in the context of openssl
alternatively with DES-EDE-CBC
, which expects a 16
bytes key.
Triple-DES is much slower than the modern AES, but has a comparable security. As with padding, you should switch to AES if possible.
Upvotes: 3
Reputation: 11
Mcrypt and openssl are two different ciphers, so they encrypt and decrypt data in different ways.
You can use openSSL's des-ede3 method, however you should really be using AES: Security Comparison of AES and DES
If your external provider is using mcrypt_decrypt to decrypt the data you send them, then they won't be able to decrypt data that is encrypted with openssl, and vice-versa.
Your external provider will need to change the way the decrypt the data as well if you move to the more secure AES openSSL encryption.
(on a side note, it's good that you're moving to openssl - mcrypt is abandonware circa php 7.1)
Upvotes: 1
Reputation: 2049
As I see it, both methods use different algorithms, see this question. You should try and use des-ede3
as openssl cipher method, which is the equivalent of mcrypt's MCRYPT_3DES
.
Upvotes: 1