Search code examples
javascriptperlencryptionaescryptojs

AES encryption in Perl similar to [email protected]


The following JavaScript code:

'use strict';
const CryptoJS = require('crypto-js');

const plaintext = 's3cret';
const password  = 'MyPassword';
//const iv   = CryptoJS.lib.WordArray.random(16);
//const salt = CryptoJS.lib.WordArray.random(16);
const iv   = CryptoJS.enc.Hex.parse("43c9ccba630fe1cd61fc2bdb90121c6f"); // For testing
const salt = CryptoJS.enc.Hex.parse("5c788a415851e909d9c7951717714204"); // For testing
const key = CryptoJS.PBKDF2(password, salt, {keySize: 128/32, iterations: 1000});
const b64ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv}).ciphertext.toString(CryptoJS.enc.Base64);
console.log(b64ciphertext);
  1. Using [email protected], produces:
    'use strict';
    
    const plaintext = 's3cret';
    const password = 'MyPassword';
    //const iv   = CryptoJS.lib.WordArray.random(16);
    //const salt = CryptoJS.lib.WordArray.random(16);
    const iv   = CryptoJS.enc.Hex.parse("43c9ccba630fe1cd61fc2bdb90121c6f"); // For testing
    const salt = CryptoJS.enc.Hex.parse("5c788a415851e909d9c7951717714204"); // For testing
    const key = CryptoJS.PBKDF2(password, salt, {keySize: 128/32, iterations: 1000});
    console.log("key: " + key.toString(CryptoJS.enc.Base64));
    const b64ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv}).ciphertext.toString(CryptoJS.enc.Base64);
    console.log("ciphertext: " + b64ciphertext);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js" integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
key: 8E2dqz889iLAam1MV4wweQ==
ciphertext: eiJkaAmCVjHFKbvY/CnkvQ==
  1. Using [email protected], produces:
    'use strict';
    
    const plaintext = 's3cret';
    const password = 'MyPassword';
    //const iv   = CryptoJS.lib.WordArray.random(16);
    //const salt = CryptoJS.lib.WordArray.random(16);
    const iv   = CryptoJS.enc.Hex.parse("43c9ccba630fe1cd61fc2bdb90121c6f"); // For testing
    const salt = CryptoJS.enc.Hex.parse("5c788a415851e909d9c7951717714204"); // For testing
    const key = CryptoJS.PBKDF2(password, salt, {keySize: 128/32, iterations: 1000});
    console.log("key: " + key.toString(CryptoJS.enc.Base64));
    const b64ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv}).ciphertext.toString(CryptoJS.enc.Base64);
    console.log("ciphertext: " + b64ciphertext);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
key: 8NJim/oe3/Q0+wLRq86FGg==
ciphertext: qT1Vtod7ihS2wvtwmlnPow==

The Perl equivalent for the [email protected] output was provided by this answer:

use strict;
use warnings;
use feature qw( say );

use Crypt::CBC    qw( );
use Crypt::PBKDF2 qw( );
use Mojo::Util qw(b64_encode);

my $plaintext = 's3cret';
my $password  = 'MyPassword';
#my $iv   = Crypt::CBC->random_bytes( 16 );
#my $salt = Crypt::CBC->random_bytes( 16 );
my $iv   = pack( "H*", "43c9ccba630fe1cd61fc2bdb90121c6f" ); # For testing
my $salt = pack( "H*", "5c788a415851e909d9c7951717714204" ); # For testing

my $pbkdf2 = Crypt::PBKDF2->new(output_len=>128/8, iterations=>1000);
my $key = $pbkdf2->PBKDF2($salt,$password);
say "key: ", b64_encode($key,'');
my $cipher = Crypt::CBC->new(
                 -cipher  => 'Cipher::AES',
                 -header  => 'none',
                 -pbkdf   => 'none',
                 -keysize => length($key),
                 -key     => $key,
                 -iv      => $iv
                );
my $ciphertext = $cipher->encrypt( $plaintext );
my $b64ciphertext = b64_encode($ciphertext,'');

say "ciphertext: ", $b64ciphertext;

Using Perl, how can I get an output similar to [email protected]?


Solution

  • CryptoJS, v4.1.1 uses SHA1 as PBKDF2 digest by default, CryptoJS, v4.2.0 uses SHA256 by default. In order for SHA256 to be used in the Perl code, you must add hash_class => 'HMACSHA2', i.e.:

    my $pbkdf2 = Crypt::PBKDF2->new(output_len=>128/8, iterations=>1000, hash_class => 'HMACSHA2');
    

    For completeness: In CryptoJS the digest can also be set explicitly with the hasher option: The following code would also use SHA256 as PBKDF2 digest under v4.1.1:

    const key = CryptoJS.PBKDF2(password, salt, {keySize: 128/32, iterations: 1000, hasher: CryptoJS.algo.SHA256}); 
    

    Note that CryptoJS has recently been discontinued and is no longer maintained.