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?
SubtleCrypto$roundsis missing.