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

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);
    
  • 校验并修正密文长度:
    1. 统一字节序:发送端用htonl转换cipher_size为网络字节序,接收端用ntohl转换回主机字节序:
      read(clnt_sock,(char*)&cipher_size,sizeof(cipher_size));
      cipher_size = ntohl(cipher_size); // 转换为主机字节序
      
    2. 解密前检查密文长度是否为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:15:00