C/C++编程:Openssl使用 Windows安装包32和64位 RSA加密/解密、AES-GCM加密/解密以及ECDSA签名/验证示例
Openssl的头文件和库
C/C++使用openssl,需要openssl的头文件和库,这些都在安装包里。从http://slproweb.com/products/Win32OpenSSL.html下载已经编译好的包含 lib 和 include 文件的安装包。
也可以从官网下载源码,再编译成安装包,但编译有点复杂,编译过程中可能产生各种的问题,不建议使用。
C++使用示例
Windows下在C++使用OpenSSL库的示例
以下是在Windows环境下使用C++和OpenSSL库的完整指南,包括安装、配置和代码示例。
1. 安装OpenSSL
方法1:使用预编译的二进制文件
- 访问 OpenSSL官方下载页面 或 Win32/Win64 OpenSSL安装器
- 下载适合您系统的安装包(32位或64位)
- 运行安装程序,按照向导完成安装
方法2:使用vcpkg包管理器
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
bootstrap-vcpkg.bat
vcpkg install openssl:x64-windows # 64位
# 或
vcpkg install openssl:x86-windows # 32位
vcpkg integrate install
2. 配置Visual Studio项目
手动配置
- 打开项目属性(右键项目 -> 属性)
- 添加包含目录:
- C/C++ -> 常规 -> 附加包含目录:添加
C:\OpenSSL-Win64\include
(根据安装位置调整)
- C/C++ -> 常规 -> 附加包含目录:添加
- 添加库目录:
- 链接器 -> 常规 -> 附加库目录:添加
C:\OpenSSL-Win64\lib
(根据安装位置调整)
- 链接器 -> 常规 -> 附加库目录:添加
- 添加依赖库:
- 链接器 -> 输入 -> 附加依赖项:添加
libssl.lib
和libcrypto.lib
- 链接器 -> 输入 -> 附加依赖项:添加
使用vcpkg配置
如果使用vcpkg并且运行了vcpkg integrate install
,则无需额外配置,可以直接使用库。
3. C++代码示例
示例1:基本的OpenSSL初始化和清理
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <iostream>int main() {// 初始化OpenSSL库SSL_library_init();SSL_load_error_strings();OpenSSL_add_all_algorithms();std::cout << "OpenSSL版本: " << OpenSSL_version(OPENSSL_VERSION) << std::endl;// 清理OpenSSL资源EVP_cleanup();ERR_free_strings();return 0;
}
示例2:使用RSA进行加密和解密
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <iostream>
#include <string>
#include <vector>// 错误处理函数
void handleErrors() {ERR_print_errors_fp(stderr);abort();
}// 生成RSA密钥对并保存到文件
void generateRSAKeyPair(const char* privateKeyFile, const char* publicKeyFile) {EVP_PKEY* pkey = NULL;EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);if (!ctx) handleErrors();if (EVP_PKEY_keygen_init(ctx) <= 0) handleErrors();if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0) handleErrors();if (EVP_PKEY_keygen(ctx, &pkey) <= 0) handleErrors();// 保存私钥FILE* fp = fopen(privateKeyFile, "wb");if (!fp) handleErrors();if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) handleErrors();fclose(fp);// 保存公钥fp = fopen(publicKeyFile, "wb");if (!fp) handleErrors();if (!PEM_write_PUBKEY(fp, pkey)) handleErrors();fclose(fp);EVP_PKEY_free(pkey);EVP_PKEY_CTX_free(ctx);
}// 使用公钥加密数据
std::vector<unsigned char> rsaEncrypt(const char* publicKeyFile, const std::string& plainText) {FILE* fp = fopen(publicKeyFile, "rb");if (!fp) handleErrors();EVP_PKEY* pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);fclose(fp);if (!pkey) handleErrors();EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);if (!ctx) handleErrors();if (EVP_PKEY_encrypt_init(ctx) <= 0) handleErrors();if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) handleErrors();size_t outlen;if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char*)plainText.c_str(), plainText.length()) <= 0) handleErrors();std::vector<unsigned char> encrypted(outlen);if (EVP_PKEY_encrypt(ctx, encrypted.data(), &outlen, (const unsigned char*)plainText.c_str(), plainText.length()) <= 0) handleErrors();encrypted.resize(outlen);EVP_PKEY_CTX_free(ctx);EVP_PKEY_free(pkey);return encrypted;
}// 使用私钥解密数据
std::string rsaDecrypt(const char* privateKeyFile, const std::vector<unsigned char>& cipherText) {FILE* fp = fopen(privateKeyFile, "rb");if (!fp) handleErrors();EVP_PKEY* pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);fclose(fp);if (!pkey) handleErrors();EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);if (!ctx) handleErrors();if (EVP_PKEY_decrypt_init(ctx) <= 0) handleErrors();if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) handleErrors();size_t outlen;if (EVP_PKEY_decrypt(ctx, NULL, &outlen, cipherText.data(), cipherText.size()) <= 0) handleErrors();std::vector<unsigned char> decrypted(outlen);if (EVP_PKEY_decrypt(ctx, decrypted.data(), &outlen, cipherText.data(), cipherText.size()) <= 0) handleErrors();EVP_PKEY_CTX_free(ctx);EVP_PKEY_free(pkey);return std::string(decrypted.begin(), decrypted.begin() + outlen);
}int main() {// 初始化OpenSSLERR_load_crypto_strings();OpenSSL_add_all_algorithms();const char* privateKeyFile = "private.pem";const char* publicKeyFile = "public.pem";std::string message = "This is a secret message for RSA encryption!";// 生成RSA密钥对generateRSAKeyPair(privateKeyFile, publicKeyFile);std::cout << "RSA密钥对已生成.\n";// 加密消息std::vector<unsigned char> encrypted = rsaEncrypt(publicKeyFile, message);std::cout << "消息已加密,密文大小: " << encrypted.size() << " 字节\n";// 解密消息std::string decrypted = rsaDecrypt(privateKeyFile, encrypted);std::cout << "解密后的消息: " << decrypted << "\n";// 清理EVP_cleanup();CRYPTO_cleanup_all_ex_data();ERR_free_strings();return 0;
}
示例3:使用AES-256-GCM进行加密和解密
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <iostream>
#include <string>
#include <vector>
#include <cstring>void handleErrors() {ERR_print_errors_fp(stderr);abort();
}// AES-GCM 加密
std::vector<unsigned char> aesGcmEncrypt(const std::string& plaintext,const std::vector<unsigned char>& key,const std::vector<unsigned char>& iv,std::vector<unsigned char>& tag) {EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) handleErrors();// 初始化加密操作if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))handleErrors();// 设置IV长度if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL))handleErrors();// 初始化密钥和IVif (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key.data(), iv.data()))handleErrors();// 提供要加密的明文int len = 0;int ciphertext_len = 0;std::vector<unsigned char> ciphertext(plaintext.size() + EVP_MAX_BLOCK_LENGTH);if (1 != EVP_EncryptUpdate(ctx, ciphertext.data(), &len, (const unsigned char*)plaintext.c_str(), plaintext.size()))handleErrors();ciphertext_len = len;// 加密最终块if (1 != EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len))handleErrors();ciphertext_len += len;// 获取认证标签tag.resize(16);if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag.data()))handleErrors();EVP_CIPHER_CTX_free(ctx);ciphertext.resize(ciphertext_len);return ciphertext;
}// AES-GCM 解密
std::string aesGcmDecrypt(const std::vector<unsigned char>& ciphertext,const std::vector<unsigned char>& key,const std::vector<unsigned char>& iv,const std::vector<unsigned char>& tag) {EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) handleErrors();// 初始化解密操作if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))handleErrors();// 设置IV长度if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL))handleErrors();// 初始化密钥和IVif (1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key.data(), iv.data()))handleErrors();// 提供密文int len = 0;int plaintext_len = 0;std::vector<unsigned char> plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH);if (1 != EVP_DecryptUpdate(ctx, plaintext.data(), &len, ciphertext.data(), ciphertext.size()))handleErrors();plaintext_len = len;// 设置预期的标签值if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag.size(), (void*)tag.data()))handleErrors();// 验证并解密最终块int ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);EVP_CIPHER_CTX_free(ctx);if (ret > 0) {plaintext_len += len;plaintext.resize(plaintext_len);return std::string(plaintext.begin(), plaintext.end());} else {// 认证失败return "认证失败!";}
}int main() {// 初始化OpenSSLERR_load_crypto_strings();OpenSSL_add_all_algorithms();// 生成随机密钥 (256 bits)std::vector<unsigned char> key(32);if (RAND_bytes(key.data(), key.size()) != 1)handleErrors();// 生成随机IV (96 bits 是GCM的推荐值)std::vector<unsigned char> iv(12);if (RAND_bytes(iv.data(), iv.size()) != 1)handleErrors();std::string plaintext = "这是一个使用AES-256-GCM加密的机密消息!";std::cout << "原始消息: " << plaintext << std::endl;// 加密std::vector<unsigned char> tag;std::vector<unsigned char> ciphertext = aesGcmEncrypt(plaintext, key, iv, tag);std::cout << "加密完成,密文大小: " << ciphertext.size() << " 字节" << std::endl;std::cout << "认证标签大小: " << tag.size() << " 字节" << std::endl;// 解密std::string decryptedText = aesGcmDecrypt(ciphertext, key, iv, tag);std::cout << "解密后的消息: " << decryptedText << std::endl;// 使用错误的标签尝试解密(模拟篡改)std::vector<unsigned char> wrongTag = tag;wrongTag[0] ^= 1; // 修改一个位std::string failedDecrypt = aesGcmDecrypt(ciphertext, key, iv, wrongTag);std::cout << "使用错误标签解密: " << failedDecrypt << std::endl;// 清理EVP_cleanup();CRYPTO_cleanup_all_ex_data();ERR_free_strings();return 0;
}
示例4:生成和验证ECDSA签名(类似于原始示例)
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <iostream>
#include <vector>
#include <string>void handleErrors() {ERR_print_errors_fp(stderr);abort();
}// 生成EC密钥
EC_KEY* generateEcKey() {EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);if (!ec_key || !EC_KEY_generate_key(ec_key)) {handleErrors();}return ec_key;
}// 将EC密钥保存到文件
void saveEcKey(EC_KEY* ec_key, const char* privateKeyFile, const char* publicKeyFile) {// 保存私钥FILE* fp = fopen(privateKeyFile, "wb");if (!fp) handleErrors();if (!PEM_write_ECPrivateKey(fp, ec_key, NULL, NULL, 0, NULL, NULL)) handleErrors();fclose(fp);// 保存公钥fp = fopen(publicKeyFile, "wb");if (!fp) handleErrors();if (!PEM_write_EC_PUBKEY(fp, ec_key)) handleErrors();fclose(fp);
}// 从文件加载EC密钥
EC_KEY* loadEcPrivateKey(const char* privateKeyFile) {FILE* fp = fopen(privateKeyFile, "rb");if (!fp) handleErrors();EC_KEY* ec_key = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);fclose(fp);if (!ec_key) handleErrors();return ec_key;
}EC_KEY* loadEcPublicKey(const char* publicKeyFile) {FILE* fp = fopen(publicKeyFile, "rb");if (!fp) handleErrors();EC_KEY* ec_key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL);fclose(fp);if (!ec_key) handleErrors();return ec_key;
}// 签名数据
std::vector<unsigned char> signData(EC_KEY* ec_key, const std::string& data) {EVP_MD_CTX* mdctx = EVP_MD_CTX_new();if (!mdctx) handleErrors();EVP_PKEY* pkey = EVP_PKEY_new();if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey, ec_key)) handleErrors();if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey) <= 0) handleErrors();if (EVP_DigestSignUpdate(mdctx, data.c_str(), data.size()) <= 0) handleErrors();// 获取签名大小size_t sig_len = 0;if (EVP_DigestSignFinal(mdctx, NULL, &sig_len) <= 0) handleErrors();// 生成签名std::vector<unsigned char> signature(sig_len);if (EVP_DigestSignFinal(mdctx, signature.data(), &sig_len) <= 0) handleErrors();signature.resize(sig_len);EVP_MD_CTX_free(mdctx);EVP_PKEY_free(pkey);return signature;
}// 验证签名
bool verifySignature(EC_KEY* ec_key, const std::string& data, const std::vector<unsigned char>& signature) {EVP_MD_CTX* mdctx = EVP_MD_CTX_new();if (!mdctx) handleErrors();EVP_PKEY* pkey = EVP_PKEY_new();if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey, ec_key)) handleErrors();if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) <= 0) handleErrors();if (EVP_DigestVerifyUpdate(mdctx, data.c_str(), data.size()) <= 0) handleErrors();// 验证签名int result = EVP_DigestVerifyFinal(mdctx, signature.data(), signature.size());EVP_MD_CTX_free(mdctx);EVP_PKEY_free(pkey);return result == 1;
}int main() {// 初始化OpenSSLERR_load_crypto_strings();OpenSSL_add_all_algorithms();const char* privateKeyFile = "ec_private.pem";const char* publicKeyFile = "ec_public.pem";std::string message = "Hello, ECDSA Signing with OpenSSL!";// 生成并保存密钥EC_KEY* ec_key = generateEcKey();saveEcKey(ec_key, privateKeyFile, publicKeyFile);EC_KEY_free(ec_key);std::cout << "EC密钥对已生成并保存.\n";// 从文件加载密钥EC_KEY* priv_key = loadEcPrivateKey(privateKeyFile);EC_KEY* pub_key = loadEcPublicKey(publicKeyFile);// 签名数据std::vector<unsigned char> signature = signData(priv_key, message);std::cout << "签名已生成,大小: " << signature.size() << " 字节\n";// 验证签名bool is_valid = verifySignature(pub_key, message, signature);std::cout << "签名验证: " << (is_valid ? "成功" : "失败") << "\n";// 篡改消息并验证std::string tampered_message = message + "!";bool tampered_valid = verifySignature(pub_key, tampered_message, signature);std::cout << "篡改后的消息签名验证: " << (tampered_valid ? "成功" : "失败") << "\n";// 清理EC_KEY_free(priv_key);EC_KEY_free(pub_key);EVP_cleanup();CRYPTO_cleanup_all_ex_data();ERR_free_strings();return 0;
}
4. 部署注意事项
-
确保DLL文件可以被应用程序找到:
- 将
libssl-1_1-x64.dll
和libcrypto-1_1-x64.dll
(或相应版本)放在应用程序目录中 - 或者确保它们在系统PATH环境变量包含的目录中
- 将
-
构建项目时注意:
- 确保编译版本(Debug/Release)与OpenSSL库的版本(Debug/Release)匹配
- 32位应用程序应使用32位OpenSSL库,64位应用程序应使用64位OpenSSL库
-
版本兼容性:
- 保持应用程序使用的OpenSSL版本与部署环境一致
- 考虑静态链接OpenSSL库,以避免版本依赖问题
以上示例涵盖了在Windows上使用C++和OpenSSL进行常见加密操作的基本用法,包括RSA加密/解密、AES-GCM加密/解密以及ECDSA签名/验证。您可以根据需要调整和扩展这些示例。
MFC使用示例
Windows下在MFC中使用OpenSSL库的示例
在MFC应用程序中使用OpenSSL库需要几个步骤:安装OpenSSL、配置项目以及编写代码。以下是具体指南:
1. 安装OpenSSL
首先需要在Windows上安装OpenSSL:
- 从官方网站下载OpenSSL: https://www.openssl.org/source/ 或使用预编译版本
- 或使用vcpkg包管理器:
vcpkg install openssl:x86-windows
或vcpkg install openssl:x64-windows
2. 配置MFC项目
在Visual Studio中配置MFC项目:
- 右键点击项目 → 属性
- 配置包含目录:
- C/C++ → 常规 → 附加包含目录: 添加
C:\path\to\openssl\include
- C/C++ → 常规 → 附加包含目录: 添加
- 配置库目录:
- 链接器 → 常规 → 附加库目录: 添加
C:\path\to\openssl\lib
- 链接器 → 常规 → 附加库目录: 添加
- 添加依赖库:
- 链接器 → 输入 → 附加依赖项: 添加
libssl.lib
和libcrypto.lib
- 链接器 → 输入 → 附加依赖项: 添加
3. MFC应用程序示例
下面是一个在MFC对话框应用程序中使用OpenSSL进行ECC签名的简单示例:
在头文件中添加声明
// ... existing code ...#pragma once
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <vector>
#include <string>class CYourDialog : public CDialogEx
{// ... existing code ...private:// OpenSSL相关函数EC_KEY* GenerateECCKey();std::vector<unsigned char> SignData(EC_KEY* ec_key, const std::string& data);bool VerifySignature(EC_KEY* ec_key, const std::string& data, const std::vector<unsigned char>& signature);void HandleOpenSSLErrors();public:afx_msg void OnBnClickedButtonSign();// ... existing code ...
};
在实现文件中添加代码
// ... existing code ...// 初始化时加载OpenSSL
BOOL CYourDialog::OnInitDialog()
{CDialogEx::OnInitDialog();// 初始化OpenSSLERR_load_crypto_strings();OpenSSL_add_all_algorithms();return TRUE;
}// 处理OpenSSL错误
void CYourDialog::HandleOpenSSLErrors()
{CString errorMsg;char errBuf[256];unsigned long err;while ((err = ERR_get_error()) != 0) {ERR_error_string_n(err, errBuf, sizeof(errBuf));errorMsg += errBuf;errorMsg += _T("\n");}AfxMessageBox(errorMsg, MB_ICONERROR);
}// 生成ECC密钥
EC_KEY* CYourDialog::GenerateECCKey()
{EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_secp521r1);if (!ec_key || !EC_KEY_generate_key(ec_key)) {HandleOpenSSLErrors();return nullptr;}return ec_key;
}// 签名数据
std::vector<unsigned char> CYourDialog::SignData(EC_KEY* ec_key, const std::string& data)
{std::vector<unsigned char> signature;EVP_MD_CTX* mdctx = EVP_MD_CTX_new();EVP_PKEY* pkey = EVP_PKEY_new();if (!mdctx || !pkey) {HandleOpenSSLErrors();goto cleanup;}if (!EVP_PKEY_set1_EC_KEY(pkey, ec_key)) {HandleOpenSSLErrors();goto cleanup;}if (EVP_DigestSignInit(mdctx, nullptr, EVP_sha512(), nullptr, pkey) <= 0) {HandleOpenSSLErrors();goto cleanup;}if (EVP_DigestSignUpdate(mdctx, data.c_str(), data.size()) <= 0) {HandleOpenSSLErrors();goto cleanup;}// 获取签名长度size_t sig_len = 0;if (EVP_DigestSignFinal(mdctx, nullptr, &sig_len) <= 0) {HandleOpenSSLErrors();goto cleanup;}// 执行签名signature.resize(sig_len);if (EVP_DigestSignFinal(mdctx, signature.data(), &sig_len) <= 0) {HandleOpenSSLErrors();signature.clear();goto cleanup;}signature.resize(sig_len);cleanup:if (mdctx) EVP_MD_CTX_free(mdctx);if (pkey) EVP_PKEY_free(pkey);return signature;
}// 验证签名
bool CYourDialog::VerifySignature(EC_KEY* ec_key, const std::string& data, const std::vector<unsigned char>& signature)
{bool result = false;EVP_MD_CTX* mdctx = EVP_MD_CTX_new();EVP_PKEY* pkey = EVP_PKEY_new();if (!mdctx || !pkey) {HandleOpenSSLErrors();goto cleanup;}if (!EVP_PKEY_set1_EC_KEY(pkey, ec_key)) {HandleOpenSSLErrors();goto cleanup;}if (EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha512(), nullptr, pkey) <= 0) {HandleOpenSSLErrors();goto cleanup;}if (EVP_DigestVerifyUpdate(mdctx, data.c_str(), data.size()) <= 0) {HandleOpenSSLErrors();goto cleanup;}if (EVP_DigestVerifyFinal(mdctx, signature.data(), signature.size()) == 1) {result = true;}cleanup:if (mdctx) EVP_MD_CTX_free(mdctx);if (pkey) EVP_PKEY_free(pkey);return result;
}// 点击签名按钮的处理函数
void CYourDialog::OnBnClickedButtonSign()
{CString message;GetDlgItemText(IDC_EDIT_MESSAGE, message);if (message.IsEmpty()) {AfxMessageBox(_T("请输入要签名的消息"));return;}EC_KEY* ec_key = GenerateECCKey();if (!ec_key) return;std::string messageStr(CT2A(message));std::vector<unsigned char> signature = SignData(ec_key, messageStr);if (!signature.empty()) {bool is_valid = VerifySignature(ec_key, messageStr, signature);CString result;result.Format(_T("签名长度: %d 字节\r\n签名验证: %s"), signature.size(), is_valid ? _T("成功") : _T("失败"));SetDlgItemText(IDC_EDIT_RESULT, result);}EC_KEY_free(ec_key);
}void CYourDialog::OnDestroy()
{CDialogEx::OnDestroy();// 清理OpenSSLEVP_cleanup();ERR_free_strings();
}
4. 在资源文件中添加UI控件
在对话框资源中添加:
- 一个编辑框(IDC_EDIT_MESSAGE)用于输入消息
- 一个按钮(IDC_BUTTON_SIGN)用于触发签名
- 一个编辑框(IDC_EDIT_RESULT)用于显示结果
5. 注意事项
- 确保使用正确版本的OpenSSL库(32位/64位)与项目配置匹配
- 在Debug和Release配置中都要配置包含目录和库目录
- 处理好OpenSSL库的内存管理,避免内存泄漏
- 如有多线程需求,确保OpenSSL线程安全配置
这个示例展示了如何在MFC应用程序中集成OpenSSL库进行ECC签名和验证,您可以根据需要进行修改和扩展。
相关文章:
C/C++编程:Openssl使用 Windows安装包32和64位 RSA加密/解密、AES-GCM加密/解密以及ECDSA签名/验证示例
Openssl的头文件和库 C/C使用openssl,需要openssl的头文件和库,这些都在安装包里。从http://slproweb.com/products/Win32OpenSSL.html下载已经编译好的包含 lib 和 include 文件的安装包。 也可以从官网下载源码,再编译成安装包࿰…...
Es6新特性
1. let 和 const 概念 let:用于声明 块级作用域 的变量。const:用于声明 块级作用域 的常量,声明后不可重新赋值(但可以修改对象的属性或数组的内容)。 原理 JavaScript 在 ES5 中只有全局作用域和函数作用域&…...
大数据学习(80)-数仓分层
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
StarRocks 升级注意事项
前段时间升级了生产环境的 StarRocks,从 3.3.3 升级到了 3.3.9,期间还是踩了不少坑所以在这里记录下。 因为我们的集群使用的是存算分离的版本,也是使用官方提供的 operator 部署在 kubernetes 里的,所以没法按照官方的流程进入虚…...
Java 大视界 -- Java 大数据分布式计算中的通信优化与网络拓扑设计(145)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
LabVIEW软件长时间运行导致蓝屏问题排查与优化
计算机在长时间运行LabVIEW或其他软件后出现蓝屏(BSOD),通常由硬件资源耗尽、驱动冲突或软件内存泄漏引发。本文提供从日志分析到根本性优化的全流程解决方案,确保系统稳定运行。 一、蓝屏记录查询方法 1. 查看Windows事件日志 操…...
【机密计算顶会解读】11:ACAI——使用 Arm 机密计算架构保护加速器执行
导读:本文介绍ACAI,其构建一个基于CCA的解决方案,使得机密虚拟机能够安全地使用加速器,同时保持与现有应用程序的兼容性和安全性,能够实现对加速器的安全访问。 原文链接:ACAI: Protecting Accelerator Ex…...
【WRF模拟】WPS预处理设置生成文件地址
目录 WPS 运行 geogrid.exe在 namelist.wps 中指定 geogrid.exe 输出路径WPS 运行 ungrid.exe方法 1:在 namelist.wps 中指定输出路径方法 2:手动移动 FILE:* 文件方法 3:使用环境变量 WPS_UNGRIB_OUTPUT(不推荐)另:设置文件链接地址WPS 运行 metgrid.exe方法 1:在 name…...
Midjourney使用教程—2.作品修改
当您已生成第一张Midjourney图像的时候,接下来该做什么?了解我们用于修改图像的工具!使用 Midjourney 制作图像后,您的创意之旅就不会止步于此。您可以使用各种工具来修改和增强图像。 一、放大操作 Midjourney每次会根据提示词…...
基于 ABAP RESTful 应用程序编程模型开发 OData V4 服务
一、概念 以个人图书管理为例,创建一个ABAP RESTful 应用程序编程模型项目。最终要实现的效果: 用于管理书籍的程序。读取、修改和删除书籍。 二、Data Model-数据模型 2.1 创建项目基础数据库表 首先,创建一个图书相关的表,点…...
微信小程序登陆之反向代理
一.背景 在互联网架构中,反向代理是连接客户端与后端服务的核心组件。它的核心价值在于: 安全性:隐藏内部服务细节,防止直接暴露到公网。 负载均衡:分散请求到多个后端实例,提升吞吐量。 SSL终止&#x…...
[解决] PDF转图片,中文乱码或显示方框的解决方案
在Java开发中,将PDF文件转换为图片是一项常见的需求,但过程中可能会遇到中文乱码或显示方框的问题。本文将深入探讨这一问题,并提供详细的解决方案,帮助开发者顺利地完成PDF到图片的转换。 一、问题现象 在使用Java库(如Apache PDFBox)将PDF转换为图片时,如果PDF文件中…...
面试康复训练-SQL语句
一,数据库操作 1查看所有库 show databases; --查看所有库2使用数据库 use 数据库名; --使用数据库; 3查看当前使用数据库 select database(); --查看当前使用的数据库 4 创建数据库 create databse 数据库名 charsetutf8; --创建数据库 5删…...
经典面试题:C/C++中static关键字的三大核心作用与实战应用
一、修饰局部变量:改变生命周期,保留跨调用状态 核心作用: 延长生命周期:将局部变量从栈区移至静态存储区(数据段或BSS段),生命周期与程序一致保留状态:变量在函数多次调用间保…...
Linux固定IP方法(RedHat+Net模式)
1、查看当前网关 ip route | grep default 2、配置静态IP 双击重启 3、验证...
JVM 学习前置知识
JVM 学习前置知识 Java 开发环境层次结构解析 下图展示了 Java 开发环境的层级关系及其核心组件,从底层操作系统到上层开发工具,逐步构建完整的开发与运行环境: 1. 操作系统(Windows, MacOS, Linux, Solaris) 作用&…...
数据结构---图的深度优先遍历(DFS)
一、与树的深度优先遍历之间的联系 1.类似于树的先根遍历。 递归访问各个结点: 2.图的深度优先遍历 先设置一个数组,初始值全部设置为false,先访问一个结点,在用一个循环,依次检查和这个结点相邻的其他结点,…...
QPrintDialog弹出慢的问题
开发环境 操作系统: openkylin2qt版本 : 5.15.10排查过程 首先看下问题的现象, 问题现象 复现问题的demo很简单,只能是从跟踪qt代码方面入手 void MainWindow::on_pushButton_clicked(){QPrinter printer;QPrintDialog dialog(&printer,this);dialog.exec();} 现在需要找一…...
QT-LINUX-Bluetooth蓝牙开发
BlueToothAPI QT-BlueToothApi Qt Bluetooth 6.8.2 官方提供的蓝牙API不支持linux。 D-Bus的API实现蓝牙 确保系统中安装了 BlueZ(版本需≥5.56),并且 Qt 已正确安装并配置了 D-Bus 支持。 默默看了下自己的版本.....D-BUS的API也不支持。 在 D-Bus 中,org 目录是 D-Bus…...
kvm虚拟机的基本使用
[rootkvm ~]# virsh destroy 虚拟机名 #关闭虚拟机 [rootkvm ~]# virsh undefine 虚拟机名 #删除虚拟机 [rootkvm ~]# virsh start 虚拟机名 #开启虚拟机 [rootkvm ~]# virsh console 虚拟机名 #登录虚拟机 [rootkvm ~]# virsh list --all …...
K8S中若要挂载其他命名空间中的 Secret
在Kubernetes(k8s)里,若要挂载其他命名空间中的Secret,你可以通过创建一个 Secret 的 ServiceAccount 和 RoleBinding 来实现对其他命名空间 Secret 的访问,接着在 Pod 中挂载这个 Secret。下面是详细的步骤和示例代码…...
【Java SE】抽象类/方法、模板设计模式
目录 1.抽象类/方法 1.1 基本介绍 1.2 语法格式 1.3 使用细节 2. 模板设计模式(抽象类使用场景) 2.1 基本介绍 2.2 具体例子 1.抽象类/方法 1.1 基本介绍 ① 当父类的某些方法,需要声明,但是又不确定如何实现时ÿ…...
如何理解java中Stream流?
在Java中,Stream 是 Java 8 引入的一个强大API,用于处理集合(如 List、Set、Map 等)数据的流式操作。它提供了一种声明式、函数式的编程风格,可以高效地进行过滤、映射、排序、聚合等操作。 Stream 的核心概念 流&…...
QT编程之数据库开发
一、架构层次 用户接口层 QSqlQueryModel:管理SQL查询结果,提供表格数据模型用于展示QSqlTableModel:支持直接操作数据库表(增删改查)QSqlRelationalTableModel:支持带外键关联的复杂表…...
【10】高效存储MongoDB的用法
目录 一、什么是MongoDB 二、准备工作 (1)安装MongoDB (2)安装pymongo库 三、连接MongoDB 四、指定数据库 五、指定集合 六、插入数据 (1) insert 方法 (2)insert_one(…...
使用Qdrant等其他向量数据库时需要将将numpy 数组转换为列表 确保数据能被正确处理和序列化,避免类型不兼容的问题。
在使用Qdrant等其他向量数据库时需要 转换 numpy 数组为列表主要是为了确保数据能被正确处理和序列化,避免类型不兼容的问题。具体原因如下: 序列化兼容性: 很多数据库接口、API 或者 JSON 序列化工具只能处理 Python 的内置类型(…...
mayfly-go开源的一站式 Web 管理平台
mayfly-go 是一款开源的一站式 Web 管理平台,旨在通过统一的界面简化 Linux 服务器、数据库(如 MySQL、PostgreSQL、Redis、MongoDB 等)的运维管理。以下从多个维度对其核心特性、技术架构、应用场景及生态进行详细解析: 一、核心…...
Linux中的yum和vim工具使用总结
在Linux系统管理和文本编辑中,yum和vim是两个非常重要的工具。yum作为包管理器帮助我们轻松安装和管理软件,而vim则是一个功能强大的文本编辑器。下面我将对这两个工具进行详细介绍。 一、YUM包管理器 1. YUM简介 YUM (Yellowdog Updater Modified) 是…...
笔记:代码随想录算法训练营day58:101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿
学习资料:代码随想录 文中含大模型生成内容 101. 孤岛的总面积 卡码网:101. 孤岛的总面积 所以找周边都是水的陆地的方法就是找边缘的陆地然后删除它连同它的连通的陆地 深搜 #include <iostream> #include <vector> using namespac…...
Rust语言介绍和猜数字游戏的实现
文章目录 Rust语言介绍和猜数字游戏的实现cargo是什么使用Rust编写猜数字 Rust语言介绍和猜数字游戏的实现 Rust语言是一种系统编程语言,核心强调安全性、并发性以及高性能,由类似于C/C的底层控制能力,性能也非常接近,Rust有一些…...
高并发库存系统是否适合使用 ORM(Hibernate / MyBatis)
在设计高并发的库存管理系统时,数据层的选择至关重要。许多企业开发中习惯使用 ORM(如 Hibernate、MyBatis)来简化数据库访问,但在高并发、高吞吐的场景下,ORM 的适用性往往成为争议焦点。本文将探讨高并发库存系统是否…...
Spring Boot中接口数据字段为 Long 类型时,前端number精度丢失问题解决方案
Spring Boot中接口数据字段为 Long 类型时,前端number精度丢失问题解决方案 在Spring Boot中,当接口数据字段为 Long 类型时,返回页面的JSON中该字段通常会被序列化为数字类型。 例如,一个Java对象中有一个 Long 类型的属性 id …...
Java-servlet(七)详细讲解Servlet注解
Java-servlet(七)详细讲解Servlet注解 前言一、注解的基本概念二、Override 注解2.1 作用与优势2.2 示例代码 三、Target 注解3.1 定义与用途3.2 示例代码 四、WebServlet 注解4.1 作用4.2 示例代码 五、反射与注解5.1 反射的概念5.2 注解与反射的结合使…...
OpenCV Imgproc 模块使用指南(Python 版)
一、模块概述 imgproc 模块是 OpenCV 的图像处理核心,提供从基础滤波到高级特征提取的全流程功能。核心功能包括: 图像滤波:降噪、平滑、锐化几何变换:缩放、旋转、透视校正颜色空间转换:BGR↔灰度 / HSV/Lab 等阈值…...
PostgreSQL:简介与安装部署
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
流畅如丝:利用requestAnimationFrame优化你的Web动画体验
requestAnimationFrame 是前端开发中用于优化动画性能的 API。它允许浏览器在下一次重绘之前执行指定的回调函数,通常用于实现平滑的动画效果。 1.作用 优化性能:requestAnimationFrame 会根据浏览器的刷新率(通常是 60Hz,即每秒…...
OpenCV 基础模块 Python 版
OpenCV 基础模块权威指南(Python 版) 一、模块全景图 plaintext OpenCV 架构 (v4.x) ├─ 核心层 │ ├─ core:基础数据结构与操作(Mat/Scalar/Point) │ └─ imgproc:图像处理流水线(滤…...
代码随想录算法训练营第十五天 | 数组 |长度最小的子数组和螺旋矩阵II
长度最小的子数组 【题目简介】 【自写数组解法】 class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:minLength float(inf)slow 0fast 0cur_sum nums[slow]# 终止条件:fast不能超过最大索引值while slow < fast and fas…...
C++ 入门第27天:异常处理详细讲解
往期回顾: C 入门第24天:C11 多线程基础-CSDN博客 C 入门第25天:线程池(Thread Pool)基础-CSDN博客 C 入门第26天:文件与流操作基础-CSDN博客 C 入门第27天:异常处理详细讲解 前言 在 C 开发中…...
Powershell WSL导出导入ubuntu22.04.5子系统
导出Linux子系统 导出位置在C盘下,根据自己的实际情况更改即可Write-Host "export ubuntu22.04.5" -ForegroundColor Green wsl --export Ubuntu-22.04 c:\Ubuntu-22.04.tar 导入Linux子系统 好处是目录可用在任意磁盘路径,便于迁移不同的设备之间Write-Host &quo…...
中文文献去哪里查找,个人下载知网、万方、维普文献途径
国内三大知识库知网、万方、维普是查找中文文献常用数据库,本文将以实例演示个人下载这三个数据库文献的途径及过程。 先说下途径: 获取知网、万方、维普数据库资源可去文献党下载器网站: 使用方法: 在文献党下载器官网下载安装…...
玩转C#函数:参数、返回值与游戏中的攻击逻辑封装
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
简易shell
自主实现shell done,故意写成,表示先赋值,再判断,分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL,并且while判断结束 Makefile 1 myshell:myshell.c …...
注意力机制,本质上是在做什么?
本文以自注意机制为例,输入一个4*4的矩阵 如下: input_datatorch.tensor([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16] ],dtypetorch.float) 得到Q和K的转置如下。 此时,计算QK^T ,得到如下结果 第一行第一个位置就是第一条样本和第…...
【机器学习-模型评估】
“评估”已建立的模型 在进行回归和分类时,为了进行预测,定义了预测函数fθ(x) 然后根据训练数据求出了预测函数的参数θ(即对目标函数进行微分,然后求出参数更新表达式的操作) 之前求出参数更新表达式之后就结束了。但是,其实我…...
19681 01背包
19681 01背包 ⭐️难度:中等 🌟考点:动态规划、01背包 📖 📚 import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 10001…...
Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测
Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测 目录 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预…...
基于CAMEL 的Workforce 实现多智能体协同工作系统
文章目录 一、workforce 简介1.架构设计2.通信机制 二、workforce 工作流程图示例1.用户角色2.工作流程 三、workforce 中重要函数说明1.__init__函数2.add_single_agent_worker 函数3.add_role_playing_worker 函数4.add_workforce 函数 四、基于workforce实现多智能体协调&am…...
炫酷的3D按钮效果实现 - CSS3高级特性应用
炫酷的3D按钮效果实现 - CSS3高级特性应用 这里写目录标题 炫酷的3D按钮效果实现 - CSS3高级特性应用项目介绍核心技术实现1. 基础结构设计2. 视觉效果实现2.1 背景渐变2.2 立体感营造 3. 交互动效设计3.1 悬停效果3.2 按压效果 技术要点分析1. 深度层次感2. 动画过渡3. 性能优…...
AI视频是否会影响原创价值
AI视频是否会影响原创价值 AI视频带来全民创意对原创内容的影响 随着AI技术的发展,AI视频技术在视频领域的影响也逐渐凸显,从自动剪辑、特效生成到基于文本或语音自动生成视频内容,这一系列的进步极大地降低了视频制作的技术门槛与成本。这种…...