I'll-Be-Back
I'll-Be-Back

Reputation: 10828

Encryption Crypt - Trying to get to work in PHP same way as Perl

I have written encryption functionality in Perl and I am trying to get to work same way in PHP.

In PERL:

#!/usr/bin/perl

use strict;
use warnings;

use Crypt::CBC;
use Crypt::Rijndael;

my $cryptkey = '_PRIVATE_';

my $cipher = Crypt::CBC->new( -key    => $cryptkey,
                              -salt   => 1,
                              -cipher => 'Rijndael',
                             );

my $data = "hello";

my $ciphertext = $cipher->encrypt_hex($data);

print "HEX_KEY: '$ciphertext' \n";

Output:

HEX_KEY: '53616c7465645f5fc36630f5364619c31ac26e44809c81bf84ae995c22be45ce'

I am trying to get to work in PHP and output same HEX but it is not the same, what went wrong?

class Test {

    public function Encypt($data, $cryptkey) {

        $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $cryptkey, $data, MCRYPT_MODE_CBC);

        return bin2hex($encrypted);
    }
}


$data = "hello";

$test = new Test();

$cryptkey = "_PRIVATE_";

$hex =  $test->Encypt($data, $cryptkey);
echo $hex;

Output

2bab1b8874692176d213e4c23565b304

Upvotes: 3

Views: 835

Answers (1)

amon
amon

Reputation: 57640

Crypt::CBC and mcrypt_encrypt use different defaults, which lead to this mismatch.

For mcrypt_encrypt, the documentation offers this information:

string mcrypt_encrypt ( string $cipher , string $key , string $data , string $mode [, string $iv ] )

  • $cipher is the algorithm name.
  • $key is the encryption key, if it is too short it will be padded with NUL bytes.
  • $data is the data to be encrypted, if it is too short it will be padded with NUL bytes.
  • $mode is the mode of operation, here "cbc" is correct.
  • $iv is the initialization vector, if not provided it will be initialized as NUL bytes.

For Crypt::CBC, we get this behaviour instead:

Crypt::CBC->new(KEYWORD_OPTIONS)

  • -key => STRING is the passphrase from which the encryption key is generated by some hashing operations.
  • -literal_key => BOOL If set, this skips the hashing for the -key and uses it as a literal key.
  • -cipher => CIPHER the name of a cipher, or a pre-initialized cipher object.
  • -salt => 1 | STRING If set to "1", this will produce a random salt. Any other values are taken as the literal salt. This defaults to -salt => 1 if a salt is needed. Or something like that, the docs are a bit confusing here. The salt is not needed if both the -iv and literal_key options are set.
  • -iv => STRING is the initialization vector, which is usually generated from the salt.
  • -header => STRING controls what kind of header is prepended to the output. This defaults to "salt", but can also be set to "none".

Note further that RIJNDAEL_128 implies a keylength of 16, whereas Crypt::CBC assumes a keylength of 32.

Using Crypt::Rijndael without the Crypt::CBC wrapper is probably preferable, because this allows us to easily set the required options to the same defaults which PHP uses:

use Crypt::Rijndael;

my $key = "_PRIVATE_";
my $data = "hello";

# pad the $key to 16 bytes
$key .= "\0" x (16 - length $key);
# pad the $data to a multiple of 16 bytes:
if (my $distance = length($data) % 16) {
    $data .= "\0" x (16 - $distance);
}

my $crypt = Crypt::Rijndael->new($key, Crypt::Rijndael::MODE_CBC);
$crypt->set_iv("\0" x 16);

my $binary = $crypt->encrypt($data);
print unpack("H*", $binary), "\n";

Which then outputs 2bab1b8874692176d213e4c23565b304.

Upvotes: 3

Related Questions