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:使用预编译的二进制文件

  1. 访问 OpenSSL官方下载页面Win32/Win64 OpenSSL安装器
  2. 下载适合您系统的安装包(32位或64位)
  3. 运行安装程序,按照向导完成安装

方法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项目

手动配置

  1. 打开项目属性(右键项目 -> 属性)
  2. 添加包含目录:
    • C/C++ -> 常规 -> 附加包含目录:添加 C:\OpenSSL-Win64\include (根据安装位置调整)
  3. 添加库目录:
    • 链接器 -> 常规 -> 附加库目录:添加 C:\OpenSSL-Win64\lib (根据安装位置调整)
  4. 添加依赖库:
    • 链接器 -> 输入 -> 附加依赖项:添加 libssl.liblibcrypto.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() {
    // 初始化OpenSSL
    ERR_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();
    
    // 初始化密钥和IV
    if (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();
    
    // 初始化密钥和IV
    if (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() {
    // 初始化OpenSSL
    ERR_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() {
    // 初始化OpenSSL
    ERR_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. 部署注意事项

  1. 确保DLL文件可以被应用程序找到:

    • libssl-1_1-x64.dlllibcrypto-1_1-x64.dll(或相应版本)放在应用程序目录中
    • 或者确保它们在系统PATH环境变量包含的目录中
  2. 构建项目时注意:

    • 确保编译版本(Debug/Release)与OpenSSL库的版本(Debug/Release)匹配
    • 32位应用程序应使用32位OpenSSL库,64位应用程序应使用64位OpenSSL库
  3. 版本兼容性:

    • 保持应用程序使用的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-windowsvcpkg install openssl:x64-windows

2. 配置MFC项目

在Visual Studio中配置MFC项目:

  1. 右键点击项目 → 属性
  2. 配置包含目录:
    • C/C++ → 常规 → 附加包含目录: 添加 C:\path\to\openssl\include
  3. 配置库目录:
    • 链接器 → 常规 → 附加库目录: 添加 C:\path\to\openssl\lib
  4. 添加依赖库:
    • 链接器 → 输入 → 附加依赖项: 添加 libssl.liblibcrypto.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();
    
    // 初始化OpenSSL
    ERR_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();
    
    // 清理OpenSSL
    EVP_cleanup();
    ERR_free_strings();
}

4. 在资源文件中添加UI控件

在对话框资源中添加:

  • 一个编辑框(IDC_EDIT_MESSAGE)用于输入消息
  • 一个按钮(IDC_BUTTON_SIGN)用于触发签名
  • 一个编辑框(IDC_EDIT_RESULT)用于显示结果

5. 注意事项

  1. 确保使用正确版本的OpenSSL库(32位/64位)与项目配置匹配
  2. 在Debug和Release配置中都要配置包含目录和库目录
  3. 处理好OpenSSL库的内存管理,避免内存泄漏
  4. 如有多线程需求,确保OpenSSL线程安全配置

这个示例展示了如何在MFC应用程序中集成OpenSSL库进行ECC签名和验证,您可以根据需要进行修改和扩展。

<think>好,我现在需要帮助用户诊断他们的Nginx配置中的SSL证书问题。用户提供的配置看起来已经启用了HTTPSHTTP/2,监听443端口,证书路径也正确。但用curl访问时出现SSL_ERROR_SYSCALL错误。我需要逐步分析可能的原因。 首先,检查证书文件路径是否正确。用户配置中的ssl_certificate是miamor.top_bundle.crt,可能这个文件应该包含服务器证书中间证书的合并。如果路径不对或者权限有问题,Nginx可能无法加载证书,导致SSL握手失败。需要确认文件是否存在,权限是否允许Nginx进程读取。 其次,证书私钥的匹配问题。如果私钥证书不配对,SSL连接会失败。用户可以使用openssl命令检查证书私钥是否匹配,比如openssl rsa -in miamor.top.key -noout -modulusopenssl x509 -in miamor.top_bundle.crt -noout -modulus,比较两者的哈希值是否一致。 接下来,SSL协议密码套件配置。用户配置了TLSv1.2TLSv1.3,这通常是正确的,但可能需要更广泛的密码套件支持。例如,某些客户端可能不支持当前的密码列表,导致协商失败。建议使用更通用的密码套件,比如Mozilla推荐的配置,或者检查ssl_ciphers的设置是否正确。 另外,防火墙或端口443是否开放。虽然用户能访问到443端口,但可能中间有防火墙拦截了SSL握手的数据包。需要确认服务器的防火墙设置,以及云服务提供商的安全组是否允许443端口的入站流量。 还有Nginx服务是否已正确加载配置并重启。用户可能在修改配置后没有执行nginx -s reload或重启服务,导致旧配置仍在运行。需要确认配置是否生效,检查Nginx的错误日志,通常于/var/log/nginx/error.log,查看是否有相关的SSL错误信息,比如证书加载失败或协议不支持等。 此外,可能还有其他因素,比如证书是否过期,域名是否正确绑定,或者中间证书是否缺失。用户提供的证书文件是否包含了完整的证书链,如果中间证书缺失,某些客户端可能无法验证证书链,导致握手失败。可以尝试使用在线工具如SSL Labs的SSL Test来检查证书链的完整性。 用户使用curl测试时出现的错误SSL_ERROR_SYSCALL可能由多种原因引起,包括网络问题、服务端配置错误或客户端问题。需要逐步排除,比如使用openssl s_client -connect miamor.top:443命令查看详细的握手过程,或者在其他设备上测试,确认是否是客户端的问题。 总结可能的解决步骤:检查证书路径权限,验证证书私钥是否匹配,调整SSL配置使用更兼容的密码套件,检查防火墙设置,查看Nginx错误日志,确保证书链完整,最后使用诊断工具进一步排查。</think>通过您的Nginx配置错误现象分析,证书相关的问题可能涉及以下几个方面,我们逐步排查: --- ### 一、证书路径验证(最常见问题) ```nginx ssl_certificate miamor.top_bundle.crt; # 需验证文件存在性 ssl_certificate_key miamor.top.key; # 需验证文件权限 ``` - **关键检查项**: 1. 确保证书文件路径是 **绝对路径**(建议法:`/etc/ssl/miamor.top_bundle.crt`) 2. 检查证书文件权限: ```bash # 证书需可读,私钥需严格权限 chmod 644 miamor.top_bundle.crt chmod 600 miamor.top.key ``` 3. 通过命令验证Nginx是否能加载证书: ```bash sudo nginx -t # 重点观察是否有"SSL: error"提示 ``` --- ### 二、证书链完整性(中级问题) - **现象特征**:浏览器可能正常访问,但命令行工具(如curl)报错 - **验证方法**: ```bash openssl s_client -connect miamor.top:443 -showcerts ``` - 观察输出是否包含完整的证书链(服务器证书+中间证书) - 若`miamor.top_bundle.crt`未合并中间证书,需按顺序拼接: ``` # 正确顺序:你的证书 → 中间证书 cat miamor.top.crt intermediate.crt > miamor.top_bundle.crt ``` --- ### 三、SSL协议/加密套件兼容性(进阶问题) 当前配置: ```nginx ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ``` - **潜在问题**: - 如果证书是RSA类型但配置了`ECDHE-ECDSA`开头的套件,会导致握手失败 - 旧版curl可能不支持TLS1.3(可通过`curl -v`观察协商结果) - **建议优化**: ```nginx ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256; ``` --- ### 四、辅助诊断命令 1. **验证证书与私钥匹配性**: ```bash openssl x509 -noout -modulus -in miamor.top_bundle.crt | openssl md5 openssl rsa -noout -modulus -in miamor.top.key | openssl md5 # 两个MD5值必须一致 ``` 2. **查看详细SSL握手过程**: ```bash curl -vvI https://miamor.top --tlsv1.2 --tls-max 1.3 ``` 3. **检查端口可达性**: ```bash telnet miamor.top 443 # 确认端口连通性 sudo netstat -tuln | grep 443 # 确认Nginx监听状态 ``` --- ### 五、临时调试建议 1. 在`server`块中添加显式根证书配置: ```nginx ssl_trusted_certificate /path/to/root_ca.crt; # 部分系统需要 ``` 2. 暂时开放TLSv1.1测试兼容性: ```nginx ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # 测试后恢复严格配置 ``` 请按上述步骤排查后,可通过`journalctl -u nginx --since "5 minutes ago"`查看最新日志辅助定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黄人软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值