C语言基于OpenSSL手动实现DES CBC模式输出异常与填充问题求助
问题修复方案
以下是针对你代码中现存问题的直接修复方案,覆盖输出不匹配、填充缺失、逻辑错误等所有核心问题:
现存致命问题列表
- IV未正确初始化与传递:你在循环内定义
roundOutput变量,既没有初始化为预设的IV值,每次加密后更新的也是局部变量,下一轮加密时上一轮的密文输出完全丢失,不符合CBC模式链式加密的要求 - append函数逻辑完全错误:调用时传入的size参数为1,导致eight_bits数组只能存入1个字符,后续字符全部追加失败;且二进制数据不能用strlen计算长度,该函数完全不适用当前场景
- feof接口误用:feof仅在读完文件最后一个字符后才会返回真,会导致你多读取一次EOF值存入缓冲区,引入无效字节
- 填充逻辑缺失:最后不足8字节的块没有按照标准规则填充,导致加密块长度不一致,是输出不匹配的核心原因
- 代码语法错误:
doCBCenc函数的roundOutput参数为const类型,无法被修改更新;缺失printvalueOfDES_LONG函数定义,会导致链接失败 - 缓冲区未初始化:
eight_bits数组未初始化,首次调用strlen时返回值随机,进一步导致字符追加逻辑异常
修复后完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/des.h> #include <sys/time.h> #include <unistd.h> #define ENC 1 #define DEC 0 #define BLOCK_SIZE 8 DES_key_schedule key; // 调试用打印函数,可按需删除 void printvalueOfDES_LONG(DES_LONG *data) { for(int i=0; i<2; i++) { printf("%08lx ", data[i]); } } // 改为按字节异或,避免大小端兼容问题 void doBitwiseXor(unsigned char *xorValue, const unsigned char *data, const unsigned char *roundOutput) { for(int i=0; i<BLOCK_SIZE; i++) { xorValue[i] = data[i] ^ roundOutput[i]; } } void doCBCenc(const unsigned char *data, unsigned char *roundOutput, FILE *outFile) { unsigned char in[BLOCK_SIZE]; doBitwiseXor(in, data, roundOutput); DES_encrypt1((DES_LONG*)in, &key, ENC); printf("ENCRYPTED: "); printvalueOfDES_LONG((DES_LONG*)in); printf("\n"); fwrite(in, BLOCK_SIZE, 1, outFile); memcpy(roundOutput, in, BLOCK_SIZE); } int main(int argc, char** argv) { const_DES_cblock cbc_key = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; const_DES_cblock IV = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; // 保存上一轮加密输出,初始化为IV unsigned char last_block[BLOCK_SIZE]; memcpy(last_block, IV, BLOCK_SIZE); struct timeval start, end; gettimeofday(&start, NULL); int l; if ((l = DES_set_key_checked(&cbc_key,&key)) != 0) { printf("\nkey error\n"); return -1; } FILE *inpFile; FILE *outFile; // 二进制模式打开文件,避免换行符转换导致内容异常 inpFile = fopen("test.txt", "rb"); outFile = fopen("test_results.txt", "wb"); if(inpFile && outFile) { unsigned char eight_bits[BLOCK_SIZE] = {0}; int count = 0; int ch; while((ch = fgetc(inpFile)) != EOF) { printf("%c", (unsigned char)ch); eight_bits[count] = (unsigned char)ch; count++; if(count == BLOCK_SIZE) { printf("\n"); doCBCenc(eight_bits, last_block, outFile); count = 0; memset(eight_bits, 0, BLOCK_SIZE); } } // PKCS#7填充逻辑,保证所有块大小为8字节 int pad_len = BLOCK_SIZE - count; for(int i=count; i<BLOCK_SIZE; i++) { eight_bits[i] = pad_len; } doCBCenc(eight_bits, last_block, outFile); } else { printf("Error in opening file\n"); return -1; } fclose(inpFile); fclose(outFile); gettimeofday(&end, NULL); long seconds = (end.tv_sec - start.tv_sec); long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec); printf("The elapsed time is %ld seconds and %ld microseconds\n", seconds, micros); return 0; }
关键实现说明
- 填充采用标准PKCS#7规则:若最后一个块剩余N个空位(1<=N<=8),则填充N个值为N的字节;若最后一个块刚好满8字节,则额外填充8个值为0x08的字节,解密时只要取最后一个字节的值,减去对应数量的填充字节即可还原原始内容
- CBC模式链式逻辑已修复:上一轮加密的密文会作为下一轮的异或输入,首次加密使用预设IV做异或
- 所有长度计数不再依赖字符串函数,直接通过count变量统计,兼容二进制内容加密
内容的提问来源于stack exchange,提问作者Widget
相关产品推荐
相关产品推荐

