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

基于OpenSSL的C/S加解密故障求助:EVP_DecryptFinal_ex解密报错

问题排查:OpenSSL AES-256-CBC解密会话密钥时EVP_DecryptFinal_ex报错

我基于OpenSSL实现客户端-服务器加解密模型:服务器用已建立的密钥skey,通过AES-256-CBC加密会话密钥字节数组,同时用该会话密钥加密验证消息"Verification Message SessionKey",将IV与密文拼接后分两条发送给客户端。客户端接收后,先用skey解密第一条消息(对应服务器的会话密钥密文),但执行EVP_DecryptFinal_ex步骤时报错。调试发现解密文本长度应为32字节,但decryptedText1占用48字节。此前仅加密验证消息时逻辑正常,现在因加密对象为字节数组出现问题,已卡两周,求排查。


服务端代码

// Generate an initialization vector (IV) for CBC mode
const EVP_CIPHER* cipher = EVP_aes_256_cbc(); // Use the desired cipher algorithm
EVP_CIPHER_CTX* ctx_encrypt = EVP_CIPHER_CTX_new();
const unsigned char* plaintext = sessionKey;
size_t plaintext_len = sessionKeyLen;
const int MAX_TEXT_LENGTH = 256;
unsigned char iv[EVP_MAX_IV_LENGTH];

if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) {
    fprintf(stderr, "Error generating IV\n");
    exit(1);
}

// Set the IV for the encryption operation
if (EVP_EncryptInit_ex(ctx_encrypt, EVP_aes_256_cbc(), NULL, skey, iv) != 1) {
    fprintf(stderr, "Error setting encryption IV\n");
    exit(1);
}

// Encrypt the plaintext (session key)
unsigned char ciphertext[MAX_TEXT_LENGTH + EVP_MAX_BLOCK_LENGTH];
int ciphertext_len;

if (EVP_EncryptUpdate(ctx_encrypt, ciphertext, &ciphertext_len, plaintext, plaintext_len) != 1) {
    fprintf(stderr, "Error encrypting data\n");
    exit(1);
}

// Finalize the encryption
int final_len;
if (EVP_EncryptFinal_ex(ctx_encrypt, ciphertext + ciphertext_len, &final_len) != 1) {
    fprintf(stderr, "Error finalizing encryption\n");
    exit(1);
}
ciphertext_len += final_len;

const size_t ivLength = EVP_CIPHER_iv_length(EVP_aes_256_cbc()); // Get the length of the IV
const size_t concatenatedMessageLength = ivLength + ciphertext_len;
unsigned char concatenatedMessage[concatenatedMessageLength];

memcpy(concatenatedMessage, iv, ivLength);
memcpy(concatenatedMessage + ivLength, ciphertext, ciphertext_len);

printf("Concatenated Message 1:\n");
for (size_t i = 0; i < concatenatedMessageLength; i++) {
    printf("%02x", concatenatedMessage[i]);
}
printf("\n");

EVP_CIPHER_CTX_free(ctx_encrypt);


// Preparing for Bob's share

// Generate an initialization vector (IV) for CBC mode
plaintext = (const unsigned char*)"Verification Message SessionKey";
plaintext_len = strlen((const char*)plaintext);
EVP_CIPHER_CTX* ctx_encrypt2 = EVP_CIPHER_CTX_new();

if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) {
    fprintf(stderr, "Error generating IV\n");
    exit(1);
}

// Set the IV for the encryption operation
if (EVP_EncryptInit_ex(ctx_encrypt2, EVP_aes_256_cbc(), NULL, sessionKey, iv) != 1) {
    fprintf(stderr, "Error setting encryption IV\n");
    exit(1);
}

// Encrypt the plaintext (Bob's share)
if (EVP_EncryptUpdate(ctx_encrypt2, ciphertext, &ciphertext_len, plaintext, plaintext_len) != 1) {
    fprintf(stderr, "Error encrypting data\n");
    exit(1);
}

// Finalize the encryption
if (EVP_EncryptFinal_ex(ctx_encrypt2, ciphertext + ciphertext_len, &final_len) != 1) {
    fprintf(stderr, "Error finalizing encryption\n");
    exit(1);
}
ciphertext_len += final_len;
const size_t concatenatedMessageLength2 = ivLength + ciphertext_len;
unsigned char concatenatedMessage2[concatenatedMessageLength2];

memcpy(concatenatedMessage2, iv, ivLength);
memcpy(concatenatedMessage2 + ivLength, ciphertext, ciphertext_len);

printf("Concatenated Message 2:\n");
for (size_t i = 0; i < concatenatedMessageLength2; i++) {
    printf("%02x", concatenatedMessage2[i]);
}
printf("\n");

EVP_CIPHER_CTX_free(ctx_encrypt2);

// Sending Server Share and Verification Message
if (concatenatedMessageLength > 0) {
    send(client_sock_Bob, concatenatedMessage, concatenatedMessageLength, 0);
    // Send the encrypted hash to the server
    usleep(100);
    if (concatenatedMessageLength2 > 0) {
        send(client_sock_Bob, concatenatedMessage2, concatenatedMessageLength2, 0);
    } else {
        printf("Server Verification message is Null: %s\n");
    }
} else {
    printf("Session key is Null : %s\n");
}

客户端代码

// Receive concatenatedMessage2
char receivedMessage1[4096];
ssize_t concatenatedMessageLength1 = recv(sock, receivedMessage1, sizeof(receivedMessage1), 0);
if (concatenatedMessageLength1 > 0) {
memcpy(concatenatedMessage1, receivedMessage1, concatenatedMessageLength1);
} else if (concatenatedMessageLength1 == 0) {
// Connection closed by the server
fprintf(stderr, "Connection closed by the server.\n");
exit(1);
} else {
// Error receiving data
fprintf(stderr, "Error receiving concatenatedMessage2.\n");
exit(1);
}


// Receive concatenatedMessage1
char receivedMessage2[4096];
ssize_t concatenatedMessageLength2 = recv(sock, receivedMessage2, sizeof(receivedMessage2), 0);
if (concatenatedMessageLength2 > 0) {
memcpy(concatenatedMessage2, receivedMessage2, concatenatedMessageLength2);
} else if (concatenatedMessageLength2 == 0) {
// Connection closed by the server
fprintf(stderr, "Connection closed by the server.\n");
exit(1);
} else {
// Error receiving data
fprintf(stderr, "Error receiving concatenatedMessage1.\n");
exit(1);
}

bzero(buffer, 1024);

const EVP_CIPHER* cipher = EVP_aes_256_cbc(); // Use the desired cipher algorithm
EVP_CIPHER_CTX* ctx_decrypt = EVP_CIPHER_CTX_new();
unsigned char decryptedText1[MAX_TEXT_LENGTH + EVP_MAX_BLOCK_LENGTH];
unsigned char decryptedText2[MAX_TEXT_LENGTH + EVP_MAX_BLOCK_LENGTH];
int decryptedText1_len;
int decryptedText2_len;
memset(decryptedText1, 0, sizeof(decryptedText1));

// Extract the IV from the concatenated message 2
memcpy(iv, concatenatedMessage1, EVP_MAX_IV_LENGTH);

// Set the IV for the decryption operation
if (EVP_DecryptInit_ex(ctx_decrypt, EVP_aes_256_cbc(), NULL, skey, iv) != 1) {
fprintf(stderr, "Error setting decryption IV\n");
exit(1);
}

// Decrypt the ciphertext
if (EVP_DecryptUpdate(ctx_decrypt, decryptedText1, &decryptedText1_len, concatenatedMessage1 + EVP_MAX_IV_LENGTH, concatenatedMessageLength1 - EVP_MAX_IV_LENGTH) != 1) {
fprintf(stderr, "Error decrypting data\n");
exit(1);
}

// Finalize the decryption
int final_len1;
if (EVP_DecryptFinal_ex(ctx_decrypt, decryptedText1 + decryptedText1_len, &final_len1) != 1) {
fprintf(stderr, "Error finalizing decryption\n");
exit(1);
}
decryptedText1_len += final_len1;

// Print the decrypted plaintext
printf("Decrypted Text: %.*s\n", decryptedText1_len, decryptedText1);
EVP_CIPHER_CTX_free(ctx_decrypt);



EVP_CIPHER_CTX* ctx_decrypt2 = EVP_CIPHER_CTX_new();
// Initialize the encryption/decryption context
if (EVP_CipherInit_ex(ctx_decrypt2, cipher, NULL, NULL, NULL, 1) != 1) {
fprintf(stderr, "Error initializing the cipher context\n");
EVP_CIPHER_CTX_free(ctx_decrypt2);
return;
}

// Extract the IV from the concatenated message 1
memcpy(iv, concatenatedMessage2, EVP_MAX_IV_LENGTH);

// Set the IV for the decryption operation
if (EVP_DecryptInit_ex(ctx_decrypt2, EVP_aes_256_cbc(), NULL, decryptedText2, iv) != 1) {
fprintf(stderr, "Error setting decryption IV\n");
exit(1);
}

// Decrypt the ciphertext
if (EVP_DecryptUpdate(ctx_decrypt2, decryptedText2, &decryptedText2_len, concatenatedMessage2 + EVP_MAX_IV_LENGTH, concatenatedMessageLength2 - EVP_MAX_IV_LENGTH) != 1) {
fprintf(stderr, "Error decrypting data\n");
exit(1);
}

// Finalize the decryption
int final_len2;
if (EVP_DecryptFinal_ex(ctx_decrypt2, decryptedText2 + decryptedText2_len, &final_len2) != 1) {
fprintf(stderr, "Error finalizing decryption\n");
exit(1);
}
decryptedText2_len += final_len2;

// Print the decrypted plaintext
printf("Decrypted Text: %.*s\n", decryptedText2_len, decryptedText2);

// Check if the decrypted message matches the expected message
const unsigned char* expectedMessage2 = (const unsigned char*)"Verification Message SessionKey";
if (memcmp(decryptedText2, expectedMessage2, decryptedText2_len) == 0) {
printf("Message verified\n");
const unsigned char* DoneMessage = (const unsigned char*)"Bob Hello Done";
ssize_t sent = send(sock, DoneMessage, strlen((const char*)DoneMessage), MSG_NOSIGNAL);
if (sent < 0) {
    perror("[-]Error sending done message");
    exit(1);
}
printf("Done message sent\n");
} else {
printf("Message unverified\n");
}
bzero(buffer, 1024);

报错代码行

// Finalize the decryption
int final_len1;
if (EVP_DecryptFinal_ex(ctx_decrypt, decryptedText1 + decryptedText1_len, &final_len1) != 1) {
fprintf(stderr, "Error finalizing decryption\n");
exit(1);
}

核心问题排查与修复

  • 消息接收顺序完全颠倒:服务器先发送加密会话密钥的concatenatedMessage,再发送加密验证消息的concatenatedMessage2,但客户端把第一条收到的消息当成验证消息,第二条当成会话密钥消息,导致解密用错IV和密文,触发EVP_DecryptFinal_ex失败。
  • 第二次解密密钥参数错误:客户端解密验证消息时,EVP_DecryptInit_ex传入的密钥是未初始化的decryptedText2,正确应该是刚解密出的会话密钥decryptedText1。
  • 冗余的上下文初始化:客户端中EVP_CipherInit_ex(ctx_decrypt2, cipher, NULL, NULL, NULL, 1)属于冗余操作,且设置为加密模式,后续调用解密初始化会导致上下文混乱,直接删除即可。
  • 缓冲区未明确初始化:客户端中concatenatedMessage1和concatenatedMessage2未提前声明并初始化,直接memcpy存在越界或垃圾数据风险,建议声明为unsigned char concatenatedMessage1[4096] = {0};这类明确的缓冲区。

修复后的关键代码片段

客户端接收部分修正

// 先接收加密会话密钥消息
unsigned char concatenatedMessage1[4096] = {0};
char receivedMessage1[4096];
ssize_t concatenatedMessageLength1 = recv(sock, receivedMessage1, sizeof(receivedMessage1), 0);
if (concatenatedMessageLength1 > 0) {
    memcpy(concatenatedMessage1, receivedMessage1, concatenatedMessageLength1);
} else if (concatenatedMessageLength1 == 0) {
    fprintf(stderr, "服务器关闭连接。\n");
    exit(1);
} else {
    fprintf(stderr, "接收加密会话密钥消息失败。\n");
    exit(1);
}

// 再接收加密验证消息
unsigned char concatenatedMessage2[4096] = {0};
char receivedMessage2[4096];
ssize_t concatenatedMessageLength2 = recv(sock, receivedMessage2, sizeof(receivedMessage2), 0);
if (concatenatedMessageLength2 > 0) {
    memcpy(concatenatedMessage2, receivedMessage2, concatenatedMessageLength2);
} else if (concatenatedMessageLength2 == 0) {
    fprintf(stderr, "服务器关闭连接。\n");
    exit(1);
} else {
    fprintf(stderr, "接收加密验证消息失败。\n");
    exit(1);
}

客户端第二次解密修正

EVP_CIPHER_CTX* ctx_decrypt2 = EVP_CIPHER_CTX_new();
memcpy(iv, concatenatedMessage2, EVP_MAX_IV_LENGTH);
// 使用解密得到的会话密钥作为解密验证消息的密钥
if (EVP_DecryptInit_ex(ctx_decrypt2, EVP_aes_256_cbc(), NULL, decryptedText1, iv) != 1) {
    fprintf(stderr, "设置解密IV失败\n");
    exit(1);
}

if (EVP_DecryptUpdate(ctx_decrypt2, decryptedText2, &decryptedText2_len, concatenatedMessage2 + EVP_MAX_IV_LENGTH, concatenatedMessageLength2 - EVP_MAX_IV_LENGTH) != 1) {
    fprintf(stderr, "解密验证消息数据失败\n");
    exit(1);
}

int final_len2;
if (EVP_DecryptFinal_ex(ctx_decrypt2, decryptedText2 + decryptedText2_len, &final_len2) != 1) {
    fprintf(stderr, "完成验证消息解密失败\n");
    exit(1);
}
decryptedText2_len += final_len2;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 05:46:57