I have an application in which I am encrypting a json encoded array using AES CBC 128 algorithm and then Decrypting it in javascript(React/Next Js Project). My Encryption in php is as shown in the below code
ENCRYPTION PHP
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
I am facing problems in decryption of this in Javascript
my code so far is as shown below
const baseenc = CryptoJS.enc.Base64.parse(cipher).toString();
var encrypted = CryptoJS.AES.decrypt(cipher, key, { iv: iv }).toString();
var plaintext = CryptoJS.enc.Latin1.stringify(encrypted);
Can any body please show what is the error or help me in getting the correct output
-
1Your CryptoJS code is missing the separation of IV, HMAC and actual ciphertext. In addition, the HMAC for the ciphertext must be calculated. The ciphertext is authentic if the calculated and received HMAC match. You need the IV together with the key to decrypt the actual ciphertext after successful authentication. See also here for using the same key for encryption and authentication.Topaco– Topaco2021年01月07日 14:16:40 +00:00Commented Jan 7, 2021 at 14:16
-
@Topaco So can you help me how can i seperate the iv hmac and the ciphertextAsish Antony– Asish Antony2021年01月07日 15:58:46 +00:00Commented Jan 7, 2021 at 15:58
-
Sure, please see my answer.Topaco– Topaco2021年01月07日 17:16:00 +00:00Commented Jan 7, 2021 at 17:16
1 Answer 1
The following steps must be implemented in the CryptoJS code:
- Separate IV, HMAC and ciphertext (after Base64 decoding)
- Calculate the HMAC for the ciphertext
- Check the authenticity of the ciphertext. The ciphertext is authentic if the received and calculated HMAC are identical.
- Perform decryption, only if the ciphertext is authentic
The following code is a possible implementation. As key 0123456789012345 was applied and with the PHP code the used ciphertext was generated:
var ciphertext = 'WqfMfCxKg7U7h5S1mbx7mSHOkkkIrUUpg++mX4ZdWt0I26VfKn7bsi60Oo/SIsWQGyC4dF5z081NvjTXwZGjIpguA0k/QqIM/GDEpCojaro=';
var key = '0123456789012345';
// Convert key and ciphertext into WordArrays
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var keyWA = CryptoJS.enc.Utf8.parse(key);
// Separate IV, HMAC and ciphertext
var ivWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(0, 4));
var hmacWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4, 4 + 8));
var actualCiphertextWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4 + 8));
// Authenticate
var hmacCalculatedWA = CryptoJS.HmacSHA256(actualCiphertextWA, keyWA);
if(CryptoJS.enc.Base64.stringify(hmacCalculatedWA) === CryptoJS.enc.Base64.stringify(hmacWA)) {
// Decrypt if authentication is successfull
var decryptedMessageWA = CryptoJS.AES.decrypt({ciphertext: actualCiphertextWA}, keyWA, {iv: ivWA});
var decryptedMessage = CryptoJS.enc.Utf8.stringify(decryptedMessageWA);
console.log(decryptedMessage);
} else {
console.log('Authentication failed!');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please note that it is better to use different keys for encryption and authentication, see here.