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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 19:01:19