解决qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed

引言

 想用Qt写爬虫玩玩,但是在向网页请求的过程中,报错下面错误。

qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed

解决过程

认知错误

 一开始看到connectToHostEncrypted,觉得大概是连接加密的意思,以为是网站的问题(感觉是网站的一些放爬虫的防护措施)。于是换了几个网站,无一例外,都不行。
 于是拿了一个本地的.html的文档,请求了一下,没有报错。区别就是本地通信和网络通信
 老是这么试来试去猜来猜去也不是办法,于是查询了该错误关键字,TLS initialization failed

传输层安全性协议(英语:Transport Layer Security,缩写作TLS),及其前身安全套接层(Secure Sockets Layer,缩写作SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。网景公司(Netscape)在1994年推出首版网页浏览器,网景导航者时,推出HTTPS协议,以SSL进行加密,这是SSL的起源。IETF将SSL进行标准化,1999年公布第一版TLS标准文件。随后又公布RFC 5246 (2008年8月)与RFC 6176(2011年3月)。在浏览器、邮箱、即时通信、VoIP、网络传真等应用程序中,广泛支持这个协议。主要的网站,如Google、Facebook等也以这个协议来创建安全连线,发送数据。目前已成为互联网上保密通信的工业标准。

 原来是安全传输协议相关初始化失败(这不废话,错误里都写了)。TLS呢,大概意思就是在数据到网络传输之前做了一层安全措施(加密),也就是安全传输层,Transport Layer Security。
 而它的前身SSL,对HTTP协议加了密,变成了HTTPS。
 这边简单介绍下,网站的协议,
protocol
 HTTPS ≈ HTTP + SSL,即有了加密层的HTTP。
 当然,上面的说法很不严谨,不过不深入了解的话大致可以这么理解。
 看完这些之后,我发现刚刚访问的所有报错的网页,都是HTTPS协议的
 那我如果不用HTTPS协议,用HTTP协议,是不是就不用TLS初始化了,就不会报错了。于是,我找了好久,终于找到了HTTP协议的网站,放入代码中请求,果然,可以访问了,不会报上面错误。(至于HTTP协议的网站那么少,应该是它的安全性不高,会暴露用户信息,所以现在用的已经不多了)
 总而言之,就是加密层初始化失败了,至于为什么失败,可能性很多(用法问题?缺少库?网站问题?等等)

定位错误

 于是,我查询Qt包中关于TLS/SSL的文档,
其中Qt助手(Qt assistant)中更多的是关于QSslSocket使用方法的介绍,我从QSslSocket的细节说明中,没有看到有关初始化TLS的注意事项。但是在QSslSocket的静态成员函数中,发现了几个可能有用的函数,

	long sslLibraryBuildVersionNumber()		// 返回编译时SSL(静态库)版本号
	QString sslLibraryBuildVersionString()	// 返回编译时SSL(静态库)版本字符串
	long sslLibraryVersionNumber()			// 返回运行时SSL库版本号
	QString sslLibraryVersionString()		// 返回运行时SSL库版本字符串
	bool supportsSsl()						// 返回是否支持SSL true支持/false不支持

 然后,我将这几个函数一一加入代码,运行后打印情况如下,

	269488175 "OpenSSL 1.1.1b  26 Feb 2019" "" "" false

 打印显示了我编译时有链接静态库,但是我没有动态库,所以最终不支持SSL
 到这里,基本上就定位了错误,是因为我缺少SSL相关动态库

查找根源

 我很不解,Qt为什么编译的时候给你link了静态库,而又不把动态库给你安排好呢。
 于是,我想确定Qt到底有没有给你准备SSL动态库,便在Qt安装目录下,搜索ssl关键字。结果出来很多文件,

	ssleay32.dll libssl.a libssl.dll.a

 于是,我将与编译工具链(mingw64)相同目录下的这几个静态库链入程序,动态库放入可执行文件目录下,运行结果并没有变化,还是报错,且打印不支持。
 这时没辙了,翻了一遍搜索目录,也没有找到其他可能相关的库,仔细一想,实际链接和运行需要的库名都不知道,就开始乱加库,属实不科学。
 但这时,又在搜索目录下,看到了一个ssl.html文档,觉得里面可能有我想要的东西。于是打开来,确实里面讲到了一些根本性的东西,

Enabling and Disabling SSL Support
When building Qt from source, the configuration system checks for the presence of the openssl/opensslv.h header provided by source or developer packages of OpenSSL.
By default, an SSL-enabled Qt library dynamically loads any installed OpenSSL library at run-time. However, it is possible to link against the library at compile-time by configuring Qt with the -openssl-linked option.
When building a version of Qt linked against OpenSSL, the build system will attempt to link with libssl and libcrypt libraries located in the default location on the developer’s system. This location is configurable: set the OPENSSL_LIBS environment variable to contain the linker options required to link Qt against the installed library. For example, on a Unix/Linux system:
OPENSSL_LIBS=’-L/opt/ssl/lib -lssl -lcrypto’ ./configure -openssl-linked
To disable SSL support in a Qt build, configure Qt with the -no-openssl option.
Datagram Transport Layer Security
Datagram Transport Layer Security (DTLS) is a protocol that enables security for datagram-based applications, providing them with protection against eavesdropping, tampering, or message forgery. The DTLS protocol is based on the stream-oriented Transport Layer Security (TLS) protocol. QtNetwork enables the use of DTLS with User Datagram Protocol (UDP), as defined by RFC 6347.

扫描二维码关注公众号,回复: 8729787 查看本文章

Import and Export Restrictions
Qt binary installers include the OpenSSL libraries used by QtNetwork. However, those are not automatically deployed with applications that are built with Qt. Import and export restrictions apply for some types of software, and for some parts of the world. Developers wishing to use SSL communication in their deployed applications should either ensure that their users have the appropriate libraries installed, or they should consult a suitably qualified legal professional to ensure that applications using code from the OpenSSL project are correctly certified for import and export in relevant regions of the world.

译文:

  1. 启用和关闭SSL支持
     用源码编译Qt时,配置系统会检查OpenSSL的开发包或者源码提供的openSSL openssl/opensslv.h头文件是否存在。
     默认情况下,启用了SSL的Qt库会动态加载任意的OpenSSL运行时库(动态库)。但是,通过-openssl-linked选项也可以编译时链接到库。
     当构建一个带OpenSSL链接的版本时,构建系统()会尝试链接开发者系统默认路径下的libssl和libcrypt。这个路径可配置,设置OPENSSL_LIBS环境变量来包含链接Qt需要的链接选项(若要在Qt构建中禁止SSL支持,用-no-openssl选项)。例如,Unix/Linux系统中:
	OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked
  1. 数据报传输层的安全性(DTLS)
     DTLS是一种协议,它为应用程序数据流提供安全性,提供用于防窃听、篡改、消息伪造的保护。
     DTLS协议基于面向流的传输层安全性协议(TLS)。
     QtNetWork支持用户数据报协议(UDP)来使用DTLS。RFC6347中定义了该条目。

  2. 进出口管制
     Qt二进制安装包包含了QtNetWork使用的OpenSSL库。不过,它不会自动部署到Qt构建的应用程序中。
     进出口管制适用于某些类型(涉及加密)的软件,也适用于某些地区。
     开发者希望在部署的应用程序中使用SSL通信应该确保用户安装了匹配的库,或者它们应该查阅相关法律确保当地(进出口管制相关地区)在应用程序中使用OpenSSL项目的代码是合理的、被成功认证的。

 大致说,Qt模块中是包含了OpenSSL库的,但是这些库不会自动部署到应用程序中。开发人员希望使用SSL的同时,它们的用户也安装了相关库。或者确保在当地使用SSL是经过许可的。
 反正,Qt因为某些顾虑(可能和SSL本身的一些条例相关),支持SSL,但是不完全提供SSL,需要你确认可以使用后,才去使用它。
题外话,
 关于这点,我听说美国对软件出口限制很严,

  1. 软件出口需要申请许可
  2. 如果软件是开源的,那不需要,但是如果开源涉及加密技术,也需要申请许可
  3. 如果加密技术也开源,则不需要申请许可,但是会列入清单备查

 而OpenSSL就是一个开源的涉及加密的软件,所以可能在某些地区使用时,会有一些限制。

解决问题

 到这,我们知道了Qt不直接提供SSL动态库,当然可能工程中确实有(但它也没有让用户直接使用)。而我系统中因为版本问题或者编译器问题也没有直接能使用的SSL库。所以最有效的方法就是根据sslLibraryBuildVersionString提供的版本号,去SSL官网下载对应版本的库,引入到程序中。
openssl官网首页链接
openssl历史版本地址
 我在openssl历史版本中找到了我需要的1.1.1.b,经过一系列编译操作后,得到了对应版本的库。
openssl
 于是,将编译所得的动态库加入执行目录下,最终完成了对HTTPS网页的请求。

269488175 "OpenSSL 1.1.1b  26 Feb 2019" "OpenSSL 1.1.1b  26 Feb 2019" "OpenSSL 1.1.1b  26 Feb 2019" true

总结

 上述是一个比较标准的解决方案。
但我觉得Qt安装目录下肯定有能直接跑的库(ps:虽然我找了好久没找到,试了几个网上的/网上指名的库也都不行)。有错误欢迎指正。
 mingw64下编译OpenSSL的教程我之后会写,需要用到msys2/perl/mingw64。

参考

  • qtnetwork/ssl.html
  • Qt Assistant
  • 网上的HTTP/HTTPS介绍
发布了60 篇原创文章 · 获赞 18 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/BadAyase/article/details/103905615