在 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_cleanup
和 EVP_CIPHER_CTX_init
函数。所以基本上应该正确使用什么,EVP_CIPHER_CTX_new
/EVP_CIPHER_CTX_free
是否已被弃用? EVP_CIPHER_CTX_new
/EVP_CIPHER_CTX_free
和EVP_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
memset
和calloc
感到好奇,这里有一个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库之相关数据结构和内存分配
如果程序因错误而提前退出,那么释放动态分配的内存的正确方法是啥?