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

OpenSSL AES-256-CBC加解密实现密钥协商时客户端解密失败求助

解密失败问题分析与修复方案

核心问题:解密顺序完全颠倒

服务器发送消息的顺序是:

  • 第一份消息:用skey加密的sessionKey(concatenatedMessage)
  • 第二份消息:用sessionKey加密的验证消息(concatenatedMessage2)

但客户端代码中,先尝试用skey解密第二份消息(concatenatedMessage2),而这份消息是用客户端尚未获取的sessionKey加密的,必然解密失败。

具体修复步骤

1. 调整客户端解密顺序

将客户端的解密逻辑调换,先解密第一份消息获取sessionKey,再用它解密第二份消息:

// 先处理第一份消息:解密获取sessionKey
EVP_CIPHER_CTX* ctx_decrypt = EVP_CIPHER_CTX_new();
unsigned char decryptedSessionKey[1024] = {0}; // 用unsigned char存储二进制密钥,避免符号问题
int sessionKeyLen = 0;

// 直接初始化解密模式上下文(最后一个参数为0表示解密)
if (EVP_CipherInit_ex(ctx_decrypt, EVP_aes_256_cbc(), NULL, NULL, NULL, 0) != 1) {
    fprintf(stderr, "Error initializing the cipher context\n");
    EVP_CIPHER_CTX_free(ctx_decrypt);
    return;
}

// 设置密钥长度与skey
if (EVP_CIPHER_CTX_set_key_length(ctx_decrypt, skeylen) != 1) {
    fprintf(stderr, "Error setting the key length\n");
    EVP_CIPHER_CTX_free(ctx_decrypt);
    return;
}

if (EVP_CipherInit_ex(ctx_decrypt, NULL, NULL, skey, NULL, 0) != 1) {
    fprintf(stderr, "Error setting the session key\n");
    EVP_CIPHER_CTX_free(ctx_decrypt);
    return;
}

unsigned char iv[EVP_MAX_IV_LENGTH];
// 提取第一份消息中的IV
memcpy(iv, concatenatedMessage1, EVP_MAX_IV_LENGTH);

// 设置解密IV
if (EVP_DecryptInit_ex(ctx_decrypt, EVP_aes_256_cbc(), NULL, skey, iv) != 1) {
    fprintf(stderr, "Error setting decryption IV\n");
    exit(1);
}

// 解密第一份消息的密文部分
if (EVP_DecryptUpdate(ctx_decrypt, decryptedSessionKey, &sessionKeyLen, concatenatedMessage1 + EVP_MAX_IV_LENGTH, concatenatedMessageLength1 - EVP_MAX_IV_LENGTH) != 1) {
    fprintf(stderr, "Error decrypting session key\n");
    exit(1);
}

// 完成解密
int final_len;
if (EVP_DecryptFinal_ex(ctx_decrypt, decryptedSessionKey + sessionKeyLen, &final_len) != 1) {
    fprintf(stderr, "Error finalizing session key decryption\n");
    exit(1);
}
sessionKeyLen += final_len;

printf("Decrypted Session Key length: %d\n", sessionKeyLen);
EVP_CIPHER_CTX_free(ctx_decrypt);

// 再处理第二份消息:用解密得到的sessionKey解密验证消息
EVP_CIPHER_CTX* ctx_decrypt2 = EVP_CIPHER_CTX_new();
char decryptedVerifyMsg[1024] = {0};
int verifyMsgLen = 0;

// 初始化解密上下文
if (EVP_CipherInit_ex(ctx_decrypt2, EVP_aes_256_cbc(), NULL, NULL, NULL, 0) != 1) {
    fprintf(stderr, "Error initializing the cipher context\n");
    EVP_CIPHER_CTX_free(ctx_decrypt2);
    return;
}

// 设置密钥长度为解密得到的sessionKey长度
if (EVP_CIPHER_CTX_set_key_length(ctx_decrypt2, sessionKeyLen) != 1) {
    fprintf(stderr, "Error setting the key length\n");
    EVP_CIPHER_CTX_free(ctx_decrypt2);
    return;
}

if (EVP_CipherInit_ex(ctx_decrypt2, NULL, NULL, decryptedSessionKey, NULL, 0) != 1) {
    fprintf(stderr, "Error setting the session key\n");
    EVP_CIPHER_CTX_free(ctx_decrypt2);
    return;
}

// 提取第二份消息中的IV
memcpy(iv, concatenatedMessage2, EVP_MAX_IV_LENGTH);

// 设置解密IV
if (EVP_DecryptInit_ex(ctx_decrypt2, EVP_aes_256_cbc(), NULL, decryptedSessionKey, iv) != 1) {
    fprintf(stderr, "Error setting decryption IV\n");
    exit(1);
}

// 解密验证消息
if (EVP_DecryptUpdate(ctx_decrypt2, (unsigned char*)decryptedVerifyMsg, &verifyMsgLen, concatenatedMessage2 + EVP_MAX_IV_LENGTH, concatenatedMessageLength2 - EVP_MAX_IV_LENGTH) != 1) {
    fprintf(stderr, "Error decrypting verification message\n");
    exit(1);
}

// 完成解密
if (EVP_DecryptFinal_ex(ctx_decrypt2, (unsigned char*)decryptedVerifyMsg + verifyMsgLen, &final_len) != 1) {
    fprintf(stderr, "Error finalizing verification message decryption\n");
    exit(1);
}
verifyMsgLen += final_len;

printf("Decrypted Verification Message: %.*s\n", verifyMsgLen, decryptedVerifyMsg);
EVP_CIPHER_CTX_free(ctx_decrypt2);

2. 修复服务器端冗余的上下文初始化

服务器端加密第一份消息时,重复调用EVP_EncryptInit_ex可能导致潜在问题,简化为:

// 初始化加密上下文,直接设置算法、密钥、IV
EVP_CIPHER_CTX* ctx_encrypt = EVP_CIPHER_CTX_new();
const EVP_CIPHER* cipher = EVP_aes_256_cbc();
unsigned char iv[EVP_MAX_IV_LENGTH];

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

// 一步完成加密上下文初始化
if (EVP_EncryptInit_ex(ctx_encrypt, cipher, NULL, skey, iv) != 1) {
    fprintf(stderr, "Error initializing encryption context\n");
    EVP_CIPHER_CTX_free(ctx_encrypt);
    return;
}

// 后续加密操作保持不变...

3. 二进制数据处理注意事项

  • sessionKey是二进制密钥,必须用unsigned char数组存储解密结果,避免符号扩展导致的密钥错误。
  • 禁止用strlen处理二进制数据长度,始终使用加密/解密过程中返回的长度值。

额外检查点

  • 确认客户端接收的concatenatedMessage1和concatenatedMessageLength1与服务器发送的完全一致,排查网络传输中的截断或字节序问题。
  • 确保skey长度符合AES-256要求(32字节),密钥长度不匹配会直接导致解密失败。

内容的提问来源于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.19 01:52:02