OpenSSL+GOST引擎加解密失败:QString转unsigned char密钥问题
GOST89加解密程序:QString转unsigned char密钥后解密失败
我正在编写一个采用GOST89算法的加解密程序,原本运行正常,但尝试将QString转换为unsigned char类型作为密钥时,解密操作失败。已尝试多种转换方式,但均未解决问题,相关代码如下:
#include <cstdio> #include <iostream> #include <openssl/conf.h> #include <openssl/err.h> #include <openssl/engine.h> #include <openssl/evp.h> #include <fstream> #include <QString> #include <QDebug> #include <QFile> using std::cerr; using std::endl; void encryptdata(QString pass, QString data){ OPENSSL_add_all_algorithms_conf(); ENGINE *engine_gost = ENGINE_by_id("gost"); const EVP_CIPHER * cipher_gost = EVP_get_cipherbyname("gost89"); unsigned char *key = (unsigned char * )"password"; qDebug() << (char*)key; unsigned char * iv = (unsigned char * ) "12345678"; unsigned char *text = (unsigned char*)"Hello World"; int text_len = 11; unsigned char ciph[512]; int ciph_len = 0; EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx); int init = EVP_EncryptInit_ex(ctx, cipher_gost, engine_gost, key, iv); int enc = EVP_EncryptUpdate(ctx, ciph, &ciph_len, text, text_len); std::ofstream myfile; myfile.open ("example.bin"); for (int i = 0; i < text_len; i++){ myfile << ciph[i]; } myfile.close(); EVP_CIPHER_CTX_free(ctx); } void decryptdata(){ OPENSSL_add_all_algorithms_conf(); ENGINE *engine_gost1 = ENGINE_by_id("gost"); const EVP_CIPHER * cipher_gost1 = EVP_get_cipherbyname("gost89"); unsigned char * key1 = (unsigned char * ) "password"; qDebug() << (char*)key1; unsigned char * iv1 = (unsigned char * ) "12345678"; unsigned char text1[512]; int text_len1 = 11; unsigned char ciph1[512]; int ciph_len1 = 0; std::ifstream yourfile; yourfile.open ("example.bin"); yourfile >> text1; yourfile.close(); qDebug() << text1; EVP_CIPHER_CTX * ctx1 = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx1); int init = EVP_DecryptInit_ex(ctx1, cipher_gost1, engine_gost1, key1, iv1); int enc = EVP_DecryptUpdate(ctx1, ciph1, &ciph_len1, text1, text_len1); //int enc1 = EVP_DecryptFinal(ctx, ciph, &ciph_len); for (int i = 0; i < text_len1; i++){ std::cout << ciph1[i]; } std::cout << std::endl; EVP_CIPHER_CTX_free(ctx1); } int main(){ //unsigned char t[512] = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; QString pss = "password"; QString dat = "Hello World"; encryptdata(pss, dat); decryptdata(); }
问题分析与解决步骤
1. QString转unsigned char的正确方式
QString是Unicode编码,直接强转会破坏字节结构,必须先转换为QByteArray(对应字节流),再取其数据指针:
// 加密和解密必须使用完全相同的编码格式,比如统一用UTF-8 QByteArray keyBytes = pass.toUtf8(); unsigned char* key = reinterpret_cast<unsigned char*>(keyBytes.data());
注意:GOST89要求密钥长度为32字节(256位),如果你的密钥字符串不足32字节,需要用固定字符(比如'\0')填充到指定长度,否则OpenSSL会出现未定义行为。
2. 修复加解密流程的核心问题
现有代码还有几个导致解密失败的关键隐患:
- 缺失Final步骤:分组加密需要调用
EVP_EncryptFinal_ex/EVP_DecryptFinal_ex处理最后一块数据的填充,否则密文不完整,解密无法还原。 - 二进制文件读写错误:用文本模式读写密文会过滤控制字符,导致数据丢失,必须以二进制模式打开文件:
// 加密写文件 std::ofstream myfile("example.bin", std::ios::binary); // 解密读文件 std::ifstream yourfile("example.bin", std::ios::binary); - 读文件方式错误:
yourfile >> text1会在遇到空格或控制字符时停止读取,应该用read方法读取全部文件内容:// 先获取文件大小,再读取全部数据 std::ifstream yourfile("example.bin", std::ios::binary | std::ios::ate); std::streampos fileSize = yourfile.tellg(); yourfile.seekg(0); yourfile.read(reinterpret_cast<char*>(text1), fileSize);
修正后的核心代码片段
加密函数修改
void encryptdata(QString pass, QString data){ OPENSSL_add_all_algorithms_conf(); ENGINE *engine_gost = ENGINE_by_id("gost"); const EVP_CIPHER * cipher_gost = EVP_get_cipherbyname("gost89"); // 转换QString到密钥字节流并填充到32字节 QByteArray keyBytes = pass.toUtf8(); if(keyBytes.length() < 32){ keyBytes.append(32 - keyBytes.length(), '\0'); } unsigned char* key = reinterpret_cast<unsigned char*>(keyBytes.data()); // IV固定为8字节 QByteArray ivBytes = "12345678"; unsigned char* iv = reinterpret_cast<unsigned char*>(ivBytes.data()); // 转换明文为字节流 QByteArray textBytes = data.toUtf8(); unsigned char* text = reinterpret_cast<unsigned char*>(textBytes.data()); int text_len = textBytes.length(); unsigned char ciph[512]; int ciph_len = 0; int final_len = 0; EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx); EVP_EncryptInit_ex(ctx, cipher_gost, engine_gost, key, iv); EVP_EncryptUpdate(ctx, ciph, &ciph_len, text, text_len); // 处理最后一块数据的填充 EVP_EncryptFinal_ex(ctx, ciph + ciph_len, &final_len); ciph_len += final_len; // 二进制模式写入完整密文 std::ofstream myfile("example.bin", std::ios::binary); myfile.write(reinterpret_cast<char*>(ciph), ciph_len); myfile.close(); EVP_CIPHER_CTX_free(ctx); }
解密函数修改
void decryptdata(QString pass){ OPENSSL_add_all_algorithms_conf(); ENGINE *engine_gost1 = ENGINE_by_id("gost"); const EVP_CIPHER * cipher_gost1 = EVP_get_cipherbyname("gost89"); // 和加密用完全相同的方式转换密钥 QByteArray keyBytes = pass.toUtf8(); if(keyBytes.length() < 32){ keyBytes.append(32 - keyBytes.length(), '\0'); } unsigned char* key1 = reinterpret_cast<unsigned char*>(keyBytes.data()); QByteArray ivBytes = "12345678"; unsigned char* iv1 = reinterpret_cast<unsigned char*>(ivBytes.data()); unsigned char text1[512]; unsigned char ciph1[512]; int ciph_len1 = 0; int final_len1 = 0; // 读取完整密文 std::ifstream yourfile("example.bin", std::ios::binary | std::ios::ate); std::streampos fileSize = yourfile.tellg(); yourfile.seekg(0); yourfile.read(reinterpret_cast<char*>(text1), fileSize); yourfile.close(); EVP_CIPHER_CTX * ctx1 = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx1); EVP_DecryptInit_ex(ctx1, cipher_gost1, engine_gost1, key1, iv1); EVP_DecryptUpdate(ctx1, ciph1, &ciph_len1, text1, fileSize); // 处理填充还原明文 EVP_DecryptFinal_ex(ctx1, ciph1 + ciph_len1, &final_len1); ciph_len1 += final_len1; // 添加结束符并输出明文 ciph1[ciph_len1] = '\0'; std::cout << ciph1 << std::endl; EVP_CIPHER_CTX_free(ctx1); }
主函数修改
int main(){ QString pss = "password"; QString dat = "Hello World"; encryptdata(pss, dat); decryptdata(pss); // 传入加密用的密钥 return 0; }
内容的提问来源于stack exchange,提问作者Urimat9000
相关产品推荐
相关产品推荐

