-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Decryption js side fails with error:1C800064:Provider routines::bad decrypt #4940
Description
Describe the bug
Decryption fails on the Node.js side and throws the following exception:
error:1C800064:Provider routines::bad decrypt
To Reproduce
Encrypt a string on the C++ side using POCO, then attempt to decrypt the encrypted string on the JavaScript side.
CODE:
Cpp side:
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/Context.h>
#include <Poco/Net/SSLManager.h>
#include <Poco/StreamCopier.h>
#include <Poco/Path.h>
#include <Poco/URI.h>
#include <Poco/Crypto/Cipher.h>
#include <Poco/Crypto/CipherFactory.h>
#include <Poco/Crypto/CipherKey.h>
#include <Poco/DigestEngine.h>
#include <Poco/MD5Engine.h>
#include <Poco/Base64Decoder.h>
#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
using namespace Poco;
using namespace Poco::Net;
using namespace Poco::Crypto;
using namespace std;
// Global encryption parameters
const std::string g_encMethod = "aes-256-cbc";
const std::string g_key = "01234567890123456789012345678901"; // 32 bytes
const std::string g_iv = "1234567890123456"; // 16 bytes
void printHex(const std::string& data) {
std::ostringstream oss;
for (unsigned char c : data) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)c << "";
}
std::cout << "[DEBUG] Base64 Decoded (Hex) : [" << oss.str() << "]" << std::endl;
std::cout << "[DEBUG] Base64 Decoded Length: " << oss.str().length() << "]" << std::endl;
}
std::string base64Decode(const std::string& base64Str) {
std::istringstream istr(base64Str);
Poco::Base64Decoder decoder(istr);
std::ostringstream out;
Poco::StreamCopier::copyStream(decoder, out);
return out.str(); // decoded binary string
}
// In CppClient2.cpp
std::string encryptData(const std::string& data) {
// Create cipher with explicit parameters
CipherKey cipherKey(g_encMethod, g_key, g_iv);
unique_ptr<Cipher> pCipher(CipherFactory::defaultFactory().createCipher(cipherKey));
// Use base64 encoding
std::string encryptedData = pCipher->encryptString(data, Cipher::ENC_BASE64);
return encryptedData;
}
std::string decryptData(const std::string& encryptedBase64) {
// Create cipher with explicit parameters
CipherKey cipherKey(g_encMethod, g_key, g_iv);
unique_ptr<Cipher> pCipher(CipherFactory::defaultFactory().createCipher(cipherKey));
try {
// Decrypt from base64
std::string decryptedText = pCipher->decryptString(encryptedBase64, Cipher::ENC_BASE64);
return decryptedText;
}
catch (Exception& ex) {
std::cerr << "Decryption error: " << ex.displayText() << std::endl;
throw;
}
}
std::string calculateMD5(const std::string& data) {
MD5Engine md5;
md5.update(data);
return DigestEngine::digestToHex(md5.digest());
}
int mainUsingPayloadEncryption() {
try {
std::cout << "Using key: "; printHex(g_key); std::cout << std::endl;
std::cout << "Using IV: "; printHex(g_iv); std::cout << std::endl;
std::cout << "Using method: " << g_encMethod << std::endl << std::endl;
// First test local encryption/decryption
std::string jsonData = "{\"name\":\"John\",\"message\":\"Hello from C++\"}";
std::cout << "Original JSON: [" << jsonData << "]" << std::endl;
std::cout << "Original JSON length: [" << jsonData.length() << "]" << std::endl;
// Test encryption
std::string encrypted = encryptData(jsonData);
std::cout << "Encrypted data (base64): [" << encrypted << "]" << std::endl;
std::cout << "Encrypted data length: [" << encrypted.length() << "]" << std::endl;
std::string decoded = base64Decode(encrypted);
std::cout << "decoded data (from base64): [" << decoded << "]" << std::endl;
std::cout << "decoded data (from base64): ["; printHex(decoded); cout << "]" << std::endl;
std::cout << "decoded data length: [" << decoded.length() << "]" << std::endl;
// Test decryption
std::string decrypted = decryptData(encrypted);
std::cout << "Decrypted Data: [" << decrypted << "]" << std::endl;
std::cout << "Decrypted Data length: [" << decrypted.length() << "]" << std::endl;
// Verify the round trip was successful
if (jsonData == decrypted) {
std::cout << "✓ Local encryption/decryption test PASSED!" << std::endl;
}
else {
std::cout << "✗ Local encryption/decryption test FAILED!" << std::endl;
return 1;
}
// Now proceed with the HTTPS request
const string certPath = "certs/";
const string clientCert = certPath + "client.crt";
const string clientKey = certPath + "client.key";
const string caFile = certPath + "rootCA.crt";
Context::Ptr context = new Context(
Context::CLIENT_USE,
clientKey,
clientCert,
caFile,
Context::VERIFY_STRICT,
9,
false,
"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
);
SSLManager::instance().initializeClient(0, 0, context);
HTTPSClientSession session("localhost", 8090, context);
session.setTimeout(Timespan(10, 0));
// Calculate MD5 hash for integrity check
Poco::MD5Engine md5;
md5.update(encrypted);
std::string md5Hash = Poco::DigestEngine::digestToHex(md5.digest());
// Prepare request body
std::string requestBody = "{\"encrypted_payload\":\"" + encrypted +
"\",\"integrity_hash\":\"" + md5Hash + "\"}";
HTTPRequest request(HTTPRequest::HTTP_POST, "/send", HTTPMessage::HTTP_1_1);
request.setContentType("application/json");
request.setContentLength(requestBody.length());
std::ostream& os = session.sendRequest(request);
os << requestBody;
HTTPResponse response;
std::istream& rs = session.receiveResponse(response);
std::string responseBody;
StreamCopier::copyToString(rs, responseBody);
cout << "Response: " << response.getStatus() << " " << response.getReason() << endl;
cout << "Body: " << responseBody << endl;
return 0;
}
catch (Exception& ex) {
cerr << "Error: " << ex.displayText() << endl;
return 1;
}
}
JavaScript code:
const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
const https = require('https');
const app = express();
app.use(express.json());
// Global encryption parameters - must match C++ client
const g_encMethod = 'aes-256-cbc';
const g_key = Buffer.from('01234567890123456789012345678901', 'utf8'); // 32 bytes
const g_iv = Buffer.from('1234567890123456', 'utf8'); // 16 bytes
/**
* Encrypts text using AES-256-CBC.
*
* @param {string} plaintext - The text to encrypt.
* @param {string} algorithm - The encryption algorithm, e.g., 'aes-256-cbc'.
* @param {Buffer} key - The 32-byte key.
* @param {Buffer} iv - The 16-byte initialization vector.
* @returns {Buffer} The encrypted data.
*/
function encrypt(plaintext, algorithm, key, iv) {
try {
console.log('[DEBUG] Plaintext:', plaintext);
console.log('[DEBUG] Key:', key.toString('hex'));
console.log('[DEBUG] IV:', iv.toString('hex'));
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(plaintext, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
console.log('[DEBUG] Encrypted buffer (hex):', encrypted.toString('hex'));
console.log('[DEBUG] Encrypted buffer (base64):', encrypted.toString('base64'));
return encrypted;
} catch (error) {
console.error('[DEBUG] Encryption error details:', error.message);
throw error;
}
}
/**
* Decrypts AES-256-CBC encrypted data.
*
* @param {Buffer} encryptedBuffer - The encrypted data buffer.
* @param {string} algorithm - The encryption algorithm, e.g., 'aes-256-cbc'.
* @param {Buffer} key - The 32-byte key.
* @param {Buffer} iv - The 16-byte initialization vector.
* @returns {string} The decrypted plaintext.
*/
function decrypt(encryptedBuffer, algorithm, key, iv) {
try {
console.log('[DEBUG] Encrypted buffer:', encryptedBuffer.toString('hex'));
console.log('[DEBUG] Encrypted buffer length:', encryptedBuffer.length);
console.log('[DEBUG] Key:', key.toString('hex'));
console.log('[DEBUG] IV:', iv.toString('hex'));
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encryptedBuffer);
decrypted = Buffer.concat([decrypted, decipher.final()]);
const result = decrypted.toString('utf8');
console.log('[DEBUG] Decrypted text:', result);
return result;
} catch (error) {
console.error('[DEBUG] Decryption error details:', error.message);
throw error;
}
}
// Middleware to verify data integrity
function verifyIntegrity(req, res, next) {
const { encrypted_payload, integrity_hash } = req.body;
if (!encrypted_payload || !integrity_hash) {
return res.status(400).json({ error: 'Missing required fields' });
}
console.log('[DEBUG] Received encrypted_payload:', encrypted_payload);
console.log('[DEBUG] Received integrity_hash:', integrity_hash);
// Calculate MD5 hash of encrypted payload
const calculatedHash = crypto.createHash('md5')
.update(encrypted_payload)
.digest('hex');
console.log('[DEBUG] Calculated MD5:', calculatedHash);
// Verify hash matches
if (calculatedHash !== integrity_hash) {
return res.status(400).json({ error: 'Data integrity check failed' });
}
next();
}
// Endpoint to receive encrypted data
app.post('/send', verifyIntegrity, (req, res) => {
const { encrypted_payload } = req.body;
console.log('[DEBUG] Key hex: [' + g_key.toString('hex') + "]");
console.log('[DEBUG] IV hex: [' + g_iv.toString('hex') + "]");
console.log('[DEBUG] Method: [' + g_encMethod + "]");
try {
console.log('[DEBUG] Ciphertext (base64): [' + encrypted_payload + "]");
console.log('[DEBUG] Ciphertext length: [' + encrypted_payload.length + "]");
// Decrypt using the global parameters
const decryptedStr = decrypt(encrypted_payload, g_encMethod, g_key, g_iv);
console.log('[DEBUG] Decrypted: [' + decryptedStr + "]");
const data = JSON.parse(decryptedStr);
console.log('[DEBUG] Parsed data: [' + data + "]");
// Send success response
res.json({
status: 'success',
message: `Hello ${data.name || 'User'}, your message was received securely`
});
} catch (error) {
console.error('[ERROR] Processing error:', error.message);
res.status(500).json({ error: 'Failed to process data: ' + error.message });
}
});
// Default route
app.get('/', (req, res) => {
res.send('Secure server is running');
});
// HTTPS server options
const options = {
key: fs.readFileSync('../certs/server.key'),
cert: fs.readFileSync('../certs/server.crt'),
ca: fs.readFileSync('../certs/rootCA.crt'),
requestCert: true,
rejectUnauthorized: true
};
const server = https.createServer(options, app);
server.listen(8090, () => {
console.log('Secure server listening on port 8090');
});
Expected behavior
At js side, Decrypted string should be same as original string before encryption from c++ side.
Logs
Please add relevant environment information:
- OS Type and Version: 10
- POCO Version: 1.14.0
- Third-party product (eg. database or library) type and version: NA
Additional context
I'm attempting to encrypt a JSON object on the C++ side using POCO and then send the encrypted data (along with the payload) to the server. On the JavaScript side, both the base64-encoded string and the raw encrypted data match the output from C++, which confirms the data is transmitted correctly.
However, when I try to decrypt the data in JavaScript, I get the following error:
error:1C800064:Provider routines::bad decrypt
I'm unsure why the decryption fails in JavaScript. For context:
Encryption and decryption on the C++ side work perfectly.
Similarly, both encryption and decryption in JavaScript also work as expected.
The issue only occurs when encrypting in C++ and attempting to decrypt that encrypted data in JavaScript.
Note: I am using the same key, iv and encryption AES like 256.
I'd greatly appreciate any guidance on how to resolve this issue.
Thanks a lot in advance to this amazing community!
