Issue
I can encrypt and decrypt a string using openssl:
ENCRYPTED=$(echo "my_secret_data" | openssl aes-256-cbc -pass "pass:bab3fd92bcd7d464" -pbkdf2 -a -A)
echo -n $ENCRYPTED | base64 -d | openssl aes-256-cbc -d -pass "pass:bab3fd92bcd7d464" -pbkdf2
However, I'm not able to decrypt the string back using Node.js.
I tried the following Node.js code:
const crypto = require('crypto');
const encryptedTextBase64 = 'U2FsdGVkX18AYE13z9uboo3WZhktr03EeV0WFA0MH4o=';
const password = 'bab3fd92bcd7d464';
// Decode the base64-encoded text
const encryptedText = Buffer.from(encryptedTextBase64, 'base64');
// Create a decipher object
const decipher = crypto.createDecipher('aes-256-cbc', password);
// Update the decipher with the encrypted text
let decrypted = decipher.update(encryptedText, 'binary', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
But I get the error
node:internal/crypto/cipher:199
const ret = this[kHandle].final();
^
Error: error:1C800064:Provider routines::bad decrypt
at Decipher.final (node:internal/crypto/cipher:199:29)
at Object.<anonymous> (.../decrypt.js:14:23)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:86:12)
at node:internal/main/run_main_module:23:47 {
library: 'Provider routines',
reason: 'bad decrypt',
code: 'ERR_OSSL_BAD_DECRYPT'
}
But the following bash code works as expected:
echo -n U2FsdGVkX18AYE13z9uboo3WZhktr03EeV0WFA0MH4o= | base64 -d | openssl aes-256-cbc -d -pass "pass:bab3fd92bcd7d464" -pbkdf2
Any help in order to have a valid Node.js code to decrypt the payload is highly appreciated.
If the bash command also needs to be changed, that's not a problem. My end goal is to be able to encrypt a string with bash and decrypt with Node.js using a known passkey.
Solution
The NodeJS code lacks the separation of salt and ciphertext (encryptedText
is the concatenation of Salted__
+ 8 bytes salt + ciphertext) as well as the determination of a 48 bytes array with PBKDF2 (using the password, the salt, an iteration count of 10000 and SHA-256). The first 32 bytes are the key, the remaining 16 bytes the IV.
After that the decryption has to be carried out with createDecipheriv()
(with as before aes-256-cbc
).
A working code is:
const crypto = require('crypto');
const encryptedTextBase64 = 'U2FsdGVkX18AYE13z9uboo3WZhktr03EeV0WFA0MH4o=';
const password = 'bab3fd92bcd7d464';
// Decode the base64-encoded text
const encryptedText = Buffer.from(encryptedTextBase64, 'base64');
// Extract salt (first 8 bytes) and ciphertext (the rest)
const salt = encryptedText.subarray(8, 16);
const ciphertext = encryptedText.subarray(16);
// Derive the key and IV using PBKDF2
const keyIVBuffer = crypto.pbkdf2Sync(password, salt, 10000, 48, 'sha256');
const key = keyIVBuffer.subarray(0, 32);
const iv = keyIVBuffer.subarray(32);
// Create a decipher object with IV
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
// Update the decipher with the ciphertext
let decrypted = decipher.update(ciphertext, 'binary', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
Answered By - Miguel Prytoluk Answer Checked By - Clifford M. (WPSolving Volunteer)