openssl的对称分块加密流程(AES的ECB模式)

1.主要流程为:

EVP_CIPHER_CTX_new创建加密上下文

EVP_EncryptInit_ex初始化加密上下文

EVP_EncryptUpdate开始加密

EVP_EncryptFinal_ex处理最后一块

EVP_CIPHER_CTX_free释放资源

2.函数原型如下

EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void);
用于创建一个新的加密上下文。
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv);
用于初始化加密上下文。
ctx:指向加密上下文的指针。
type:指向加密算法的类型。
impl:指向引擎实现的指针(通常为NULL)。
key:指向密钥的指针。
iv:指向初始化向量(IV)的指针。
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
用于执行分步加密操作。
ctx:指向加密上下文的指针。
out:指向输出缓冲区的指针,用于存储加密后的数据。
outl:指向一个整数,用于存储输出缓冲区中实际写入的字节数。
in:指向输入缓冲区的指针,包含要加密的数据。
inl:输入缓冲区中要加密的字节数。
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
用于处理最后一块数据并完成加密过程。
ctx:指向加密上下文的指针。
out:指向输出缓冲区的指针,用于存储加密后的数据。
outl:指向一个整数,用于存储输出缓冲区中实际写入的字节数。
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx);
用于释放加密上下文占用的资源。
ctx:指向要释放的加密上下文的指针。

3.官方文档

21.9 对称加密过程_OpenSSL 中文手册

4.EcbCIpher加解密类

ecbcipher.h

#ifndef _ECBCIPHER_H
#define _ECBCIPHER_H

#include <openssl/evp.h>
#include <vector>   
#include <stdexcept>

class ECBCipher {
public:
    ECBCipher();
    // 分块加密
    void encrypt_chunk(EVP_CIPHER_CTX* ctx, 
                      const unsigned char* plaintext, int plaintext_len,
                      unsigned char* ciphertext, int* ciphertext_len);
    // 加密(处理最后一块)
    void encrypt_final(EVP_CIPHER_CTX* ctx, unsigned char* ciphertext, int* ciphertext_len);
    // 分块解密
    void decrypt_chunk(EVP_CIPHER_CTX* ctx,
                      const unsigned char* ciphertext, int ciphertext_len,
                      unsigned char* plaintext, int* plaintext_len);
    // 解密(处理最后一块)
    void decrypt_final(EVP_CIPHER_CTX* ctx, unsigned char* plaintext, int* plaintext_len);
    // 创建加密上下文
    EVP_CIPHER_CTX* create_encrypt_ctx();
    // 创建解密上下文
    EVP_CIPHER_CTX* create_decrypt_ctx();
private:
    std::vector<unsigned char> key_;
};

#endif // ECBCIPHER_H

ecbcipher.cpp

#include "ecbcipher.h"

ECBCipher::ECBCipher() {
    const std::vector<unsigned char> encryption_key = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
    };
    key_ = encryption_key;
}
// 分块加密
void ECBCipher::encrypt_chunk(EVP_CIPHER_CTX* ctx, 
                    const unsigned char* plaintext, int plaintext_len,
                    unsigned char* ciphertext, int* ciphertext_len) 
{
    if (1 != EVP_EncryptUpdate(ctx, ciphertext, ciphertext_len, plaintext, plaintext_len)) {
        throw std::runtime_error("Encryption update failed");
    }
}
// 处理最后一块
void ECBCipher::encrypt_final(EVP_CIPHER_CTX* ctx, unsigned char* ciphertext, int* ciphertext_len) {
    if (1 != EVP_EncryptFinal_ex(ctx, ciphertext, ciphertext_len)) {
        throw std::runtime_error("Encryption final failed");
    }
}
// 分块解密
void ECBCipher::decrypt_chunk(EVP_CIPHER_CTX* ctx,
                    const unsigned char* ciphertext, int ciphertext_len,
                    unsigned char* plaintext, int* plaintext_len) 
{
    if (1 != EVP_DecryptUpdate(ctx, plaintext, plaintext_len, ciphertext, ciphertext_len)) {
        throw std::runtime_error("Decryption update failed");
    }
}
// 解密(处理最后一块)
void ECBCipher::decrypt_final(EVP_CIPHER_CTX* ctx, unsigned char* plaintext, int* plaintext_len) {
    if (1 != EVP_DecryptFinal_ex(ctx, plaintext, plaintext_len)) {
        throw std::runtime_error("Decryption final failed");
    }
}
// 创建加密上下文
EVP_CIPHER_CTX* ECBCipher::create_encrypt_ctx() {
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) throw std::runtime_error("Failed to create encryption context");
    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_ecb(), nullptr, key_.data(), nullptr)) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Encryption init failed");
    }
    EVP_CIPHER_CTX_set_padding(ctx, 1); // 启用PKCS#7填充
    return ctx;
}

// 创建解密上下文
EVP_CIPHER_CTX* ECBCipher::create_decrypt_ctx() {
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) throw std::runtime_error("Failed to create decryption context");
    if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), nullptr, key_.data(), nullptr)) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Decryption init failed");
    }
    EVP_CIPHER_CTX_set_padding(ctx, 1);
    return ctx;
}

该类实现了使用AES的ECB模式分块加密解密文件。在自己的项目中集成一下就行。

5.使用例子(发送端)

void FileSender::send_ecrypt_file(const std::string& file_path, tcp::socket& socket) {
    try {
        std::ifstream file(file_path, std::ios::binary);
        if (!file) throw std::runtime_error("Cannot open file: " + file_path);

        fs::path path_obj(file_path);
        uint64_t file_size = fs::file_size(path_obj);

        // 计算加密后大小 (考虑填充)
        uint64_t encrypted_size = file_size;
        if (file_size % BLOCK_SIZE != 0) {
            encrypted_size += BLOCK_SIZE - (file_size % BLOCK_SIZE);
        } else {
            // 即使是块大小的倍数也添加填充块
            encrypted_size += BLOCK_SIZE;
        }
        // 发送文件头
        send_header(file_path, encrypted_size, 0, socket);
        // 初始化加密上下文
        EVP_CIPHER_CTX* enc_ctx = ecbcipher_.create_encrypt_ctx();

        std::vector<unsigned char> plain_buf(ECB_BUFFER_SIZE);
        std::vector<unsigned char> cipher_buf(ECB_BUFFER_SIZE + BLOCK_SIZE);
        uint64_t total_sent = 0;

        while (total_sent < encrypted_size) {
            size_t chunk_size = std::min(
                ECB_BUFFER_SIZE,
                static_cast<size_t>(file_size - total_sent)
            );
            //读文件
            file.read(reinterpret_cast<char*>(plain_buf.data()), chunk_size);
            // 加密数据块
            int cipher_len = 0;
            ecbcipher_.encrypt_chunk(
                enc_ctx,
                plain_buf.data(), chunk_size,
                cipher_buf.data(), &cipher_len
            );
            // 发送加密数据
            if (cipher_len > 0) {
                asio::write(socket, asio::buffer(cipher_buf.data(), cipher_len));
                total_sent += cipher_len;
            }
            // 检查是否需要发送最后一块
            if (file.tellg() >= static_cast<std::streamoff>(file_size)) {
                int final_len = 0;
                ecbcipher_.encrypt_final(enc_ctx, cipher_buf.data(), &final_len);
                if (final_len > 0) {
                    asio::write(socket, asio::buffer(cipher_buf.data(), final_len));
                    total_sent += final_len;
                }
            }
        }

        EVP_CIPHER_CTX_free(enc_ctx);
    } catch (const std::exception& e) {
        //释放
        spdlog::error("File send error: {} - {}", e.what(), file_path);
    }
}

6.使用例子(接收端)

void Session::receive_decrypt_file(std::ofstream& file, const uint64_t encrypted_size){       
    // 初始化解密上下文
    EVP_CIPHER_CTX* dec_ctx = ecbcipher_.create_decrypt_ctx();
    try {
        std::vector<unsigned char> cipher_buf(ECB_BUFFER_SIZE);
        std::vector<unsigned char> plain_buf(ECB_BUFFER_SIZE + BLOCK_SIZE);
        uint64_t total_received = 0;
        //循环接收加密数据并解密(除最后一块外
        while (total_received < encrypted_size) {
            size_t to_read = std::min(ECB_BUFFER_SIZE,
                    static_cast<size_t>(encrypted_size - total_received));
            
            size_t bytes_read = asio::read(socket_, asio::buffer(cipher_buf.data(), to_read));

            int plain_len = 0;
            ecbcipher_.decrypt_chunk(dec_ctx,
                                     cipher_buf.data(), bytes_read,
                                     plain_buf.data(), &plain_len);

            if (plain_len > 0) {
                file.write(reinterpret_cast<char*>(plain_buf.data()), plain_len);
                if (!file) throw std::runtime_error("File write failed");
            }

            total_received += bytes_read;
        }
        // 处理最终块,去掉填充
        int final_len = 0;
        ecbcipher_.decrypt_final(dec_ctx, plain_buf.data(), &final_len);
        if (final_len > 0) {
            file.write(reinterpret_cast<char*>(plain_buf.data()), final_len);
            if (!file) throw std::runtime_error("Final file write failed");
        }
        EVP_CIPHER_CTX_free(dec_ctx);
    } catch (const std::exception& e) {
        EVP_CIPHER_CTX_free(dec_ctx);
        throw std::runtime_error("Decryption failed: " + std::string(e.what()));
    }
}
posted @ 2025-07-19 12:11  桂洛克船长  阅读(99)  评论(0)    收藏  举报