简体   繁体   中英

PHP mcrypt_generic() method get a different result from C# and Python

I have written a Python version of DES3 encryption method to encrypt the data send to the server, but the server side said that it could not decrypt the requested data.

So I have to check the source code. And found that the encryption result of PHP version are different from Python and C#.

The algorithm used is DES3, Mode: ECB, in such mode, IV is unnecessary. And the padding used in Python and C# is Zeros (that is the padding string consists of bytes set to zero). I do not know how PHP dealing with padding.

The key used is 1234567887654321 , the plain is hello , IV is eight empty space (non-used).

The hex dump of PHP encryption result is

77 2c fe 6e c1 df 71 94

The hex dump of Python and C# are

BC 17 0C 67 7D 06 5C AA

Anybody could tell me what's wrong with the PHP code?

The following are the source code

PHP

function encrypt($plain, $key, $iv) {
    /* Open module, and create IV */
    $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');

    /* Initialize encryption handle */
    if (mcrypt_generic_init($td, $key, $iv) != -1) {

        /* Encrypt data */
        $c_t = mcrypt_generic($td, $plain);
        mcrypt_generic_deinit($td);

        /* Clean up */
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
    }
    return $c_t;
}

Python

def encrypt(text, key, iv=''):
    cypher = DES3.new(key, DES3.MODE_ECB, iv)

    padding_length = (8 - len(text) % 8) % 8
    padding = '\x00' * padding_length
    return cypher.encrypt(text + padding)

C#

public static byte[] Encrypt(byte[] plain, byte[] Key, byte[] IV)
{
    // Create a MemoryStream to accept the encrypted bytes 
    MemoryStream ms = new MemoryStream();
    TripleDES alg = TripleDES.Create(); 

    alg.Key = Key;
    alg.IV = IV;
    alg.Mode = CipherMode.ECB;
    alg.Padding = PaddingMode.Zeros;

    CryptoStream cs = new CryptoStream(ms,
       alg.CreateEncryptor(), CryptoStreamMode.Write);

    // Write the data and make it do the encryption 
    cs.Write(plain, 0, plain.Length);
    cs.Close();

    byte[] cypher = ms.ToArray();
    return cypher;
}

Because of Triple DES, you will should be passing in a 24 byte key. Since you are passing in 16 bytes different implementations use different methods to expand this key to 24 bytes.

It appears that in Python/C# the 16 byte key is extended by cycling/repeating it around to make this 24 bytes. So for example:

1234567887654321

becomes

123456788765432112345678

PHP on the other hand appears to simply pad the key with nulls ( \\x00 ) which you can verify by doing

$output1 = bin2hex(encrypt($str, "1234567887654321", ""));
$output2 = bin2hex(encrypt($str, "1234567887654321\x00\x00\x00\x00\x00\x00\x00\x00", ""));

echo $output1 == $output2;  // true!

If you mimic this Python/C# "cycling" behaviour manually with PHP, you will get a consistent output for all 3 languages:

$output = bin2hex(encrypt($str, "123456788765432112345678", ""));
echo $output; // bc170c677d065caa

Although you can simply extend the 16-byte key to 24-bytes by simple cycling/repeating it, just go for a fully random 24-byte key instead for max security. You can read more here (you want keying option #1)

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