【学习】Windows下使用OpenSSl实现AES加密兼容Qt

一、前言

Qt本身使用了OpenSSL,但是因为地区法律规定的原因,在Qt安装包里面并没有包含OpenSSL。

默认情况下,Qt在运行时动态加载OpenSSL,如果能找到对应版本的OpenSSL库,相关模块就支持SSL通信,否则就不支持。

二、打印当前版本Qt支持的OpenSSL版本

此处需要引入 Network 库

  • cmake版本
find_package(Qt5 COMPONENTS Network REQUIRED)
  • qmake版本
QT += network
  • 打印OpenSSL版本信息
qDebug() << QSslSocket::sslLibraryBuildVersionString();

笔者这里的版本信息为:

"OpenSSL 1.1.1d  10 Sep 2019"

三、下载并安装OpenSSl

下载链接(包含Win32和Win64)

链接: https://pan.baidu.com/s/1SLhNDmRC-tmm86lDnEvNmw?pwd=abcd

提取码: abcd

(感谢 https://www.cnblogs.com/FBsharl/p/18171746)

安装时,选择将DLL拷贝至/bin,方便后续使用,并记录安装路径(我这里是 E:\OpenSSL-Win64)

四、复制头文件和DLL

E:\OpenSSL-Win64 的根目录

  • 找到 libcrypto-1_1-x64libssl-1_1-x64 两个DLL文件

    • 复制到qt项目的某一文件中,我这里qt项目路径是 E:\qt_demo,复制路径是 E:\qt_demo\libs
  • 再找到 .\include\openssl

    • 复制到qt项目的某一文件中,我这里复制到 E:\qt_demo\openssl

五、程序中引入

我这里以cmake为例,在你的cmakelist中增加下方代码

# Include the OpenSSL library
set(OPENSSL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/openssl")
set(OPENSSL_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libs")

include_directories(${OPENSSL_INCLUDE_DIR})
link_directories(${OPENSSL_LIBRARY_DIR})

target_link_libraries(${PROJECT_NAME}
    libcrypto-1_1-x64
    libssl-1_1-x64
)

程序引入时

#include <openssl/aes.h>
#include <openssl/rand.h>

六、示例

我试了很多网上给出的教程,但是基本上都没有办法复刻,因此基于c++11写了一套工具函数放在下面,基本上复制粘贴就能直接用。

工具函数用纯c++编写的,这样即使不放在qt中,也能够使用。

6.1 生成密钥

AES算法要求密钥长度是特定的,分别是128位(16字节)、192位(24字节)或256位(32字节),但实际使用过程中常需要用到自定义长度的key。

这里我才用SHA-256对密钥进行哈希(32字节),然后截取哈希值的前n个字节作为AES密钥。

/**
 * 生成 AES 密钥
 * @param input_key 明文密钥
 * @param aes_key 生成的密钥
 * @param key_len 最终密钥长度
 */
void generateAESKey(const std::string& input_key, unsigned char* aes_key, int key_len) {
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char*>(input_key.c_str()), input_key.size(), hash);
    memcpy(aes_key, hash, key_len);
}

6.2 AES 加密

/**
 * AES CBC 加密函数
 * @param plain_text 待加密数据
 * @param encrypt_text 加密后结果
 */
void aesEncrypt(
        const std::string& plain_text,
        const std::string& encrypt_file_path){

    unsigned int plain_text_len = plain_text.size();
    unsigned char key_hex[16];
    generateAESKey(KEY_STR, key_hex, 16);
    AES_KEY en_key;
    // ============= 设置 AES 加密密钥 ==================
    if(AES_set_encrypt_key(key_hex,128,&en_key) < 0){
        // 密钥设置失败
        std::cerr << "[AESUtilities] set encrypt key failed!" << std::endl;
    }

    // 计算填充长度
    int padding_len = AES_BLOCK_SIZE - (plain_text_len % AES_BLOCK_SIZE);
    int total_len = plain_text_len + padding_len;

    unsigned char* padded_plain_text = (unsigned char*)malloc(total_len);
    // 使用 memcpy 复制 plain_text 到 padded_plain_text
    memcpy(padded_plain_text, plain_text.c_str(), plain_text_len);
    memset(padded_plain_text + plain_text_len, padding_len, padding_len);

    unsigned char iv_tmp[17];
    memcpy(iv_tmp, IV_STR, 17);

    unsigned char* encrypt_text = (unsigned char*)malloc(total_len);
    AES_cbc_encrypt(padded_plain_text,encrypt_text,total_len,&en_key,iv_tmp,AES_ENCRYPT);
    free(padded_plain_text);

    //    qDebug() << "加密后的密文: ";
    //    printHex(encrypt_text, total_len);
    // 加密数据写入文件
    if (!encrypt_file_path.empty()) {
        std::ofstream outfile(encrypt_file_path, std::ios::binary);
        if (!outfile) {
            std::cerr << "[AESUtilities] encrypt_file_path open failed ! " << std::endl;
        } else {
            outfile.write(reinterpret_cast<char*>(encrypt_text), total_len);
            outfile.close();
        }
    }
    free(encrypt_text);
}

6.3 AES 解密

/**
 * AES CBC 解密函数
 * @param encrypt_file_path 待解密文件路径
 * @return
 */
unsigned char* aesDecrypt(
        const std::string &encrypt_file_path) {

    unsigned char key_hex[16];
    generateAESKey(KEY_STR, key_hex, 16);

    AES_KEY de_key;
    // ============= 设置 AES 解密密钥 ==================
    if (AES_set_decrypt_key(key_hex, 128, &de_key) < 0) {
//        qDebug("[AESUtilities] set decrypt key failed ! ");
        std::cerr << "[AESUtilities] set decrypt key failed!" << std::endl;
    }
    // 从文件中以二进制模式读取密文
    std::ifstream encrypt_file(encrypt_file_path, std::ios::binary);
    if (!encrypt_file) {
//        qDebug("[AESUtilities] encrypt_file_path open failed ! ");
        std::cerr << "[AESUtilities] encrypt_file_path open failed ! " << std::endl;
    }

    // 读取文件内容到 vector 中
    std::vector<unsigned char> buffer((std::istreambuf_iterator<char>(encrypt_file)), std::istreambuf_iterator<char>());
    encrypt_file.close();

    unsigned char iv_tmp[17];
    memcpy(iv_tmp, IV_STR, 17);

    unsigned char* decrypt_text = (unsigned char*)malloc(buffer.size());
    AES_cbc_encrypt(buffer.data(), decrypt_text, buffer.size(), &de_key, iv_tmp, AES_DECRYPT);

    // 获取填充长度
    int padding_len = decrypt_text[buffer.size() - 1];
    int plain_text_len = buffer.size() - padding_len;

    decrypt_text[plain_text_len] = '\0'; // 添加 null 终止符
    return decrypt_text;
}

6.4 使用方法

int main() {

    const std::string plain_text = "zhe shi xu yao jia mi de wen ben 这是需要加密的文本";

    aesEncrypt(plain_text, "E:/存储路径/加密后.bin");

    unsigned char* decrypt_text = aesDecrypt("E:/存储路径/加密后.bin");
    std::cout << (char*)decrypt_text;
    return 0;
}
posted @ 2024-07-04 14:12  小拳头呀  阅读(95)  评论(0编辑  收藏  举报