Search code examples
phpperlencryption3des

Same return value in php and perl 3DES CBC


I would like to know how to return the same values for a 3DES encription in perl and php. The PHP code is the following:

$bytes = array(0,0,0,0,0,0,0,0);
$iv = implode(array_map("chr", $bytes)); 
$ciphertext = mcrypt_encrypt(MCRYPT_3DES, base64_decode('Mk9m98IfEblmPfrpsawt7BmxObt98Jev'), '0000001920', MCRYPT_MODE_CBC, $iv);
echo base64_encode($ciphertext);

The result is: "A/VCTXA6q/x/emW0zzlSDg=="

The perl code is:

use Crypt::CBC;
$cipher = Crypt::CBC->new(  -key    => decode_base64('Mk9m98IfEblmPfrpsawt7BmxObt98Jev'),
                                -cipher => 'DES_EDE3',
                                -iv     => pack("H*","0000000000000000"),
                                -literal_key => 1,
                                -header      => 'none'
                            );

$ciphertext = $cipher->encrypt("0000001920");
print encode_base64($ciphertext, '');

The result is: "A/VCTXA6q/y9g7ypgqlWIg=="

The results are very similar, what I'm doing wrong in my perl code?


Solution

  • You are not using the same padding mechanism.

    If you read the reference page of PHP's mcrypt_encrypt very carefully you'll see the following note next to the data parameter:

    If the size of the data is not n * blocksize, the data will be padded with '\0'.

    Now, if you also read the reference page of Perls Crypt::CBC, you notice they have several padding methods (defined by the -padding parameter). The default is PKCS#5 which is something different than just padding with 0x00.

    Now if you change the padding to "null", Perl prints the same as PHP. So your code should look like this:

    use MIME::Base64;
    use Crypt::CBC;
    $cipher = Crypt::CBC->new(  -key    => decode_base64('Mk9m98IfEblmPfrpsawt7BmxObt98Jev'),
                                    -cipher => 'DES_EDE3',
                                    -iv     => pack("H*","0000000000000000"),
                                    -padding     => "null",
                                    -literal_key => 1,
                                    -header      => 'none'
                                );
    
    $ciphertext = $cipher->encrypt("0000001920");
    print encode_base64($ciphertext, '');
    

    Alternatively you could implement PKCS#5 in PHP, which you have to do by yourself, since PHP does not include it by default (search for PHP PKCS#5 and you probably find some code-snippets which you can use, theres even a simple PKCS#5 function in one of the PHP-doc comments).