Reputation: 366
I have trouble decrypting an message encrypted in php with the openssl_encrypt
method. I am using the new WebCrypto API (so I use crypto.subtle
).
Encrypting in php:
$ALGO = "aes-256-ctr";
$key = "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4";
function encrypt ($data, $key) {
global $ALGO;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ALGO), $strong);
if (!$strong) {
exit("can't generate strong IV");
}
return bin2hex($iv).openssl_encrypt($data, $ALGO, $key, 0, $iv);
}
$enc = encrypt("Lorem ipsum dolor", $key);
exit($enc);
example output:
8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=
(I can decrypt that in php and get the cleartext back)
In JS I decrypt like this:
function Ui8FromStr (StrStart) {
const Ui8Result = new Uint8Array(StrStart.length);
for (let i = 0; i < StrStart.length; i++) {
Ui8Result[i] = StrStart.charCodeAt(i);
}
return Ui8Result;
}
function StrFromUi8 (Ui8Start) {
let StrResult = "";
Ui8Start.forEach((charcode) => {
StrResult += String.fromCharCode(charcode);
});
return StrResult;
}
function Ui8FromHex (hex) {
for (var bytes = new Uint8Array(Math.ceil(hex.length / 2)), c = 0; c < hex.length; c += 2)
bytes[c/2] = parseInt(hex.substr(c, 2), 16);
return bytes;
}
const ALGO = 'AES-CTR'
function decrypt (CompCipher, HexKey) {
return new Promise (function (resolve, reject) {
// remove IV from cipher
let HexIv = CompCipher.substr(0, 32);
let B64cipher = CompCipher.substr(32);
let Ui8Cipher = Ui8FromStr(atob(B64cipher));
let Ui8Iv = Ui8FromHex (HexIv);
let Ui8Key = Ui8FromHex (HexKey);
crypto.subtle.importKey("raw", Ui8Key, {name: ALGO}, false, ["encrypt", "decrypt"]). then (function (cryptokey){
return crypto.subtle.decrypt({ name: ALGO, counter: Ui8Iv, length: 128}, cryptokey, Ui8Cipher).then(function(result){
let Ui8Result = new Uint8Array(result);
let StrResult = StrFromUi8(Ui8Result);
resolve(StrResult);
}).catch (function (err){
reject(err)
});
})
})
}
when I now run decrypt("8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=", "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4").then(console.log)
I get gibberish: SÌõÅ°blfçSÑ-
The problem I have is, that I am not sure what is meant with counter
. I tried the IV but failed.
This Github tutorial suggests*1, that it is the IV - or at least part of it, as I've seen people talk about that the counter is part of the IV (something like 4 bytes, that means that the IV is made from 12 bytes IV and 4 bytes Counter)
If that is indeed true, my question then becomes: Where do I give the script the other 12 bytes of IV when counter is only 4 bytes of it.
Can anyone maybe give me a working example of encryption in php
*1 It says that the same counter has to be used for en- and decryption. This leads me to believe, that it is at least something similar to the IV
Upvotes: 1
Views: 1210
Reputation: 79733
You are handling the key incorrectly in PHP.
In the PHP code you are passing the hex encoded key directly to the openssl_encrypt
function, without decoding it. This means the key you are trying to use is twice as long as expected (i.e. 64 bytes). OpenSSL doesn’t check the key length, however—it just truncates it, taking the first 32 bytes and using them as the encryption key.
The Javascript code handles the key correctly, hex decoding it before passing the decoded array to the decryption function.
The overall result is you are using a different key in each case, and so the decryption doesn’t work.
You need to add a call to hex2bin
on the key in your PHP code, to convert it from the hex encoding to the actual 32 raw bytes.
Upvotes: 4