简体   繁体   中英

Encrypt using 3DES and PHP's openssl_encrypt()

I'm trying to implement 3DES in PHP (v7.4.6) using openssl_encrypt() but am having problems generating the correct ciphertext output. (I am aware that 3DES is insecure, but I must use it because the legacy system I'm connecting to requires it.)

Sample key:

fa21b161b659afec7633e08ea5ef54aaddd8728f5d3f7b97

Sample plaintext input:

9/3/2013 9:26:21 PM|100|john.smith@clubsoftpayments.com|John|Smith|1234.56

And expected ciphertext output:

18E4719B4DC9C4E0581964C16B0077609B58717B465106A235AEC353467EC2A370AF97F3CFBB68B6E4005AF1178FE926418E49E55ECEFE794E28E8FC2DED4AFC077883B6D2517BD542F116408588B0B6

The tripledes.online-domain-tools.com website correctly encodes the sample plaintext using "ECB (electronic codebook)" mode, resulting in the following ciphertext (shown as hex):

18E4719B4DC9C4E0581964C16B0077609B58717B465106A235AEC353467EC2A370AF97F3CFBB68B6E4005AF1178FE926418E49E55ECEFE794E28E8FC2DED4AFC077883B6D2517BD5D00067BFA921ABC0

Even though The last 8 bytes ( 42F116408588B0B6 ) differ from the expected ciphertext, both ciphertexts can be successfully decoded by the same website to the original plaintext:

Encode: http://tripledes.online-domain-tools.com/link/2b65dfdgf4MoJSND32/

Decode: http://tripledes.online-domain-tools.com/link/2b68348glGkJ0h3ejQ/

Attempting to encrypt the plaintext using PHP's openssl_encrypt() , I am calling it as follows:

$ciphertext_raw = openssl_encrypt(
    '9/3/2013 9:26:21 PM|100|john.smith@clubsoftpayments.com|John|Smith|1234.56', // plaintext data
    'des-ecb', // algorithm
    hex2bin('fa21b161b659afec7633e08ea5ef54aaddd8728f5d3f7b97'), // Key must be provided as plaintext, hence the call to hex2bin()
);
$ciphertext_hex = bin2hex($ciphertext_raw);

The resulting (hex encoded) ciphertext is:

6e7267444579666f6b7338795334614347626949455a544347316d3768732f7730784b6552375945344e466f67463677737a6b786e463863506b79466c3941593956734579705939356c754d6b794a76347345424b302f47735a623972575543395a4f4f7857456e34744d3d

What is wrong with the arguments I'm passing to openssl_encrypt() ?

  • Algorithm:

Since http://tripledes.online-domain-tools.com worked using "ECB (electronic codebook)" mode, I assume I should be using a cipher method that indicates both "des" and "ecb". openssl_get_cipher_methods() lists the following algorithms with "des" in their names:

des-cbc
des-cfb
des-cfb1
des-cfb8
des-ecb
des-ede
des-ede-cbc
des-ede-cfb
des-ede-ofb
des-ede3
des-ede3-cbc
des-ede3-cfb
des-ede3-cfb1
des-ede3-cfb8
des-ede3-ofb
des-ofb
desx-cbc
id-smime-alg-CMS3DESwrap

The only algorithm containing both "des" and "ecb" is "des-ecb".

  • options :

I've tried every bitwise combination of OPENSSL_RAW_DATA , OPENSSL_ZERO_PADDING , and OPENSSL_NO_PADDING , to no avail.

  • Zero-padding the data:

I also tried adding two null bytes to the plaintext by appending str_repeat("\x00", 2) to make the length a multiple of the 8-byte block size. (Reference: https://www.php.net/manual/en/function.openssl-encrypt.php#121545 ) This did not help.

  • iv (Initialization Vector):

I'm not sure if I need an Initialization Vector since I didn't need to provide one to http://tripledes.online-domain-tools.com and it didn't return one to me when it encrypted the sample. Maybe I'm wrong?

  • tag , aad , and tag_length :

As far as I can tell, none of these is applicable to 3DES so I am leaving them unset.

How can I get openssl_encrypt() to encode the sample plaintext, using the sample key, to generate the desired ciphertext?

I'm aware of Cannot find the right 3DES cipher in PHP and the phpcrypt project on GitHub. While the GitHub library works, I'm trying to figure out how to do this just with openssl_encrypt() if possible.

The desired ciphertext (including end) is produced with the following PHP code:

<?php
// Encryption
$ciphertext_raw = openssl_encrypt(
    '9/3/2013 9:26:21 PM|100|john.smith@clubsoftpayments.com|John|Smith|1234.56', 
    'des-ede3', // des-ede3-ecb
    hex2bin('fa21b161b659afec7633e08ea5ef54aaddd8728f5d3f7b97'), 
    OPENSSL_RAW_DATA
);
$ciphertext_hex = bin2hex($ciphertext_raw);
print($ciphertext_hex . PHP_EOL); // 18e4719b4dc9c4e0581964c16b0077609b58717b465106a235aec353467ec2a370af97f3cfbb68b6e4005af1178fe926418e49e55ecefe794e28e8fc2ded4afc077883b6d2517bd542f116408588b0b6

// Decryption
$decrypted = openssl_decrypt(
    $ciphertext_raw, 
    'des-ede3', // des-ede3-ecb
    hex2bin('fa21b161b659afec7633e08ea5ef54aaddd8728f5d3f7b97'), 
    OPENSSL_RAW_DATA
);
print($decrypted . PHP_EOL); // 9/3/2013 9:26:21 PM|100|john.smith@clubsoftpayments.com|John|Smith|1234.56
?>

Explanation:

  • Since a 24 bytes key is applied, Triple DES (aka 3DES aka TDES) is used in the 3TDEA or triple-length keys variant. OpenSSL refers to this variant as des-ede3 (or des-ede3-ecb ). The mode used is ECB. The des-ecb you apply, however, specifies DES (in ECB mode). Even though Triple DES is based on DES, they are ultimately different algorithms.
  • The 4th parameter in openssl_encrypt() has the default value 0 , which means that the ciphertext is Base64 encoded during encryption. During decryption a Base64 decoded ciphertext is expected. Since the ciphertext in your example should not be Base64 encoded during encryption, the flag OPENSSL_RAW_DATA must be set.
  • OpenSSL uses PKCS#7 padding by default. This provides the expected end of the ciphertext. In contrast, the referenced web site applies Zero padding , which results in the different end of the ciphertext.

Security:
You have already pointed out that Triple DES is deprecated. Today's standard is AES. But ECB is also insecure , because this mode does not use an IV. More secure is a mode with an IV (like CBC ), even better authenticated encryption (eg GCM ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM