返回> 网站首页 

c++基于openssl使用windows证书

yoours2025-06-24 18:32:16 阅读 37

简介一边听听音乐,一边写写文章。

#include <stdio.h>

#include <string.h>

#include <winsock2.h>

#include <ws2tcpip.h>

#include <openssl/ssl.h>

#include <openssl/err.h>

#include <openssl/x509v3.h>


#pragma comment(lib, "ws2_32.lib")

#pragma comment(lib, "openssl.lib")


#define strcasecmp _stricmp

#define strncasecmp _strnicmp


#define REQUEST "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n"

#define PORT 443

#define BUFFER_SIZE 4096


void init_openssl() {

    SSL_load_error_strings();

    OpenSSL_add_ssl_algorithms();

}


void cleanup_openssl() {

    EVP_cleanup();

}


// 导入windows证书

#include <wincrypt.h>

void load_windows_root_certs(SSL_CTX* ctx)

{

    HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");

    if (!hStore) {

        fprintf(stderr, "Error opening Windows certificate store\n");

        return;

    }


    X509_STORE* store = SSL_CTX_get_cert_store(ctx);

    PCCERT_CONTEXT pContext = NULL;


    while ((pContext = CertEnumCertificatesInStore(hStore, pContext))) {

        X509* x509 = d2i_X509(NULL,

            (const unsigned char**)&pContext->pbCertEncoded,

            pContext->cbCertEncoded);

        if (x509) {

            X509_STORE_add_cert(store, x509);

            X509_free(x509);

        }

    }

    CertCloseStore(hStore, 0);

}


SSL_CTX* create_ssl_context() {

    const SSL_METHOD* method = TLS_client_method();

    SSL_CTX* ctx = SSL_CTX_new(method);

    if (!ctx) {

        ERR_print_errors_fp(stderr);

        exit(EXIT_FAILURE);

    }


    // 配置验证选项

    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    SSL_CTX_set_verify_depth(ctx, 4);


    // 加载系统默认的CA证书

    if (!SSL_CTX_set_default_verify_paths(ctx)) {

        ERR_print_errors_fp(stderr);

        exit(EXIT_FAILURE);

    }


    // 在 create_ssl_context() 中调用:

    load_windows_root_certs(ctx);

    return ctx;

}


SOCKET create_socket(const char* hostname) {

    WSADATA wsaData;

    SOCKET sockfd = INVALID_SOCKET;

    struct addrinfo hints, * result, * ptr;


    ZeroMemory(&hints, sizeof(hints));

    hints.ai_family = AF_UNSPEC;

    hints.ai_socktype = SOCK_STREAM;

    hints.ai_protocol = IPPROTO_TCP;


    // 解析服务器地址和端口

    if (getaddrinfo(hostname, "443", &hints, &result) != 0) {

        fprintf(stderr, "getaddrinfo failed: %d\n", WSAGetLastError());

        WSACleanup();

        exit(EXIT_FAILURE);

    }


    // 尝试连接

    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

        if (sockfd == INVALID_SOCKET) {

            fprintf(stderr, "socket failed: %d\n", WSAGetLastError());

            continue;

        }


        if (connect(sockfd, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR) {

            closesocket(sockfd);

            sockfd = INVALID_SOCKET;

            continue;

        }

        break;

    }


    freeaddrinfo(result);


    if (sockfd == INVALID_SOCKET) {

        fprintf(stderr, "Unable to connect to server\n");

        WSACleanup();

        exit(EXIT_FAILURE);

    }


    return sockfd;

}


void verify_certificate(SSL* ssl, const char* hostname) {

    X509* cert = SSL_get_peer_certificate(ssl);

    if (cert) {

        if (SSL_get_verify_result(ssl) != X509_V_OK) {

            fprintf(stderr, "Certificate verification failed\n");

            X509_free(cert);

            exit(EXIT_FAILURE);

        }


        // 替代 X509_check_host 的主机名验证

        int match = -1;

        X509_NAME* subj = X509_get_subject_name(cert);

        char temp[256];


        // 检查CN (Common Name)

        X509_NAME_get_text_by_NID(subj, NID_commonName, temp, sizeof(temp));

        if (strcasecmp(temp, hostname) == 0) {

            match = 1;

        }


        // 检查SAN (Subject Alternative Name)

        STACK_OF(GENERAL_NAME)* sans = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);

        if (sans) {

            int san_count = sk_GENERAL_NAME_num(sans);

            for (int i = 0; i < san_count; i++) {

                GENERAL_NAME* san = sk_GENERAL_NAME_value(sans, i);

                if (san->type == GEN_DNS) {

                    char* dnsname = (char*)ASN1_STRING_get0_data(san->d.dNSName);

                    //if (strcasecmp(dnsname, hostname) == 0)

                    {

                        match = 1;

                        break;

                    }

                }

            }

            sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);

        }


        if (match != 1) {

            fprintf(stderr, "Certificate does not match hostname\n");

            X509_free(cert);

            exit(EXIT_FAILURE);

        }


        X509_free(cert);

    }

    else {

        fprintf(stderr, "No peer certificate\n");

        exit(EXIT_FAILURE);

    }

}


void https_request(const char* hostname) {

    SSL_CTX* ctx;

    SSL* ssl;

    SOCKET sockfd;

    char request[512];

    char response[BUFFER_SIZE];

    int bytes;


    // 初始化OpenSSL

    init_openssl();

    ctx = create_ssl_context();

    int d = SSL_CTX_load_verify_locations(ctx, "EncryptionEverywhereDVTLSCA-G1.crt.pem", NULL);


    // 创建TCP套接字并连接

    sockfd = create_socket(hostname);

    // 创建SSL连接

    ssl = SSL_new(ctx);

    SSL_set_fd(ssl, (int)sockfd);


    // 设置SNI

    SSL_set_tlsext_host_name(ssl, hostname);

    if (SSL_connect(ssl) <= 0) {

        ERR_print_errors_fp(stderr);

        exit(EXIT_FAILURE);

    }


    printf("Connected with %s encryption\n", SSL_get_cipher(ssl));


    // 验证证书

    verify_certificate(ssl, hostname);


    // 发送HTTP请求

    snprintf(request, sizeof(request), REQUEST, hostname);

    SSL_write(ssl, request, strlen(request));


    // 读取响应

    while ((bytes = SSL_read(ssl, response, sizeof(response) - 1)) > 0) {

        response[bytes] = '\0';

        printf("%s", response);

    }


    // 清理

    SSL_shutdown(ssl);

    SSL_free(ssl);

    closesocket(sockfd);

    SSL_CTX_free(ctx);

    cleanup_openssl();

}


int main(int argc, char** argv)

{

    printf("OPENSSL_VERSION_NUMBER:\t\t\t0x%x\n", OPENSSL_VERSION_NUMBER);

    printf("OPENSSL_VERSION_TEXT:\t\t\t%s\n", OPENSSL_VERSION_TEXT);

#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L

    printf("SSLeay:\n\t\t\t0x%x\n", SSLeay());

    printf("SSLeay_version(SSLEAY_VERSION):\n\t\t\t%s\n", SSLeay_version(SSLEAY_VERSION));

    printf("SSLeay_version(SSLEAY_CFLAGS):\n\t\t\t%s\n", SSLeay_version(SSLEAY_CFLAGS));

    printf("SSLeay_version(SSLEAY_BUILT_ON):\n\t\t\t%s\n", SSLeay_version(SSLEAY_BUILT_ON));

    printf("SSLeay_version(SSLEAY_PLATFORM):\n\t\t\t%s\n", SSLeay_version(SSLEAY_PLATFORM));

    printf("SSLeay_version(SSLEAY_DIR):\n\t\t\t%s\n", SSLeay_version(SSLEAY_DIR));

#else

    printf("OpenSSL_version_num:\t\t\t0x%x\n", OpenSSL_version_num());

    printf("OpenSSL_version(OPENSSL_VERSION):\t\t\t%s\n", OpenSSL_version(OPENSSL_VERSION));

    printf("OpenSSL_version(OPENSSL_CFLAGS):\t\t\t%s\n", OpenSSL_version(OPENSSL_CFLAGS));

    printf("OpenSSL_version(OPENSSL_BUILT_ON):\t\t\t%s\n", OpenSSL_version(OPENSSL_BUILT_ON));

    printf("OpenSSL_version(OPENSSL_PLATFORM):\t\t\t%s\n", OpenSSL_version(OPENSSL_PLATFORM));

    printf("OpenSSL_version(OPENSSL_DIR):\t\t\t%s\n", OpenSSL_version(OPENSSL_DIR));

    printf("OpenSSL_version(OPENSSL_ENGINES_DIR):\t\t\t%s\n", OpenSSL_version(OPENSSL_ENGINES_DIR));

#endif


    WSADATA tVerData;

    WORD wVersionRequested = MAKEWORD(2, 2);

    WSAStartup(wVersionRequested, &tVerData);


    https_request("registry.npmmirror.com");


    WSACleanup();

    return 0;

}


微信小程序扫码登陆

文章评论

37人参与,0条评论