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

如何优化基于OpenSSL的AES-128 CBC模式C语言目录加密性能

优化基于OpenSSL的AES-128 CBC递归目录加密程序的性能方案

问题背景

现有一个基于OpenSSL AES-128 CBC模式的C语言程序,可递归加密指定目录及子目录下的所有文件,功能运行正常,但加密速度有待提升,尝试线程优化未成功,寻求可行的性能优化方案。

程序功能说明

该程序核心功能如下:

  • encrypt_file:负责加密单个文件,加密完成后生成带.enc后缀的加密文件
  • encrypt_directory:通过readdir遍历目标目录,递归处理子目录,调用encrypt_file加密常规文件

加密实现细节:采用AES-128 CBC模式,随机生成Key和IV并写入加密文件头部,通过EVP_EncryptUpdate分块加密数据,EVP_EncryptFinal_ex处理最终加密块。

程序代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "dirent.h"
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/aes.h>

#define AES_BLOCK_SIZE 16

int encrypt_file(const char* in_filename, const char* out_filename)
{
    EVP_CIPHER_CTX* ctx;
    int len;
    int ciphertext_len;
    FILE* in_file, * out_file;
    unsigned char key[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];
    unsigned char in_buf[AES_BLOCK_SIZE];
    unsigned char out_buf[AES_BLOCK_SIZE + EVP_MAX_BLOCK_LENGTH];

    /* Generate random key and IV */
    if (!RAND_bytes(key, AES_BLOCK_SIZE))
        return 0;
    if (!RAND_bytes(iv, AES_BLOCK_SIZE))
        return 0;

    /* Initialize the encryption context */
    if (!(ctx = EVP_CIPHER_CTX_new()))
        return 0;
    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        return 0;

    /* Open the input and output files */
    if (!(in_file = fopen(in_filename, "rb")))
        return 0;
    if (!(out_file = fopen(out_filename, "wb")))
        return 0;

    /* Write the key and IV to the output file */
    if (fwrite(key, 1, AES_BLOCK_SIZE, out_file) != AES_BLOCK_SIZE)
        return 0;
    if (fwrite(iv, 1, AES_BLOCK_SIZE, out_file) != AES_BLOCK_SIZE)
        return 0;

    /* Encrypt and write the ciphertext to the output file */
    while ((len = fread(in_buf, 1, AES_BLOCK_SIZE, in_file))) {
        if (1 != EVP_EncryptUpdate(ctx, out_buf, &ciphertext_len, in_buf, len))
            return 0;
        if (fwrite(out_buf, 1, ciphertext_len, out_file) != ciphertext_len)
            return 0;
    }

    /* Finalize the encryption */
    if (1 != EVP_EncryptFinal_ex(ctx, out_buf, &ciphertext_len))
        return 0;
    if (fwrite(out_buf, 1, ciphertext_len, out_file) != ciphertext_len)
        return 0;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);
    fclose(in_file);
    fclose(out_file);

    return 1;
}

void encrypt_directory(const char* dir_name)
{
    DIR* dir;
    struct dirent* entry;
    char path[1024];

    /* Open the directory specified by dir_name */
    if (!(dir = opendir(dir_name)))
        return;

    /* Read each entry from the directory */
    while ((entry = readdir(dir))) {
        /* Ignore the current and parent directories */
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        /* Construct the full path of the entry */
        snprintf(path, sizeof(path), "%s/%s", dir_name, entry->d_name);

        /* If the entry is a directory, call encrypt_directory recursively */
        if (entry->d_type == DT_DIR) {
            encrypt_directory(path);
        }
        /* If the entry is a file, encrypt it */
        else if (entry->d_type == DT_REG) {
            /* Construct the output filename by appending ".enc" to the original filename */
            char out_filename[1024];
            strcpy(out_filename, path);
            strcat(out_filename, ".enc");

            /* Call encrypt_file to encrypt the file */
            if (encrypt_file(path, out_filename)) {
                /* If encryption was successful, delete the original file */
                remove(path);
            }
        }
    }

    /* Close the directory */
    closedir(dir);
}

可行的性能优化方案

1. 优化文件IO操作

  • 增大读写缓冲区:当前代码仅使用16字节的缓冲区,IO调用过于频繁,是主要性能瓶颈之一。将缓冲区大小调整为4KB、8KB甚至64KB(例如#define BUF_SIZE 65536),大幅减少fread/fwrite的调用次数,降低IO开销。
  • 替换高效IO接口:用系统级IO接口(如Linux的open/read/write)替代标准C库的fopen/fread/fwrite,配合O_DIRECT标志跳过内核缓冲区(需保证缓冲区与磁盘扇区对齐);或使用mmap将文件映射到内存,减少用户态与内核态的数据拷贝次数。

2. 线程池实现多文件并行加密

之前线程优化失败大概率是因为递归遍历过程中直接创建线程,导致线程管理混乱且开销过高。改用线程池方案:

  • 先收集所有文件路径:递归遍历目录时,将所有需要加密的文件路径存入线程安全的任务队列,不直接执行加密操作。
  • 创建固定大小线程池:根据CPU核心数创建线程(如4核CPU创建4-8个线程),每个线程从任务队列中取出文件路径,调用encrypt_file处理。
  • 保证线程安全:任务队列需用互斥锁+条件变量实现生产者-消费者模型,避免多线程竞争问题。

3. OpenSSL相关优化

  • 启用硬件加速:确保OpenSSL编译时开启了CPU硬件加速支持(如AES-NI指令集),可通过openssl engine -t验证硬件引擎是否可用。代码中可显式初始化硬件加速,或调用EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_NON_FIPS_ALLOW)开启相关优化。
  • 复用EVP_CIPHER_CTX:当前每个文件加密都创建并销毁EVP_CIPHER_CTX,开销较大。可为每个线程分配独立的EVP_CIPHER_CTX,重复使用,减少初始化与销毁的开销。
  • 权衡随机数生成开销:当前每个文件都调用RAND_bytes生成Key和IV,若安全需求允许,可改用主密钥派生每个文件的子密钥,减少随机数生成的次数(随机数生成是相对耗时的操作)。

4. 其他优化点

  • 批量处理减少磁盘寻道:遍历目录时,将同一目录下的文件批量提交给线程池,减少磁盘频繁切换文件的寻道时间。
  • 异步处理文件删除:加密成功后立即删除原文件会阻塞加密流程,可将删除操作放入后台线程处理,或批量完成加密后统一删除。
  • 编译优化:编译时开启-O2或-O3优化选项,让编译器生成更高效的机器码,提升整体执行速度。

内容的提问来源于stack exchange,提问作者cashy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 08:20:49