【学习】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-x64 和 libssl-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;
}