The following programs, one in Javascript, and the other in Perl, are supposed to perform the same task, they have the same input data. However, the output is different. The problem originated in the JS being used on the client side, sending a POST request to an app on the server side, written in Perl. The Perl program failed to decrypt with no obvious reason why. Analysing it I arrived to the conclusion that the libraries, in the two different programming languages, were behaving differently. So I wrote the client side in Perl to prove the point. The results are below.
const CryptoJS = require('crypto-js');
function logBytes(description, wordArray) {
const bytes = CryptoJS.enc.Hex.stringify(wordArray);
console.log(description, bytes);
}
function encrypt(key32, iv16, data) {
const key = CryptoJS.enc.Hex.parse(key32);
const iv = CryptoJS.enc.Hex.parse(iv16);
const utf8data = CryptoJS.enc.Utf8.parse(data);
// Log data and byte arrays
console.log("Data: " + data);
logBytes("Key Bytes:", key);
logBytes("IV Bytes:", iv);
logBytes("Data Bytes:", utf8data);
const encrypted = CryptoJS.AES.encrypt(utf8data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log("Encrypted (Base64):", encrypted.toString());
}
const data = "[email protected]";
const key32 = "9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd";
const iv16 = "9b2b9bdb4af5357cd78a8a2257c51a7f";
encrypt(key32, iv16, data);
output:
% node test.js
Data: [email protected]
Key Bytes: 9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd
IV Bytes: 9b2b9bdb4af5357cd78a8a2257c51a7f
Data Bytes: 6d79656d61696c406d797365727665722e636f6d
Encrypted (Base64): iit+mjBnWsMrMkJp63hpRmsCZgIxZ4FPZQId35qv12s=
#!/usr/bin/perl
use strict;
use warnings;
use MIME::Base64;
use Crypt::OpenSSL::AES;
sub logMessage {
my ($message) = @_;
print "$messagen";
}
sub logBytes {
my ($description, $bytes) = @_;
my $hex = unpack('H*', $bytes);
logMessage("$description: $hex");
}
sub encrypt {
my ($key32, $iv16, $data) = @_;
my $key = pack('H*', $key32);
my $iv = pack('H*', $iv16);
# Log data and byte arrays
logMessage("Data: $data");
logBytes("Key Bytes", $key);
logBytes("IV Bytes", $iv);
logBytes("Data Bytes", $data);
my $cipher = Crypt::OpenSSL::AES->new($key);
my $block_size = 16;
my $padding = $block_size - (length($data) % $block_size);
$data .= chr($padding) x $padding;
my $encrypted = $cipher->encrypt($data);
my $encrypted_base64 = encode_base64($encrypted);
$encrypted_base64 =~ s/n//g; # Remove newlines that might be added by MIME::Base64
logMessage("Encrypted (Base64): $encrypted_base64");
}
sub main {
my $data = "[email protected]";
my $key32 = '9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd';
my $iv16 = '9b2b9bdb4af5357cd78a8a2257c51a7f';
encrypt($key32, $iv16, $data);
}
main();
exit (0);
output
% ./test.pm
Data: [email protected]
Key Bytes: 9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd
IV Bytes: 9b2b9bdb4af5357cd78a8a2257c51a7f
Data Bytes: 6d79656d61696c406d797365727665722e636f6d
Encrypted (Base64): rk7JgOwsb7atyvEIXVNQkexbx5SYzufE05LZAoqtZGk=
Versions:
Perl:
Crypt::OpenSSL::AES version: 0.19
MIME::Base64 version: 3.16
JS:
[email protected]