I'm trying to encrypt in PHP with openssl
and decrypt in Javascript with crypto.subtle
. The problem is the encrypted string in PHP is 16 bytes shorter than the same encrypted string in Javascript and I can't figure out what the problem is.
(For the test I use fixed variables (password, salt and iv).
PHP:
$msg = 'Hello world! Goodbye world!';
$passHash = 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU='; // abcdefghijklmnopqrstuvwxyz012345
$saltHash ='nD45YGPbiHv/5B6MBFrf00CqtjEjOmsvv5mYf+d1iYU='; // random bytes
$ivHash='eq32nRkkyPDUwHdr'; // random bytes
$password = base64_decode($passHash);
$rounds = 100000;
$salt = base64_decode($saltHash);
$iv = base64_decode($ivHash);
$bits = hash_pbkdf2('sha256', $password, $salt, $rounds, 64, true);
$aesKey = pack('C*',...(array_slice(unpack('C*',$bits),32,64)));
$ENC = openssl_encrypt(
$msg,
'aes-256-gcm',
$aesKey,
3,
$iv,
$tag,
32
);
Javascript:
let aesKey = await crypto.subtle.importKey(
'raw',
aesBits,
{
"name": "AES-GCM"
},
false,
['encrypt']
);
let enc = await crypto.subtle.encrypt(
{
"name": "AES-GCM",
"iv": iv
},
aesKey,
msg
);
Salt, iv and aesKey are exactly the same in both scripts. But the output always differs 16 bytes (base64 encoded):
PHP: mXa+Wf0pfgW/IZlNpDj3F7YjpkTCcTPPUGlJ //27 bytes
JS : mXa+Wf0pfgW/IZlNpDj3F7YjpkTCcTPPUGlJCrVR35+8KljvZ18h+ZrDgQ== //43 bytes
If I decrypt the JS encrypted string in PHP I get Hello world! Goodbye world!A9��8U��^NpMu<
(the string + 16 bytes garbage)
If I decrypt the PHP encrypted string in JS I get an error as JS expects an arrayBuffer(43) and only gets an arrayBuffer(27).
I've tried adding the $tag
in PHP to the encrypted string but then the output is still different.
So my question is where those 16 bytes in JS come from and how do I add them in PHP when sending encrypted data and get rid of them when decrypting in PHP?