VS C++实现openssl访问https网页教程及实例

之前我实现了访问 http 网页,以及解析大小字段实现网页的完整读取。但现在很多网站都是 https 了,而 https 在 TCP 和 IP 层中间加了一个 SSL 或者 TLS 协议,相当地麻烦,还涉及到公钥加密等,徒手搓相当不现实。

C++也是这点缺点,调库很麻烦。不像 python,直接 import urllib,urlopen就可以直接开 https 的网页。

本文使用 openssl库 来实现 https 网页的访问。因为我在 openssl库的使用中踩了很多坑,所以我会尽量把我踩的坑都说清楚,以免读者再踩。

先看结果:
VS C++实现openssl访问https网页教程及实例_第1张图片

下载openssl

openssl托管在github的,不过README上也写明了,它不用github的release功能来发布,只在官网发布。

所以,第一步,打开https://www.openssl.org/source/,下载我红圈圈住的这个链接。3.0.0那个虽然更新,但是是预发布版,我们暂时不管他,用1.1.1g这个版本。
VS C++实现openssl访问https网页教程及实例_第2张图片

下载Perl

openssl没有用cmake,它提供的编译方式是,使用perl和nasm生成makefile,然后通过makefile编译。perl和nasm必下,不能避开。

打开http://downloads.activestate.com/ActivePerl/releases/,往下翻,有Downloads的框,选最新版,点进去,然后选名字类似于 ActivePerl-5.28.1.2801-MSWin32-x64-24563874.exe 格式的,下载,一路next安装。
VS C++实现openssl访问https网页教程及实例_第3张图片

下载NASM

打开https://www.nasm.us/pub/nasm/releasebuilds/,选择版本下载。很奇怪的是,我下载最新的2.15rc12版本,Windows Defender提示有病毒。我最后下载的2.14rc15版本,就没有提示病毒了:https://www.nasm.us/pub/nasm/releasebuilds/2.14rc15/win64/nasm-2.14rc15-installer-x64.exe。也是一路next安装。
VS C++实现openssl访问https网页教程及实例_第4张图片

设置环境变量

perl安装时会自动设置环境变量,nasm不会,需要手动添加。确保PATH环境变量下有以下三条。
VS C++实现openssl访问https网页教程及实例_第5张图片

生成makefile

我这里默认你已经安装了VS 2019 Community,在开始菜单找到x86本地命令行工具,右键 更多 - 以管理员身份运行
VS C++实现openssl访问https网页教程及实例_第6张图片
输入D:(你放openssl的盘符),然后cd后跟openssl解压后的目录。
VS C++实现openssl访问https网页教程及实例_第7张图片
运行perl configure VC-WIN32,这里的configure是一个文件的名字,如果它说找不到configure的话,就把configura换成绝对路径,类似D:\REFER\Program\lib\openssl\openssl-1.1.1g\configure
这种的。
VS C++实现openssl访问https网页教程及实例_第8张图片
现在会出现上图的结果,白底红字的意思是找不到nmake。不过不管它。

编译makefile

现在需要运行一个叫vcvarsall.bat的东西。在我电脑上它放在这里:C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat,所以我运行:

"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x86

注意要带上双引号。得到如下结果:
VS C++实现openssl访问https网页教程及实例_第9张图片
出现最后这句就说明现在的环境变量设置成功了。

然后运行nmake
VS C++实现openssl访问https网页教程及实例_第10张图片
这里会持续运行几分钟,把很多.c文件编译成obj。如果在末尾出现了fetal error:字样,可能是你运行的VS 命令行工具、vcvarsall.bat中指定的平台指令集不一致(命令行工具运行的x64版,vcvarsall.bat的参数x86_amd64、或者perl命令中的参数是VC-WIN64A)。

然后运行nmake test
VS C++实现openssl访问https网页教程及实例_第11张图片
成功的话会在openssl目录下生成一堆.lib和.dll文件。
VS C++实现openssl访问https网页教程及实例_第12张图片
到这里openssl的编译就完成了。如果需要编译64位,可以把前面的参数都改成64对应的参数。如果需要其他编译参数,可以打开makefile查看,需要一点点makefile的知识。

实例

下面通过一个实例实现https网页的访问,代码来自另一位博主,链接提供在代码最后。我做了小幅改动。

打开VS 2019,新建命令行项目,填入代码:

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include 
#include 

#include  

#include 
#include 

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

using namespace std;

int main()
{
     
	//加载SSL错误信息 
	SSL_load_error_strings();
	//添加SSL的加密/HASH算法 
	SSLeay_add_ssl_algorithms();
	//客户端,服务端选择SSLv23_server_method() 
	const SSL_METHOD* meth = SSLv23_client_method();
	//建立新的SSL上下文 
	SSL_CTX* ctx = SSL_CTX_new(meth);
	if (ctx == NULL)
	{
     
		ERR_print_errors_fp(stderr);
		cout << "SSL_CTX_new error !";
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
     
		return -1;
	}
	SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (client == INVALID_SOCKET)
	{
     
		cout << "socket error !";
		return -1;
	}

	string host = "www.baidu.com";
	unsigned short port = 443;
	hostent* ip = gethostbyname(host.c_str());

	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr = *(in_addr*)ip->h_addr_list[0];

	if (connect(client, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
     
		cout << "connect error 1";
		return -1;
	}

	//建立SSL 
	int ret;
	SSL* ssl = SSL_new(ctx);
	if (ssl == NULL)
	{
     
		cout << "SSL NEW error";
		return -1;
	}
	//将SSL与TCP SOCKET 连接 
	SSL_set_fd(ssl, client);
	//SSL连接 
	ret = SSL_connect(ssl);
	if (ret == -1)
	{
     
		cout << "SSL ACCEPT error ";
		return -1;
	}

	stringstream stream;
	stream << "GET https://" << host << " HTTP/1.0\r\n";
	stream << "Accept: */*\r\n";
	//stream << "Accept-Encoding: gzip, deflate, br\r\n";//不要编码,否则还得多一个解码的步骤
	stream << "Accept-Language: zh-Hans-CN, zh-Hans; q=0.8, en-US; q=0.5, en; q=0.3\r\n";
	stream << "Connection: Keep-Alive\r\n";
	stream << "Host: " << host << "\r\n";
	stream << "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134\r\n";
	stream << "\r\n";

	string s = stream.str();
	const char* sendData = s.c_str();
	ret = SSL_write(ssl, sendData, strlen(sendData));
	if (ret == -1)
	{
     
		cout << "SSL write error !";
		return -1;
	}
	char* rec = new char[1024 * 1024];
	int start = 0;
	while ((ret = SSL_read(ssl, rec + start, 1024)) > 0)
	{
     
		start += ret;
	}
	rec[start] = 0;
	cout << rec;

	//关闭SSL套接字 
	SSL_shutdown(ssl);
	//释放SSL套接字 
	SSL_free(ssl);
	//释放SSL会话环境 
	SSL_CTX_free(ctx);

	closesocket(client);
	WSACleanup();
}
//————————————————
//版权声明:本文为CSDN博主「ZRXSLYG」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/ZRXSLYG/article/details/81395640

然后打开 调试 - XX属性,在VC++目录中加入包含目录和库目录,如图:
VS C++实现openssl访问https网页教程及实例_第13张图片
红线部分要换成你自己的目录。

把刚才编译生成的libssl-1_1.dlllibcrypto-1_1.dll放进工程目录。在VS中调试的话就放这里,如果要单独运行exe的话,就复制一份和exe呆在一个目录下。不做这步的话,运行程序会弹出“不能链接到xxx.dll”
VS C++实现openssl访问https网页教程及实例_第14张图片
运行结果就是这样:
VS C++实现openssl访问https网页教程及实例_第15张图片

参考文献

这篇是我的主要参考来源,不过有些坑没有指出来,后半部分的ms/do_nasm在现在的版本已经没有了。

  • Windows 编译 openssl-1.0.2p静态库 32/64、debug/release、MT/MTd

实例中的代码来自这里:

  • C/C++实现HTTPS通信(抓取百度页面)

这是官方给出的编译步骤:

  • https://github.com/openssl/openssl/blob/master/NOTES-Windows.txt

你可能感兴趣的:(密码学,网络)