IDEA/CFB模式解密首块数据损坏问题求助(OpenSSL 1.1.0g)
Let's break down the issues in your code that are causing the first 8-byte block to be corrupted, and fix them step by step:
Key Issues Identified
1. Incorrect IV Array Indexing
Your code reads the IV from positions 16-24 of the key buffer, but assigns it to iv[i] directly—this is out of bounds for the 8-byte iv array. The correct indices for iv should start at 0, not 16. This leads to a garbled IV, which directly breaks the first block decryption in CFB mode (since CFB uses the IV to generate the first keystream block).
2. Mismatched CFB Mode in Initialization
You printed the parameters for EVP_idea_cfb64() but initialized the cipher with EVP_idea_cfb()—these are different modes (EVP_idea_cfb() is CFB8, while EVP_idea_cfb64() is 64-bit CFB, matching IDEA's 8-byte block size). Using the wrong mode causes keystream generation to misalign, corrupting the first block.
3. Improper Decryption Output Buffer Management
- You didn't accumulate the output length from
EVP_CipherUpdate, soEVP_DecryptFinal_exwrites to the start of theplainbuffer, overwriting part of the decrypted data. - The final block needs to be written to the end of the already decrypted data, not the start.
4. Unsafe File Reading
Your read_file function uses a fixed BUFFER_SIZE for reading, which might not match the actual file size. You should read exactly the file size you retrieved with lseek.
Corrected Code
#include <fcntl.h> /* O_RDONLY */ #include <stdio.h> /* printf */ #include <string.h> /* memcpy */ #include <unistd.h> /* read */ #include <openssl/idea.h> #include <openssl/evp.h> #include <openssl/ripemd.h> #define BUFFER_SIZE 861 unsigned char key[16]; unsigned char iv[8]; int read_file(char *file, unsigned char *buffer); int main(void) { unsigned char buffer[BUFFER_SIZE]; unsigned char cipher_buffer[BUFFER_SIZE]; unsigned char plain[BUFFER_SIZE] = {0}; // Initialize to zero to avoid garbage output EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); char *cipher = "./cipher.bin"; char *key1 = "./key.bin"; // Read key and IV int key_file_size = read_file(key1, buffer); if (key_file_size <= 0) { fprintf(stderr, "Failed to read key file\n"); EVP_CIPHER_CTX_free(ctx); return 1; } printf("\nKeylength: %d\n", EVP_CIPHER_key_length(EVP_idea_cfb64())); printf("IV Length: %d\n", EVP_CIPHER_iv_length(EVP_idea_cfb64())); printf("\nKEY:"); for(int i = 0; i < 16; i++){ key[i] = buffer[i]; printf("%d ", buffer[i]); } printf("\nIV : "); for(int i = 16; i < 24; i++){ iv[i - 16] = buffer[i]; // Fix: correct IV array indexing printf("%d ", iv[i - 16]); } // Read ciphertext int cipherLen = read_file(cipher, cipher_buffer); if (cipherLen <= 0) { fprintf(stderr, "Failed to read cipher file\n"); EVP_CIPHER_CTX_free(ctx); return 1; } int outlen = 0; int temp_len = 0; // Initialize cipher context with correct CFB64 mode if (!EVP_CipherInit_ex(ctx, EVP_idea_cfb64(), NULL, key, iv, 0)) { fprintf(stderr, "Cipher initialization failed\n"); EVP_CIPHER_CTX_free(ctx); return 1; } // Decrypt main data block if (!EVP_CipherUpdate(ctx, plain, &temp_len, cipher_buffer, cipherLen)) { fprintf(stderr, "Cipher update failed\n"); EVP_CIPHER_CTX_free(ctx); return 1; } outlen += temp_len; // Decrypt final block (handles padding for CFB if needed) if (!EVP_DecryptFinal_ex(ctx, plain + outlen, &temp_len)) { fprintf(stderr, "Final decrypt failed\n"); EVP_CIPHER_CTX_free(ctx); return 1; } outlen += temp_len; // Print the full decrypted plaintext printf("\nDecrypted plaintext:\n%.*s\n", outlen, plain); EVP_CIPHER_CTX_free(ctx); return 0; } int read_file(char *file, unsigned char *buffer){ int fp = open(file, O_RDONLY); if (fp == -1) { perror("File not found"); return -1; } int f_size = lseek(fp, 0, SEEK_END); if (f_size == -1) { perror("Failed to get file size"); close(fp); return -1; } lseek(fp, 0, SEEK_SET); printf("File size for %s: %d\n", file, f_size); ssize_t bytes_read = read(fp, buffer, f_size); // Read exactly the file size if (bytes_read != f_size) { perror("Failed to read full file"); close(fp); return -1; } close(fp); return f_size; }
Additional Notes
- Always check the return values of OpenSSL functions—they return 1 on success, 0 on failure, which helps catch initialization or decryption errors early.
- We added
close(fp)inread_fileto avoid file descriptor leaks. - The plain buffer is initialized to zero to prevent printing uninitialized garbage after the decrypted data.
内容的提问来源于stack exchange,提问作者Twin322

