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
相关产品推荐
相关产品推荐

