C语言中OpenSSL DES解密失败问题排查与解决求助
文件加密传输程序DES解密报错问题排查
问题场景
我编写文件加密传输程序时,遇到DES解密函数报错的问题。以下是部分代码:
#define BUF_SIZE 1024 char buf[BUF_SIZE], decrypted[BUF_SIZE+EVP_MAX_BLOCK_LENGTH]; int cipher_size, decrypted_size; while(1) { read(clnt_sock,(char*)&cipher_size,sizeof(cipher_size)); read(clnt_sock,buf,cipher_size); des_decrypt((unsigned char *)buf,cipher_size,(unsigned char*)argv[2], (unsigned char*)decrypted,&decrypted_size); fwrite(buf,1,decrypted_size,fp); // 此处存在逻辑错误 } void des_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext, int *plaintext_len) { EVP_CIPHER_CTX *ctx; int len; if(!(ctx = EVP_CIPHER_CTX_new())) { return; } if(1 != EVP_DecryptInit_ex(ctx, EVP_des_ecb(), NULL, key, NULL)) { err("DES init error!"); return; } if(1 != EVP_DecryptUpdate(ctx, plaintext, plaintext_len, ciphertext, ciphertext_len)) { err("Decrypt error!"); return; // 程序总是在此处返回,plaintext_len为0 } if(1 != EVP_DecryptFinal_ex(ctx, plaintext + *plaintext_len, &len)) { err("Final Decrypt error"); return; } *plaintext_len += len; EVP_CIPHER_CTX_free(ctx); }
调试确认EVP_DecryptInit_ex返回值始终为1(初始化成功),但des_decrypt函数总是在EVP_DecryptUpdate调用失败的return语句处返回,此时plaintext_len的值为0。运行环境为Ubuntu 22.x,需排查问题成因及解决方法。
问题成因分析
- 密钥长度不符合DES要求:DES算法强制要求密钥长度为8字节(64位,含8位奇偶校验位)。如果传入的
argv[2]长度不足8字节,或超过8字节未做截断,会直接导致EVP_DecryptUpdate执行失败。 - 密文长度不合法:DES是分组密码,ECB模式下密文长度必须是8字节的整数倍。若读取到的
cipher_size对应的密文长度不满足该条件,解密操作会失败。此外,发送端与接收端未统一cipher_size的字节序(如未用htons/ntohs处理网络字节序),会导致读取的密文长度错误或数据损坏。 - 未初始化输出长度变量:调用
des_decrypt前,decrypted_size未初始化为0,EVP_DecryptUpdate写入解密长度时会基于垃圾值操作,触发内部逻辑错误。 - 代码逻辑错误:原代码中
fwrite(buf,1,decrypted_size,fp)是严重错误,应写入解密后的decrypted数组而非原始密文buf,该错误不直接导致解密失败,但会引发后续输出异常。 - 填充方式不匹配:OpenSSL的DES默认使用PKCS#7填充,若发送端加密时未使用填充(如数据长度恰好为块大小整数倍),接收端未禁用填充会导致解密失败。
解决方法
- 修正密钥长度:确保传入的密钥为8字节长度,可通过截断或补0处理用户输入的密钥:
unsigned char fixed_key[8] = {0}; // 截断或复制密钥至8字节 memcpy(fixed_key, argv[2], strlen((char*)argv[2]) > 8 ? 8 : strlen((char*)argv[2])); // 调用des_decrypt时传入fixed_key des_decrypt((unsigned char *)buf,cipher_size,fixed_key,(unsigned char*)decrypted,&decrypted_size); - 校验并修正密文长度:
- 统一字节序:发送端用
htonl转换cipher_size为网络字节序,接收端用ntohl转换回主机字节序:read(clnt_sock,(char*)&cipher_size,sizeof(cipher_size)); cipher_size = ntohl(cipher_size); // 转换为主机字节序 - 解密前检查密文长度是否为8的倍数,若不符合则丢弃数据或报错:
if (cipher_size % 8 != 0) { fprintf(stderr, "Invalid ciphertext length\n"); continue; }
- 统一字节序:发送端用
- 初始化输出长度变量:每次调用
des_decrypt前,将decrypted_size初始化为0:while(1) { decrypted_size = 0; // 必须初始化 read(clnt_sock,(char*)&cipher_size,sizeof(cipher_size)); cipher_size = ntohl(cipher_size); read(clnt_sock,buf,cipher_size); des_decrypt((unsigned char *)buf,cipher_size,fixed_key,(unsigned char*)decrypted,&decrypted_size); fwrite(decrypted,1,decrypted_size,fp); // 修正为写入解密后的数组 } - 统一填充方式:若发送端未使用填充,接收端需在
EVP_DecryptInit_ex后禁用填充:if(1 != EVP_DecryptInit_ex(ctx, EVP_des_ecb(), NULL, key, NULL)) { err("DES init error!"); return; } EVP_CIPHER_CTX_set_padding(ctx, 0); // 禁用填充,与发送端保持一致 - 添加详细错误日志:修改
err函数打印OpenSSL具体错误信息,帮助定位问题:#include <openssl/err.h> void err(const char *msg) { fprintf(stderr, "%s\n", msg); ERR_print_errors_fp(stderr); // 打印OpenSSL错误栈 }
内容的提问来源于stack exchange,提问作者zhf999
相关产品推荐
相关产品推荐

