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.官方文档
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()));
}
}

浙公网安备 33010602011771号