OpenSSL 3.0.8多线程服务解密报错bad decrypt求助
问题描述
我正在编写一个服务,用于解密大量经OpenSSL加密的内容。该代码在简单命令行(CLI)程序中运行正常,但在Poco多线程服务器中使用时,出现报错:
error:1C800064:Provider routines::bad decrypt
使用的OpenSSL版本为3.0.8 2023年2月7日(库版本:OpenSSL 3.0.8 7 Feb 2023)。我尝试将std::strings复制到C数组中,但未能解决问题。不清楚问题出在哪里,想请教可行的解决思路。看到旧资料提到使用互斥回调,但文档说明现在已不需要。以下是在CLI中正常工作但在Poco服务中失效的代码:
int decodeAndDecrypt(const char * dataIn, const unsigned char * key, const unsigned char * iv, char * dataOut, int * outLen, char * errorMsg) { while (ERR_get_error()); BIO * bio = BIO_new_mem_buf((void *)dataIn, -1); BIO * b64 = BIO_new(BIO_f_base64()); BIO * cipher = BIO_new(BIO_f_cipher()); BIO_set_cipher(cipher, EVP_aes_256_cbc(), key, iv, 0); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_push(cipher, b64); BIO_push(b64, bio); char * outPtr = dataOut; int max = *outLen; *outLen = 0; errorMsg[0] = 0; while (true) { char tmp[1024] = {0}; int bytesRead = BIO_read(cipher, tmp, sizeof(tmp)); if (bytesRead <= 0) { break; } if (bytesRead > max) { snprintf(errorMsg, ERR_SZ, "%s: result data exceeds maximum buffer size.", __func__); return EXIT_FAILURE; } snprintf(outPtr, max, "%s", tmp); outPtr += bytesRead; *outLen += bytesRead; max -= bytesRead; } if (*outLen == 0) // If no data was transferred, check for errors { unsigned long errNo(0); bool errorsFound(false); while ((errNo = ERR_get_error())) { errorsFound = true; array< char, 256 > temp; ERR_error_string_n(errNo, temp.data(), temp.size()); strcat(errorMsg, temp.data()); } if (errorsFound) { return EXIT_FAILURE; } } BIO_free_all(cipher); return EXIT_SUCCESS; }
解决思路
- 确保OpenSSL多线程初始化正确:虽然OpenSSL 3.0+默认支持线程安全,但仍需在Poco服务器启动阶段调用一次
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL)和OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL),保证加密库在多线程环境下完成全局初始化。 - 杜绝BIO对象的跨线程复用:BIO实例并非线程安全,每个解密请求必须创建独立的BIO链(
bio、b64、cipher),禁止将这些对象作为全局变量或类成员在多个线程间共享,否则会引发内部状态竞争。 - 修复数据拷贝逻辑:代码中用
snprintf(outPtr, max, "%s", tmp)拷贝解密数据存在隐患——如果解密后的二进制数据包含\0,snprintf会提前终止拷贝,导致数据不完整进而触发padding校验失败。应替换为memcpy(outPtr, tmp, bytesRead)直接复制原始字节流。 - 校验输入参数的正确性与完整性:
bad decrypt常见于padding错误,需排查:- 确认
key(32字节)和iv(16字节)的长度符合AES-256-CBC要求,多线程传递时无截断或越界; - 保证
dataIn的Base64编码数据在多线程环境下完整传递,未被篡改或截断; - 可单独拆分Base64解码步骤,验证解码后的数据长度是否为16字节的倍数(AES块大小),排除解码阶段的多线程问题。
- 确认
- 完善BIO读取错误处理:当前代码仅在
bytesRead <=0时直接break,未区分正常EOF和错误。应添加BIO_should_retry判断,同时在循环结束后主动检查BIO错误状态,而非仅当outLen ==0时才检查错误。 - 检查输出缓冲区的线程安全性:确保
dataOut和outLen在多线程环境下无竞争,每个解密请求使用独立的输出缓冲区,outLen的操作尽量用局部变量暂存后再赋值到输出参数,避免多线程同时修改同一内存。
内容的提问来源于stack exchange,提问作者Bruce
相关产品推荐
相关产品推荐

