Search code examples
phpencryptionopenssldesecb

PHP openssl_decrypt doesn't get same results as online tools


Using an online encrypt/decrypt tool, using DES-ECB, I can encrypt an 8 digit hexadecimal number using an 8 digit hexadecimal key, resulting in an 8 digit hex result. I can decrypt that 8 digit result by the same key and get the original data I encrypted.

However, I cannot reproduce this locally using PHP. The encrypted result I get online, it turns out, is the first 8 of 16 digits actually produced. No problem... But when I try to decrypt locally using PHP, I need all 16 digits in order to get the original data.

How can I decrypt with only the 8 digits and still get the original data, like the online tool does?

Online results:

Encrypting:

$data = '03 67 A6 7F C2 00 0A DB';
$key = '00 F2 83 CD BA 41 6F FF';
$result = '8b be 0f 3b ae 92 56 07';

Verify: http://des.online-domain-tools.com/link/1b40d6agZYE0TFR5sM/

Decrypting:

$data = '8b be 0f 3b ae 92 56 07';
$key = '00 F2 83 CD BA 41 6F FF';
$result = '03 67 A6 7F C2 00 0A DB';

Verify: http://des.online-domain-tools.com/link/1b40e05gD5TNgMb72h/

Local PHP test:

$enc = openssl_encrypt( hex2bin('0367A67FC2000ADB'), 'DES-ECB', hex2bin('00F283CDBA416FFF'), 1);

bin2hex($enc) results in 8bbe0f3bae9256071da486ee680f8449

If I decrypt only the first 8 hex digits, I don't get the same results I do with the online tool:

$dec = openssl_decrypt( hex2bin('8bbe0f3bae925607'), 'DES-ECB', hex2bin('00F283CDBA416FFF'), 1);

bin2hex($dec) results in null (or false if we do not convert to hex)

But if I enter the full 16 digit hex as the encrypted data, I get the correct result:

$dec = openssl_decrypt( hex2bin('8bbe0f3bae9256071da486ee680f8449'), 'DES-ECB', hex2bin('00F283CDBA416FFF'), 1);

bin2hex($dec) results in 0367A67FC2000ADB

This makes sense to me... but I need to be able to get this result from only the 8 digit hex, just like the online tool does. What do I need to do to make this possible?


Solution

  • The reason for the difference is the padding. A block cipher only allows the encryption of data whose length corresponds to an integer multiple of the blocksize (8 bytes in the case of DES). Padding ensures that this condition is met by adding data according to a specific logic.

    There are different types of padding. openssl_encrypt/openssl_decrypt uses PKCS7-padding by default, the online-tool Zero-padding.

    PKCS7-padding will always add data to the plaintext, even if the plaintext already has a length that is an integer multiple of the blocksize. In this case, a complete block is added (details). For this reason, the posted ciphertext generated with openssl_encrypt has a length of 2 blocks (16 bytes) for a plaintext with a length of 1 block (8 bytes).

    Unlike PKCS7-padding, the Zero-padding variant used by the online-tool doesn't add any data if the plaintext already has a length that is a multiple of the blocksize. For this reason, the posted ciphertext generated with the online-tool has a length of 1 block (8 bytes) for a plaintext with a length of 1 block (8 bytes).

    openssl_encrypt/openssl_decrypt doesn't support Zero-padding. For the ciphertext of openssl_encrypt to match that of the online-tool, the PKCS7-padding must be disabled and the Zero-padding variant used by the online-tool must be implemented manually. The disabling of the padding is done with the flag OPENSSL_ZERO_PADDING, which must be set with the fourth parameter. Note: The name of the flag is misleading: This flag doesn't enable Zero-padding, but only disables PKCS7-padding. This means that the Zero-padding variant of the online-tool still has to be implemented manually. Furthermore: In the current code, the value 1 is passed in the fourth parameter, which corresponds to the flag OPENSSL_RAW_DATA. To set both flags, the value 1 must therefore be replaced by OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING.

    For plaintexts whose length already corresponds to an integer multiple of the blocksize, both a disabled PKCS7-padding and the Zero-padding variant used by the online-tool don't add any additional data, so that the ciphertexts match. In this case, a manual implementation of the Zero-padding variant used by the online-tool isn't necessary.

    A final note on security: DES is insecure. Today's standard is AES. ECB is also insecure. More secure modes are e.g. CBC or GCM.