1

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?

Barmar
  • 741,623
  • 53
  • 500
  • 612
Jeremy Caris
  • 117
  • 1
  • 8
  • Little confusion there, `56` is a byte! So you need to use all. – kelalaka Oct 31 '19 at 00:26
  • How does this work? http://des.online-domain-tools.com/link/1b40e05gD5TNgMb72h/ How can I reproduce this? – Jeremy Caris Oct 31 '19 at 00:28
  • 1
    The cause is the [padding](https://en.wikipedia.org/wiki/Padding_(cryptography)). `openssl_encrypt/decrypt` uses PKCS7-padding, the Online-tool Zero-padding. For a complete solution, PKCS7-padding must be disabled and Zero-padding must be implemented _manually_. However, as long as the plaintext is an integer multiple of the blocksize (8 bytes for DES), it's sufficient to just disable the padding. To do this, the value of the 4th parameter must be changed from `1` to `OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING`. Note: `OPENSSL_ZERO_PADDING` disables padding, but doesn't enable Zero-padding. – Topaco Oct 31 '19 at 09:16
  • @Topaco Thank you! That is very helpful. I updated to the following and it works perfectly: ```$enc = openssl_encrypt( hex2bin('0367A67FC2000ADB'), 'DES-ECB', hex2bin('00F283CDBA416FFF'), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);``` and ```$dec = openssl_decrypt( hex2bin('8bbe0f3bae925607'), 'DES-ECB', hex2bin('00F283CDBA416FFF'), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);``` – Jeremy Caris Oct 31 '19 at 13:56
  • If you write you're comment in as an answer, I'll accept it as the answer @Topaco. – Jeremy Caris Oct 31 '19 at 13:59

1 Answers1

1

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.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • I'm not actually using it for security, per se. I can't really explain further because of a non-disclosure agreement, but your answer filled in the gaps for me and allowed me to accomplish the task. Many thanks! – Jeremy Caris Oct 31 '19 at 22:30