You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 03:34:56