OpenSSL中正确的上下文释放/分配方式

18

我在我的程序中使用Open SSL来使用AES密码对数据进行加密和解密。目前存在一些内存泄漏问题,因此我正在寻找解决方法。在我的加密解密例程中,我已经像这样释放了上下文。

EVP_CIPHER_CTX_free(ctx);

创建者:

EVP_CIPHER_CTX_new

这个例子出现在 OpenSSL 维基页面的 示例 中。

但是!在 MAN 页面上,建议使用 EVP_CIPHER_CTX_cleanupEVP_CIPHER_CTX_init 函数。所以应该使用什么才是正确的,EVP_CIPHER_CTX_new/EVP_CIPHER_CTX_free 是否已经过时了?EVP_CIPHER_CTX_new/EVP_CIPHER_CTX_freeEVP_CIPHER_CTX_init / EVP_CIPHER_CTX_cleanup 之间有什么重大区别吗?

if(!(ctx = EVP_CIPHER_CTX_new())) return -1;


  if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
  {
    EVP_CIPHER_CTX_free(ctx);
    return -1;
  }

  if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
  {
    EVP_CIPHER_CTX_free(ctx);
    return -1;
  }
  ciphertext_len = len;


  if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { EVP_CIPHER_CTX_free(ctx); return -1; }
  ciphertext_len += len;


  EVP_CIPHER_CTX_free(ctx);
3个回答

34

首先,如果您想得到精确的答案,您应该始终指定您使用的OpenSSL版本。FYI 1.0.2是目前长期支持版本,而1.1.0是最新版本(于2016年9月发布)。

如果您阅读1.1.0版手册,您会注意到:

  

EVP_CIPHER_CTX在OpenSSL 1.1.0中变为了不透明。因此,   EVP_CIPHER_CTX_reset()出现了,而EVP_CIPHER_CTX_cleanup()   消失了。 EVP_CIPHER_CTX_init()仍然是   EVP_CIPHER_CTX_reset()的别名。

简短的答案是:无论版本如何,您都应该使用EVP_CIPHER_CTX_new进行初始化,并使用EVP_CIPHER_CTX_free释放内存,原因如下。

分配:

1.0.2版手册说:

EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);

1.1.0 版本的 man 手册如下所示:

EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();

如果您查看1.0.2中EVP_CIPHER_CTX_init的代码

void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *ctx)
{
   memset(ctx, 0, sizeof(EVP_CIPHER_CTX));
   /* ctx->cipher=NULL; */
}

当 EVP_CIPHER_CTX_new 存在时:

EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
{
   EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof *ctx);
   if (ctx)
      EVP_CIPHER_CTX_init(ctx);
   return ctx;
}

所以,你最好仍然像1.1.0的例子中那样初始化上下文:

EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();

对于1.1.0,同样适用。

释放内存:

1.0.2手册页面:

EVP_CIPHER_CTX_cleanup(&ctx);

1.1.0 手册页:

EVP_CIPHER_CTX_free(ctx);

但是如果您检查代码,您会发现对于版本1.0.2:

void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
{
   if (ctx) {
       EVP_CIPHER_CTX_cleanup(ctx);
      OPENSSL_free(ctx);
   }
}

因此,您应该使用 EVP_CIPHER_CTX_free 进行释放内存。如果您只是想为另一个操作重置上下文,则 EVP_CIPHER_CTX_cleanup(1.0.2)和 EVP_CIPHER_CTX_reset(1.1.0)是您的好伙伴。

如果您对 mallocmemsetcalloc 感到好奇,这里有一个 很好的解释


使用EVP_CIPHER_CTX/EVP_CIPHER_CTX_free,但valgrind显示明确丢失了内存。 - Djvu

6
您不应再使用EVP_EncryptInit。该函数自动创建了特定的上下文,但不支持后来添加的加密引擎。然而EVP_EncryptInit_ex明确说明:

在调用此函数之前,必须初始化ctx

因此,我想您需要在这里使用EVP_CIPHER_CTX_newEVP_CIPHER_CTX_free则是另一回事,它似乎已被弃用,在OpenSSL的手册页中没有任何提及。删除密钥材料和密码器的其他状态在NIST认证功能中是良好的做法(也是必需的)。否则,攻击者可能会在稍后阶段扫描内存或使用溢出。 EVP_CIPHER_CTX_free的名称仅表示应释放CTX内存。但是,释放内存并不意味着首先清除其中包含的敏感信息;它只是返回给系统,该系统没有义务覆盖它。另一方面,EVP_CIPHER_CTX_cleanup在释放内存之前明确清除了这些信息(或者至少尝试这样做,我想)。因此,在提供密钥材料后,您需要调用此函数。

我认为你的内存问题不在上下文中,看着你的代码。你可能想尝试一些其他的东西,比如静态代码分析器。 - Maarten Bodewes

-5

好的,我想现在清楚了。如果你做 EVP 风格的加密/解密,请确保你创建上下文的方式如下:

  EVP_CIPHER_CTX ctx;
  EVP_CIPHER_CTX_init(&ctx);

并且像这样释放它:

EVP_CIPHER_CTX_cleanup(&ctx); 

不要使用EVP_CIPHER_CTX_new/EVP_CIPHER_CTX_free来创建/释放上下文,它们已经过时了!


8
我认为相反。事实上,openssl-1.1.0 将 EVP_CIPHER_CTX 变为不透明类型,因此上面的第一种形式会导致编译错误。 - pixelbeat
3
其他答案说得完全相反,这个回答把事情搞反了。请更新或取消接受该回答。 - rdb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接