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

Posted

技术标签:

【中文标题】在 OpenSSL 中释放/分配上下文的正确方法【英文标题】:Correct way to free/allocate the context in the OpenSSL 【发布时间】:2014-12-08 07:46:40 【问题描述】:

我在我的程序中使用 Open SSL,使用 aes 密码加密和解密数据。目前有一点内存泄漏,所以我正在寻找一种方法来解决这个问题。在我的加密解密例程中,我有这样的免费上下文

EVP_CIPHER_CTX_free(ctx);

创建者:

EVP_CIPHER_CTX_new

这是在 examples 的 OpenSSL wiki 页面上

但是!在 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);

【问题讨论】:

【参考方案1】:

首先,如果您想要一个准确的答案,您应该始终指定您使用的是哪个版本的OpenSSL。仅供参考 1.0.2 是当前的 Long Term Support 版本,而 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 手册页说:

EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();

如果你查看 1.0.2 中 EVP_CIPHER_CTX_init 的code

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);

但是,如果您检查 code,您可以看到 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) 是您的朋友。

如果您对malloc memsetcalloc 感到好奇,这里有一个good explanation

【讨论】:

带有 EVP_CIPHER_CTX/EVP_CIPHER_CTX_free,但 valgrind 显示肯定会丢失内存。【参考方案2】:

你不应该再使用EVP_EncryptInit。该函数确实自动创建了一个特定的上下文,但它不支持后来添加的加密引擎。 EVP_EncryptInit_ex 然而明确指出:

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

所以我想你需要在这里使用EVP_CIPHER_CTX_new

EVP_CIPHER_CTX_free 是另一回事,它似乎已被弃用,我在 OpenSSL 的手册页上没有看到任何提及。在使用后删除密钥材料和密码的其他状态是一种很好的做法(对于 NIST 认证的功能必需)。否则攻击者可能会在稍后阶段扫描内存或使用溢出。

EVP_CIPHER_CTX_free这个名字只表示应该释放CTX内存。但是释放内存并不意味着它首先会清除敏感信息。它只是返回到系统中,系统也没有义务覆盖它。另一方面,EVP_CIPHER_CTX_cleanup 在释放内存之前会明确清除此类信息(或者我认为它至少做了一次体面的尝试)。因此,您需要在提供密钥材料后调用此函数。

【讨论】:

我认为您的内存问题不在上下文中,请查看您的代码。您可能想尝试一些其他的东西,例如静态代码分析器。【参考方案3】:

好的,我想它现在已经清除了。如果您使用EVP 风格的加密/解密,请确保您像这样创建上下文:

  EVP_CIPHER_CTX ctx;
  EVP_CIPHER_CTX_init(&ctx);

然后像这样释放它:

EVP_CIPHER_CTX_cleanup(&ctx); 

不要使用EVP_CIPHER_CTX_new/EVP_CIPHER_CTX_free 来创建/释放上下文,它们已被弃用!

【讨论】:

我认为恰恰相反。实际上openssl-1.1.0使EVP_CIPHER_CTX成为不透明类型,所以上面第一种形式是编译错误 这个答案完全倒退,如其他答案中所述。请更新或取消接受。

以上是关于在 OpenSSL 中释放/分配上下文的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

共享内存实现上的 C++ 内存池:这种分配和释放方法是不是正确?

Linux程序:--OpenSSL库之相关数据结构和内存分配

如果程序因错误而提前退出,那么释放动态分配的内存的正确方法是啥?

释放返回变量内存的正确方法

为 segue 中传递的新 nsmanagedobject 释放分配的上下文

ssl 内存泄露