返回> 网站首页
c++基于openssl使用windows证书
yoours2025-06-24 18:32:16
简介一边听听音乐,一边写写文章。
#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;
}