Windows C++下使用c++-httplib库与Openssl库搭建https服务器与客户端通讯的保姆教程
创始人
2025-01-19 15:32:37
0

Windows C++下使用c++-httplib库与Openssl库搭建https服务器与客户端通讯的保姆教程

    • 前言
    • c++ -httplib源码下载
    • Openssl源码下载编译
      • 1.openssl源码下载
      • 2.编译环境准备
        • 1)安装Perl
        • 2)安装NASM
        • 3)编译openssl
    • 使用Openssl生成本地CA证书用于搭建https本地测试通讯
      • 1.CA:
      • 2.服务器
      • 3.客户端
    • VS2015下c++项目实战
      • 1.Openssl在VS2015工程中的配置
      • 2.本地测试代码如下
    • 结语
    • tips

前言

实现一个c++的https客户端请求https服务器是实现数据通信 。

基于已封装好的c++ -httplib库搭建SSL/TLS环境实现。

c++ -httplib源码下载

httplib库是一个基于C++11特性编写的库,所以编译器需要能支持C++11。
c++ -httplib源码下载网址直接下载zip包
此库的源代码只有一个头文件,所以在使用时只需在项目中包含一个头文件即可
解压zip包的要用的头文件如下所示:
在这里插入图片描述

Openssl源码下载编译

1.openssl源码下载

本人项目是基于为win32环境的x86编译,环境都是安装的win32版本,如是win64版本请另行下载各自对应的版本
openssl源码下载路径我下载的是openssl-1.1.1v.tar.gz可以使用,所以推荐下载此版本
若想下载旧版本,则点击old releases获取即可
在这里插入图片描述

2.编译环境准备

openssl官网下载的源码中没找到现成的dll和lib文件,在这里我选择自己编译生成想要的版本库,解压如下:
在这里插入图片描述

1)安装Perl

搜索网络上有的下载的是ActiveState Perl,但是极其麻烦,我弄半天也没下载成功,在此所以推荐下载草莓Perl
下载地址: Windows版本Strawberry Perl
下载好自己的版本,我这里下的是32位的,如下在这里插入图片描述
一般下载安装后会自动添加perl的三个环境变量:在这里插入图片描述
建议安装后还是检查一下,万一没有则手动添加即可。
cmd命令行输入perl -v查看是否安装成功:
在这里插入图片描述

2)安装NASM

官网下载路径
在这里插入图片描述
下载完运行这个exe安装即可,注意这里安装完也要对环境变量进行检查,我就是没检查,然后后面在编译openssl中编译到一半,说我编译环境错误,当时头痛的很,后面全部重新安装了一遍,手动添加了这个环境变量。
在这里插入图片描述
这里变量为你安装NASM的路径,鼠标右击nasm属性查看路径如下:
在这里插入图片描述

3)编译openssl

以上已经安装好所需的环境就可以进行编译了
windows所有程序打开vs2015开发人员命令提示应用,我的开发环境是vs2015,vs你们使用自己的版本即可
在这里插入图片描述
在此cmd窗口中进入到刚刚下载解压的openssl源码路径下,
在这里插入图片描述
命令行输入perl Configure VC-WIN32 --shared no-asm --debug --prefix=C:\Common-Test\openSSL --openssldir=C:\Common-Test\SSL
在这里插入图片描述
具体参数配置在openssl源码解压的目录下有个 INSTALL 文件可以看到
32位:VC-WIN32
64位:VC-WIN64A
编译生成动态库Dll:–shared (不生成则使用no-shared,默认不生成)
不使用汇编代码:no-asm
Debug:–debug
Release:–release(默认)
最后安装的目录:–prefix=C:\Common-Test\openSSL
一些配置说明文件存放目录:–openssldir=\Common-Test\SSL

然后依次输入

nmake namke test nmake install nmake clean  //这里是清除生成的多余文件 

等待三个命令运行完成。
注意:中途万一编译失败,请重新安装以上环境并检查环境变量是否存在
在我们的输出安装目录下可以看到以下四个文件夹
在这里插入图片描述
静态库lib文件在lib目录下,
在这里插入图片描述
头文件在include目录下,
在这里插入图片描述
动态库dll文件在bin目录下,在启动运行的时候会用到。这个放在程序启动的那个目录就行了,
在这里插入图片描述
至此,win32系统的openssl编译库完成(其他的版本环境的编译步骤与这个是一致的),下面就可以运用到项目中了。
————————————————

使用Openssl生成本地CA证书用于搭建https本地测试通讯

本地开发https服务是加密的,这里使用openssl自签名证书,并使用基于c+±httplib开启https服务。
在创建证书的过程中,会要求输入密码和证书信息(浏览器地址左侧有一个锁,点开后选择证书看到的信息),密码在输入过程中,控制台不会有任何显示。输入信息需要填写国家(ZH)、省市、机构等信息,由于自己签名并没有公网的可认证性,所以这些信息随便填都可以。后面会要求输入密码生成证书,所以需要记住密码。

1.CA:

生成私钥:这里的1024也可改为2048,指复杂度

openssl genrsa -out ca-key.pem -des 1024 

在这里插入图片描述

这里我设的密码是123456,自己随便设。

生成公钥:

openssl req -new -key ca-key.pem -out ca-csr.pem 

在这里插入图片描述

生成证书:

openssl x509 -req -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem 

在这里插入图片描述
在当前路径下会有 ca-key.pem 、 ca-csr.pem、 ca-cert.pem三个文件,如果其中有步骤出现失误操作,将这些指令重新输入即可
在这里插入图片描述

2.服务器

服务端生成公钥需要读取配置文件,创建openssl.cnf文件在统计目录下,内容为:

[req]             distinguished_name = req_distinguished_name             req_extensions = v3_req                      [req_distinguished_name]             countryName = ZH             countryName_default = CN             stateOrProvinceName = ShenZhen           stateOrProvinceName_default = ShenZhen            localityName = GuangZhou             localityName_default = GuangZhou             organizationalUnitName  = public section             organizationalUnitName_default  = Domain Control Validated             commonName = Internet Widgits Ltd             commonName_max  = 64                      [ v3_req ]             # Extensions to add to a certificate request             basicConstraints = CA:FALSE             keyUsage = nonRepudiation, digitalSignature, keyEncipherment             subjectAltName = @alt_names                      [alt_names]             IP.1 = 127.0.0.1 

上述信息是证书相关的信息,后面的值都是随意写的,可以自己替换。
也可以直接把C:\Common-Test\SSL目录下的openssl.cnf文件拷贝过来
生成私钥:

openssl genrsa -out server-key.pem 1024 

生成公钥:

openssl req -new -key server-key.pem -config openssl.cnf -out server-csr.pem 

生成证书:

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req -extfile openssl.cnf 

在这里插入图片描述

3.客户端

搭建https服务器不需要客户端证书,生成指令和上面类似:

openssl genrsa -out client-key.pem 

生成公钥:

openssl req -new -key client-key.pem -out client-csr.pem 

生成证书:

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem 

最终生成文件如下所示:
在这里插入图片描述
这里我没用到客户端的证书就没生成了,你们可以自己生成。

VS2015下c++项目实战

1.Openssl在VS2015工程中的配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里的lib也可以在代码中加载,如#pragma comment(lib, “libcrypto.lib”)。

2.本地测试代码如下

自己懒得写了,这里是引用的是
Jinato2016大佬的代码
服务器代码

#include "stdafx.h" #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include "httplib.h" #define SERVER_CERT_FILE "C:\\Common-Test\\openSSL\\bin\\server-cert.pem" #define SERVER_PRIVATE_KEY_FILE "C:\\Common-Test\\openSSL\\bin\\server-key.pem"  #pragma comment(lib, "WS2_32.lib")  #define MAXBUF 1024 using namespace std; using namespace httplib;   std::string dump_headers(const Headers &headers) { 	std::string s; 	char buf[BUFSIZ];  	for (auto it = headers.begin(); it != headers.end(); ++it) { 		const auto &x = *it; 		snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); 		s += buf; 	}  	return s; }  std::string log(const Request &req, const Response &res) { 	std::string s; 	char buf[BUFSIZ];  	s += "================================\n";  	snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(), 		req.version.c_str(), req.path.c_str()); 	s += buf;  	std::string query; 	for (auto it = req.params.begin(); it != req.params.end(); ++it) { 		const auto &x = *it; 		snprintf(buf, sizeof(buf), "%c%s=%s", 			(it == req.params.begin()) ? '?' : '&', x.first.c_str(), 			x.second.c_str()); 		query += buf; 	} 	snprintf(buf, sizeof(buf), "%s\n", query.c_str()); 	s += buf;  	s += dump_headers(req.headers);  	s += "--------------------------------\n";  	snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str()); 	s += buf; 	s += dump_headers(res.headers); 	s += "\n";  	if (!res.body.empty()) { s += res.body; }  	s += "\n";  	return s; }   void custom_error_handler(const Request& req, Response& res) { 	// 自定义错误处理逻辑 	//res.status = 500; 	//res.set_content("Custom Error Handler: Something went wrong!", "text/plain");  	const char *fmt = "

Error Status: %d

"; char buf[BUFSIZ]; snprintf(buf, sizeof(buf), fmt, res.status); res.set_content(buf, "text/html"); } int main(void) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); cout << "Waiting for the connection..." << endl; if (!svr.is_valid()) { printf("server has an error...\n"); return -1; } svr.Get("/", [=](const Request & /*req*/, Response &res) { res.set_redirect("/hi"); }); svr.Get("/hi", [](const Request & /*req*/, Response &res) { res.set_content("

Hello ludashi!

", "text/html"); }); svr.Get("/slow", [](const Request & /*req*/, Response &res) { std::this_thread::sleep_for(std::chrono::seconds(2)); res.set_content("Slow...\n", "text/plain"); }); svr.Get("/dump", [](const Request &req, Response &res) { res.set_content(dump_headers(req.headers), "text/plain"); }); svr.Get("/stop", [&](const Request & /*req*/, Response & /*res*/) { svr.stop(); }); Server::Handler hh = custom_error_handler; svr.set_error_handler(hh); //svr.set_error_handler([](const Request & /*req*/, Response &res) { // const char *fmt = "

Error Status: %d

"; // char buf[BUFSIZ]; // snprintf(buf, sizeof(buf), fmt, res.status); // res.set_content(buf, "text/html"); //}); svr.set_logger([](const Request &req, const Response &res) { printf("%s", log(req, res).c_str()); }); svr.listen("127.0.0.1", 8080); system("pause"); return 0; }

客户端代码

#include "stdafx.h" #include "httplib.h" #include  #include #include #define CA_CERT_FILE "C:\\Common-Test\\openSSL\\bin\\ca-cert.pem" using namespace std; using namespace httplib; int main(void)  { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT 	system("C:\\certmgr.exe /add /c C:\\Common-Test\\openSSL\\bin\\ca-cert.pem /s root"); 	cout << "Try to connect....." << endl; 	//Sleep(5000); 	httplib::SSLClient cli("127.0.0.1", 8080);  	cli.set_ca_cert_path(CA_CERT_FILE); 	cli.enable_server_certificate_verification(true); #else 	httplib::Client cli("127.0.0.1", 8080); #endif  	std::string source; // 用于存储资源名称 	while (1) 	{ 		std::getline(std::cin, source, '\n'); 		if (source == "/exit")break; 		 		auto res = cli.Get(source/*"/hi"*/); 		if (res) { 			cout << res->status << endl; 			cout << res->get_header_value("Content-Type") << endl; 			cout << res->body << endl; 		} 		else { 			cout << "error" << endl; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT 			auto result = cli.get_openssl_verify_result(); 			if (result) { 				cout << "verify error: " << X509_verify_cert_error_string(result) << endl; 			} #endif 		}  	} 	return 0; } 

本人在httplib.h中增加了一句
#define CPPHTTPLIB_OPENSSL_SUPPORT在这里插入图片描述

将我们的CA证书添加到受信任的根证书颁发机构是本项目
Certmgr.exe证书管理器工具安装好
客户端运行会颁发证书,弹出以下窗口,点是即可,后续不想弹出,可以注释掉以下这句,

system("C:\\certmgr.exe /add /c C:\\Common-Test\\openSSL\\bin\\ca-cert.pem /s root"); 

也可以手动在cmd窗口输入
在这里插入图片描述
至此本地代码运行如下:
在这里插入图片描述

结语

自认为这应该是相对较全的关于httplib及openssl来开发https服务器的教程。三个字,太不容易了,大家按照我这个可以直接运用到项目中,如若不想那么麻烦,上面环境安装包及代码程序都在资源这里,直接下载就行。

tips

如果不想本地编译openssl,可以直接到此网站下载对应的版本。

相关内容

热门资讯

玩家爆料!大菠萝手游辅助,智星... 玩家爆料!大菠萝手游辅助,智星德州可以透视吗,线上教程(有挂工具)1、每一步都需要思考,不同水平的挑...
第6分钟透视!wejoker内... 第6分钟透视!wejoker内置辅助,(wepoker)一直是有挂,安装教程(有挂解说);1、下载好...
透视脚本!hhpoker视频巡... 透视脚本!hhpoker视频巡查真的假的(底牌)详细透视辅助脚本(有挂方法)1、这是跨平台的hhpo...
7分钟辅助!wpk系统是否存在... 7分钟辅助!wpk系统是否存在作弊行为,wpk控制牌是真的,切实教程(有挂揭秘)1、首先打开wpk系...
技术分享!拱趴大菠萝机器人,s... 技术分享!拱趴大菠萝机器人,sohoo竞技联盟辅助器,必备教程(有挂辅助)所有人都在同一条线上,像星...
玩家攻略推荐!wepoker私... 玩家攻略推荐!wepoker私人局透视教程,wejoker黑侠辅助器,2025教程(有挂解密)小薇(...
透视脚本!hhpoker辅助软... 透视脚本!hhpoker辅助软件下载(底牌)详细透视辅助工具(有挂软件)该软件可以轻松地帮助玩家将h...
七分钟辅助!wpk控制牌是真的... 七分钟辅助!wpk控制牌是真的,wpk真,解说技巧(有挂技巧)1、wpk控制牌是真的透视辅助简单,w...
透视辅助器!wepoker破解... 透视辅助器!wepoker破解游戏盒子,(wepoker)一直是真的有挂(详细透视底牌辅助脚本)1、...
玩家亲测!约局吧开挂(透视脚本... 玩家亲测!约局吧开挂(透视脚本)详细透视辅助工具(都是真的是有挂);1、点击下载安装,约局吧开挂插件...