使用 openssl 计算 base64(防止内存泄露)

博客指出在使用OpenSSL进行资源释放时,网上很多帖子仅调用BIO_free_all()函数,实际使用中发现这样会产生内存泄露,因未查看源码,具体原因不明,猜测可能是低版本库有bug或设计如此。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include "openssl/bio.h"
#include "openssl/evp.h"
#include "openssl/buffer.h"

int base64(const unsigned char *src, int src_len, unsigned char *dst, int *dst_len) 
{
    BIO *bio_hnd_mem = NULL;
    BIO *bio_hnd_meth = NULL;
    BUF_MEM *buf_mem_ptr = NULL;

    if ((src == NULL) || (dst == NULL) || (dst_len == NULL)) {
        return -1;
    }

    bio_hnd_meth = BIO_new(BIO_f_base64());
    BIO_set_flags(bio_hnd_meth, BIO_FLAGS_BASE64_NO_NL);
    bio_hnd_mem = BIO_new(BIO_s_mem());
    bio_hnd_meth = BIO_push(bio_hnd_meth, bio_hnd_mem);

    BIO_write(bio_hnd_meth, src, src_len);
    BIO_flush(bio_hnd_meth);
    BIO_get_mem_ptr(bio_hnd_meth, &buf_mem_ptr);
    BIO_set_close(bio_hnd_meth, BIO_NOCLOSE);

    if (*dst_len < buf_mem_ptr->length) {
        return -1;
    }

    memcpy(dst, buf_mem_ptr->data, buf_mem_ptr->length);
    *dst_len = buf_mem_ptr->length;

    BUF_MEM_free(buf_mem_ptr);
    BIO_free(bio_hnd_meth);
    BIO_free(bio_hnd_mem);

    return *dst_len;
}

 

  注意:

结尾处的释放资源时我看到网上好多帖子上只调用了一个函数:BIO_free_all(bio_hnd_meth)。

但是,我在实际使用中发现如果只调用一个BIO_free_all(),会产生内存泄露,主要是没有释放掉buf_mem_ptr指向的内存。

由于我还没看openssl库源码,因此具体原因尚不明确。猜测可能是低版本的openssl库有bug,可能高版本解决了这个bug,也可能是openssl本身设计就是如此只是使用者没有搞明白而已。

 

 

下面程序读取public.pem文件内容作为公钥并加密文件,现在我希望直接将公钥内容硬编码到代码中,假如我的public.pem文件内容为: -----BEGIN PUBLIC KEY----- xxxxxxxx -----END PUBLIC KEY----- 我的代码为: #include <openssl/evp.h> #include <openssl/pem.h> #include <openssl/rand.h> #include <openssl/err.h> #include <openssl/applink.c> #include <stdio.h> #include <stdlib.h> #define AES_KEY_SIZE 32 #define RSA_KEY_SIZE 2048 #define GCM_IV_LEN 12 #define GCM_TAG_LEN 16 #define CHECK(cond, msg) do { if (!(cond)) { fprintf(stderr, “错误: %s (位置: %s:%d)\n”, msg, FILE, LINE); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } while(0) EVP_PKEY* load_public_key(const char* pubkey_path) { BIO* bio = BIO_new_file(pubkey_path, “r”); CHECK(bio != NULL, “无法打开公钥文件”); EVP_PKEY* pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); CHECK(pubkey != NULL, “解析公钥失败”); BIO_free(bio); return pubkey; } unsigned char* rsa_encrypt(EVP_PKEY* pubkey, const unsigned char* aes_key, size_t* encrypted_len) { EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pubkey, NULL); CHECK(ctx != NULL, “创建RSA上下文失败”); CHECK(EVP_PKEY_encrypt_init(ctx) > 0, “初始化RSA加密失败”); size_t outlen; CHECK(EVP_PKEY_encrypt(ctx, NULL, &outlen, aes_key, AES_KEY_SIZE) > 0, “计算缓冲区大小失败”); unsigned char* encrypted = malloc(outlen); CHECK(encrypted != NULL, “内存分配失败”); CHECK(EVP_PKEY_encrypt(ctx, encrypted, &outlen, aes_key, AES_KEY_SIZE) > 0, “RSA加密失败”); encrypted_len = outlen; EVP_PKEY_CTX_free(ctx); return encrypted; } void aes_gcm_encrypt( const unsigned char plaintext, size_t plaintext_len, const unsigned char* aes_key, unsigned char* iv, unsigned char* tag, unsigned char** ciphertext, size_t* ciphertext_len) { EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); CHECK(ctx != NULL, “创建AES上下文失败”); CHECK(RAND_bytes(iv, GCM_IV_LEN) > 0, “生成IV失败”); CHECK(EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, aes_key, iv) > 0, “初始化AES失败”); ciphertext = malloc(plaintext_len + EVP_MAX_BLOCK_LENGTH); CHECK(ciphertext != NULL, “内存分配失败”); int len; CHECK(EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) > 0, “加密失败”); ciphertext_len = len; CHECK(EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) > 0, “最终加密失败”); ciphertext_len += len; CHECK(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, GCM_TAG_LEN, tag) > 0, “获取标签失败”); EVP_CIPHER_CTX_free(ctx); } int main() { OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); unsigned char aes_key[AES_KEY_SIZE]; CHECK(RAND_bytes(aes_key, sizeof(aes_key)) > 0, “生成AES密钥失败”); EVP_PKEY pubkey = load_public_key(“public.pem”); size_t encrypted_aes_len; unsigned char encrypted_aes = rsa_encrypt(pubkey, aes_key, &encrypted_aes_len); FILE input_file = fopen(“1.txt”, “rb”); CHECK(input_file != NULL, “无法打开输入文件”); fseek(input_file, 0, SEEK_END); long plaintext_len = ftell(input_file); fseek(input_file, 0, SEEK_SET); unsigned char plaintext = malloc(plaintext_len); CHECK(plaintext != NULL, “内存分配失败”); fread(plaintext, 1, plaintext_len, input_file); fclose(input_file); unsigned char iv[GCM_IV_LEN]; unsigned char tag[GCM_TAG_LEN]; unsigned char ciphertext; size_t ciphertext_len; aes_gcm_encrypt((unsigned char)plaintext, plaintext_len, aes_key, iv, tag, &ciphertext, &ciphertext_len); FILE* out = fopen(“encrypted.bin”, “wb”); CHECK(out != NULL, “无法创建输出文件”); fwrite(&encrypted_aes_len, sizeof(size_t), 1, out); fwrite(encrypted_aes, 1, encrypted_aes_len, out); fwrite(iv, 1, GCM_IV_LEN, out); fwrite(tag, 1, GCM_TAG_LEN, out); fwrite(&ciphertext_len, sizeof(size_t), 1, out); fwrite(ciphertext, 1, ciphertext_len, out); fclose(out); free(encrypted_aes); free(ciphertext); EVP_PKEY_free(pubkey); printf(“加密成功!输出文件: encrypted.bin\n”); return 0; }
03-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值