Search code examples
phpperlopensslcryptblowfish

PHP blowfish cbc VS Perl Crypt


I'm encrypting client numbers in PHP using openssl_encrypt.

$value = '01715034842';
$key = 'pi3kn3W@k@cj3';                
$iv = 'Toy@dtv!';
$cipher = 'bf-cbc';

$crypted = openssl_encrypt($value, $cipher, $key, true, $iv);
$hashValue = unpack('H*',$crypted);

The result is: 0b6b81176ac7c298ebcb294f0a581539

Also my friend programmed the other part in Perl. And he also encoded the same number using same keys and using Blowfish to (he is using Perl library: https://metacpan.org/pod/release/LDS/Crypt-CBC-2.30/CBC.pm ):

use Crypt::CBC;
use Crypt::Blowfish;


## szyfrowanie
my $key = 'pi3kn3W@k@cj3';
my $iv = 'Toy@dtv!';

my $cipher = Crypt::CBC->new(   -key    => $key,
                                -iv => $iv,
                                -header => 'none',
                                -cipher => 'Blowfish'
                            );
sub mkHash {
        my  $crypt = $cipher->encrypt_hex($_[0]);
#        print 'Hash: '.$crypt."\n";
        return $crypt;
}


sub deHash {
        my $crypt = $cipher->decrypt_hex($_[0]);
       # print 'string: '.$crypt."\n";
        return $crypt;
}

my $clientHash = mkHash($smc);

And getting a different result for the same set of data: c5377bcf0f55af641709c35928350576

So we can't use this language. Does it depend on programming language differences? Is this is a bug in my code or language? I think that when using the same set of data and the same encrypting (BlowFish CBC) we should get the same results in every language.

Looking forward for opinion on this case.


Solution

  • The working scripts

    The following PHP and Perl scripts show how to achieve the same output for the two languages. I will explain some of the details below that.

    PHP:

    $value = '01715034842';
    $cipher = 'bf-cbc';
    $key = '12345678901234567890123456789012345678901234567890123456';
    $option = OPENSSL_RAW_DATA;
    $iv = 'Toy@dtv!';
    
    $crypted = openssl_encrypt($value, $cipher, $key, $option, $iv);
    echo($crypted)
    

    Perl:

    use Crypt::CBC;
    use Crypt::Blowfish;
    
    my $value = '01715034842';
    my $key = '12345678901234567890123456789012345678901234567890123456';
    my $iv = 'Toy@dtv!';
    
    my $cipher = Crypt::CBC->new(   -literal_key => 1,
                                    -key         => $key,
                                    -iv          => $iv,
                                    -header      => 'none',
                                    -cipher      => 'Blowfish'
                                );
    
    my $crypted = $cipher->encrypt($value);
    print $crypted;
    

    Using diff on the two outputs results in no difference, showing they are the same:

    $ diff <(php encrypt.php) <(perl encrypt.pl)
    $
    

    Details explaining the required changes

    The following sections explain the required changes, compared to your original code.

    Encryption key

    The PHP openssl_encrypt() function always expects a raw key. The bytes that you give it, are the bytes used as the encryption key. The Perl CBC class on the other hand expects a passphrase by default, from which it will derive the encryption key by doing an MD5 hash. If you want the class to use your raw bytes as the encryption key, you have to set the parameter literal_key to 1.

    After you have done that, the CBC class expects the key to be the exact number of bytes needed for the encryption scheme, which the CBC class assumes to be 56 for the Crypt::Blowfish implementation. Hence the adjusted key in the scripts. The error you will get otherwise is If specified by -literal_key, then the key length must be equal to the chosen cipher's key length of 56 bytes

    Output format

    The PHP openssl_encrypt() function by default returns a base64 encoded string, the CBC class returns the raw bytes. One way to make this consistent is by setting the OPENSSL_RAW_DATA option in PHP.

    Inspecting the ciphertext

    If you want to inspect the ciphertext in a readable format, you can add your own print routines at the end or pipe the output into a tool like hexdump or xxd

    $ php encrypt.php | xxd
    00000000: 5f35 3205 74e8 dcaa 2f05 9aa4 366e ef8b  _52.t.../...6n..
    $ perl encrypt.pl | xxd
    00000000: 5f35 3205 74e8 dcaa 2f05 9aa4 366e ef8b  _52.t.../...6n..