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.
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)
$
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..