如何解决OpenSSL的'EVP_DecryptFinal_ex:wrong final block length'错误?
咱们先拆解你遇到的这个错误——EVP_DecryptFinal_ex:wrong final block length通常意味着解密时的密文长度不符合AES块加密的对齐要求,或者密钥/IV不匹配导致解密过程中块校验失败。结合你的代码,我找到了几个关键问题:
1. 加密和解密的Salt完全不匹配
你加密时用的salt是"12345678",但解密时写成了"12345"——这会导致EVP_BytesToKey生成的密钥(key)和初始化向量(iv)完全不一样,解密自然会失败!这是最核心的问题。
修复方法:把解密代码里的salt改成和加密一致的:
unsigned char *salt = (unsigned char *)"12345678"; // 和加密端保持相同
2. 加密后写入文件的密文长度错误
你当前加密后直接把整个ciphertext向量写入文件,但实际上加密后的有效密文长度是c_len + f_len(EVP_EncryptUpdate返回的实际加密长度加上EVP_EncryptFinal_ex返回的填充块长度),而你初始化的ciphertext长度是size + AES_BLOCK_SIZE,里面包含了未使用的空字节,这些多余的字节会导致解密时密文长度不符合AES的块对齐要求。
修复方法:写入文件时只写入有效密文部分:
out_file.write((char*)ciphertext.data(), c_len + f_len); // 只写实际加密后的有效长度
3. 冗余的初始化函数调用
你提到的两次调用EVP_EncryptInit_ex/EVP_DecryptInit_ex其实是旧版本OpenSSL的遗留写法,在OpenSSL 1.1.1a里,只需要调用一次初始化函数即可,第二次空参数的调用可以删掉,不会影响功能。
4. 解密时的明文长度优化
解密时,明文的实际长度是p_len + f_len,你返回的plaintext向量包含了多余的空间,建议可以截断到有效长度,避免后续处理出现问题:
plaintext.resize(p_len + f_len); return plaintext;
修复后的完整核心代码示例
加密部分调整后:
// 打开并读取待加密文件 std::ifstream in_file(file_name, std::ios::binary); in_file.seekg(0, in_file.end); long size = in_file.tellg(); in_file.seekg(0, in_file.beg); std::vector<unsigned char> binarydata(size); in_file.read((char*)binarydata.data(), size); in_file.close(); // 加密操作 EVP_CIPHER_CTX *en = EVP_CIPHER_CTX_new(); unsigned char *salt = (unsigned char *)"12345678"; unsigned char *key_data = (unsigned char *)"super_secret_key_with_32_charact"; int k_len = strlen((const char*)key_data); int nrounds = 5; unsigned char key[32], iv[32]; EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, k_len, nrounds, key, iv); EVP_EncryptInit_ex(en, EVP_aes_256_cbc(), NULL, key, iv); int c_len = 0, f_len = 0; std::vector<unsigned char> ciphertext(size + AES_BLOCK_SIZE); EVP_EncryptUpdate(en, ciphertext.data(), &c_len, binarydata.data(), size); EVP_EncryptFinal_ex(en, ciphertext.data() + c_len, &f_len); EVP_CIPHER_CTX_free(en); // 将密文写入新文件 std::ofstream out_file(output_file, std::ios::binary); out_file.write((char*)ciphertext.data(), c_len + f_len); // 写入有效长度 out_file.close();
解密部分调整后:
// 打开并读取加密文件 std::ifstream in_file(file_name, std::ios::binary); in_file.seekg(0, in_file.end); int size = in_file.tellg(); in_file.seekg(0, in_file.beg); std::vector<unsigned char> ciphertext(size); in_file.read((char*)ciphertext.data(), size); in_file.close(); // 解密操作 EVP_CIPHER_CTX *de = EVP_CIPHER_CTX_new(); unsigned char *salt = (unsigned char *)"12345678"; // 和加密端一致 unsigned char *key_data = (unsigned char *)"super_secret_key_with_32_charact"; int k_len = strlen((const char*)key_data); int nrounds = 5; unsigned char key[32], iv[32]; EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, k_len, nrounds, key, iv); EVP_DecryptInit_ex(de, EVP_aes_256_cbc(), NULL, key, iv); int p_len = 0, f_len = 0; std::vector<unsigned char> plaintext(size); EVP_DecryptUpdate(de, plaintext.data(), &p_len, ciphertext.data(), size); EVP_DecryptFinal_ex(de, plaintext.data() + p_len, &f_len); EVP_CIPHER_CTX_free(de); plaintext.resize(p_len + f_len); // 截断到实际明文长度 return plaintext;
额外提示
- AES-256需要32字节的密钥,你的
key_data刚好是32字符,注意要确保它的长度确实是32,避免因字符串末尾的\0导致长度计算错误。 - 建议在代码中添加错误检查,比如判断
EVP_EncryptInit_ex、EVP_EncryptUpdate等函数的返回值,方便后续排查其他问题。
内容的提问来源于stack exchange,提问作者Carlos Ost

