VC++项目XP系统下,IE8访问HTTPS错误,最后使用OpenSSL解决

问题背景描述:

最近有个项目,需要获取天气预报的信息,查了好多天气预报API都是需要付费的,免费的大多数只有一个温度,其他的啥信息都没有(免费的都没好货)。找了好久,对比了天气信息以及费用的,最后发现和风天气预报API不错,不单只免费,而且需要的空气质量以及紫外线强度都有,最后决定使用它(每天普通用户免费3000次,开发者16000次,最后使用公司的电话号码,申请了好几个Key,以达到每天访问10万次,ps:通过切换key)。


其实开发这个也没什么技术含量的,而且也挺顺利,直接通过MFC框架的CInternetSession类,调用OpenURL函数获取返回的天气信息就结束了。

    CInternetSession InternetSession;
	CStdioFile *pFile;	
	char sText[1024]={0},sBuff[1024]={0};

	try
	{
		if (NULL != (pFile = InternetSession.OpenURL(strServer)))
		{
			CString strLine, strText;
			while(pFile->ReadString(sText,1024)) 
			{
				Utf8ToAnsi(sText,sBuff);
				strLine.Format("%s",sBuff);
				strText += strLine + "\n";
				memset(sText,'\0',1024);
				memset(sBuff,'\0',1024);
			}
			pFile->Close();
			InternetSession.Close();
			delete pFile;
			return strText;
		}
	}
	catch (CInternetException* pEx)
	{
		TCHAR sz[1024] = {0};
		pEx->GetErrorMessage(sz, 1024);
		LXY_PRINTF(LEVEL_WARN,"[ 获取天气预报信息异常:错误码:%d ]::%s\n",pEx->m_dwError,sz);
		pEx->Delete();
		return sz;
	}

问题1:

 突然有天,测试部反馈问题,无法获取天气信息,而且返回错误。但是我在自己开发的电脑上测试了,发现是没问题的,当时就懵逼了,而且不是一台电脑,而是陆陆续续有好几个客户也反馈了,这就让我不得不重视了。

最后,我在开发的环境中重现了该问题,嗯,包括重启电脑,重启软件等等。终于获取到错误的信息。

错误码12057 :Unable to validate the revocation of the SSL certificate because the revocation server is unavailable

说什么ssl,安全性的问题(至此我还是没有想到是什么问题,因为自己不熟悉web这块,所以对证书那些有点懵),只能通过百度了,在百度上查了一下,发现遇到的这个问题都是关于IE浏览器的。因为MFC框架的CInternetSession类,应该是调用IE内核的,所以问题出现在了IE浏览器上。

对于上面的错误,百度网友给了以下两个解决办法:

A、就是手动去把IE浏览器中,“高级”里面的“检查服务器证书是否已吊销”√去掉,不在检查服务器证书;

B、在代码中实现A的效果;

看了B后,果断选择了B(A解决办法会被客户烦死):

CHttpConnection* pHttpConnect = session.GetHttpConnection(strServer, INTERNET_FLAG_SECURE, nPort, NULL, NULL);
		if(pHttpConnect) 
		{
			CHttpFile* pHttpFile = (CHttpFile*)pHttpConnect->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject, NULL, 1,
				NULL, NULL,
				INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION|
				INTERNET_FLAG_SECURE  | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
				//SECURITY_FLAG_IGNORE_REVOCATION
				);
			//get web server option
			pHttpFile->QueryOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlags);
			dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;//忽略错误与未知的证书颁发机构
			dwFlags |= SECURITY_FLAG_IGNORE_REVOCATION;//忽略错误与撤销相关证书
			//<span style="white-space:pre">	</span>
				//set web server option
				pHttpFile->SetOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlags);
			if(pHttpFile->SendRequest()) 
			{
				//get response status if success, return 200
				pHttpFile->QueryInfo(HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusLen, 0);
				while(pHttpFile->ReadString(sText,1024)) 
				{
					Utf8ToAnsi(sText,sBuff);
					strLine.Format("%s",sBuff);
					strHtml += strLine + "\n";
					memset(sText,'\0',1024);
					memset(sBuff,'\0',1024);
				}
			} 

dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;//忽略错误与未知的证书颁发机构
dwFlags |= SECURITY_FLAG_IGNORE_REVOCATION;//忽略错误与撤销相关证书

其中这两个参数是关键。

至此,问题就告一段落了,客户也在没有反馈这个问题。正当我松一口气的时候,以为天气预报这个问题解决了,谁知新的问题也在酝酿中。

问题2:IE8不支持HTTPS

   客户:李工,那个天气预报在XP系统下无法获取信息,并且报错“The connection with the server was reset”(这个是浏览器直接返回的错误信息)

  我:哦,是吗,那我这边试一下,晚点回复你(心中有一万头草泥马在崩腾......)

(俗话说有问题,找度娘,嗯,我不知道没有网络我是否还能做个程序员,难怪每个月就这么点工资,哎。)

经过一系列的查证,定位,发现问题还是出现在IE浏览器中,IE8不支持HTTPS,因为这个天气预报是HTTPS的。所以xp系统下的IE8获取不了天气预报信息。

最后在勾选了IE浏览器中“Internet选项”,“高级”中的“使用SSL2.0”以及“使用SSL3.0”,但是还是无法访问HTTPS。

在万般无奈中,我唯有借助第三方库了,OpenSSL。

可以参考:http://slproweb.com/products/Win32OpenSSL.html

由于我的项目是32位的,所以我是下载了Win32 OpenSSL v1.1.1d这个版本。

编码很简单,百度上有很多基于socket+OpenSSL原生模拟HTTPS使用post和get的例子,但是当我再一次把打包好的程序放到xp系统下运行时,报错“缺少VCRUNTIME1400.dll”,看了这个错误我知道应该是使用了OpenSSL的dll,xp系统下没有安装VC++2005等那些组件。

第一时间,我想到了可以完全使用静态库,不在使用dll,这样就可以避免了上面缺少dll的报错。但是我发现,当我使用libcrypto_static.lib,libssl_static.lib或者是libcrypto32MTd.lib,libssl32MTd.lib,都报了非常多的连接错误,不匹配VS2010的。

后来,我发现了一个可以基于VS2010编译的OpenSSL,https://www.npcglib.org/~stathis/blog/precompiled-openssl/ 下载了对应的VS2010版本后,这次编译之后连接错误就只剩下7个了。

报错如下:

1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertFreeCertificateContext@4
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertGetCertificateContextProperty@16
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertOpenStore@20
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertFindCertificateInStore@24
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertEnumCertificatesInStore@8
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertCloseStore@8
1>libcrypto.lib(e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertDuplicateCertificateContext@4

 

这个错误是因为openssl库使用了windows的一个密码学库: Crypt32.lib

添加完以上的静态库后就运行成功了,PS:Crypt32.lib 这个静态库不用下载,直接添加到“属性”-->“链接器”-->“输入”-->“附加依赖项”。或者在代码中添加 #pragma comment(lib,"Crypt32.lib")

 

#include <openssl/ssl.h>

	//初始化OpenSSL库
	//(虽然不知道为什么,但是不加这三行似乎并不会导致什么问题,在不加这3行的情况下测试了几个网站并没有发现任何问题喵)
	SSL_library_init();
	SSLeay_add_ssl_algorithms();
	SSL_load_error_strings();

	//创建SSL会话环境等
	pSslCtx = SSL_CTX_new(TLSv1_2_client_method());
	if (pSslCtx == NULL)
	{
		LXY_PRINTF(LEVEL_ERROR,"[ https get 服务:创建ssl ctx异常 ]::Create SSL_CTX_new error.\n");
		lxy_tcp_close(socketfd);
		return -1;
	}
	psslSSL = SSL_new(pSslCtx);
	if (psslSSL == NULL)
	{
		LXY_PRINTF(LEVEL_ERROR,"[ https get 服务:创建ssl异常 ]::Create SSL_new error.\n");
		lxy_tcp_close(socketfd);
		return -1;
	}
	SSL_set_fd(psslSSL, socketfd);
	nErrorConnect = SSL_connect(psslSSL);
	if (nErrorConnect < 0)
	{
		LXY_PRINTF(LEVEL_ERROR,"[ https get 服务:ssl 连接异常 ]::To using SSL_connect error.\n");
		lxy_tcp_close(socketfd);
		return -1;
	}
	/********** 开始发送ssl加密包 **********/
	memset(data,'\0',BUFSIZE);
	sprintf(data,HTTPS_GET,file,s_Ip,n_port);
	nErrorWrite = SSL_write(psslSSL,data,strlen(data)) < 0;
	if (nErrorWrite < 0)
	{
		LXY_PRINTF(LEVEL_ERROR,"[ https get 服务:发送ssl加密包异常 ]::To using SSL_write send data error.\n");
		lxy_tcp_close(socketfd);
		return -1;
	}

	//收包并输出
	//这里接受的是char形式的,所以中文会乱码
	//如果要正常显示中文,需要再转换为wchar_t或std::wstring

	memset(data,'\0',BUFSIZE);
	len = lxy_https_recv_response_head(psslSSL,data,BUFSIZE);
	if (len <0 )
	{
		lxy_tcp_close(socketfd);
		return -1;
	}
	lxy_https_parse_response_head(data,&nStatus,&nTotalDownLoad);
	if ( nStatus == 200 )
	{
		memset(data,'\0',BUFSIZE);
		while ( len = SSL_read(psslSSL,data,BUFSIZE - 1) )
		{
			LXY_PRINTF(LEVEL_WARN,"[ https get 服务:接收数据 ]::SSL_read%s\n",data);
			nTotalRead += len;
			if ( nTotalRead < BUFSIZE )
			{
				strcat(outBuf,data);
			}
			memset(data,'\0',BUFSIZE);
		}
	}

以上的代码都是在一位网友的博客找到的,我想回来找那个博客的时候发现找不到了,感谢这位网友,如果有侵权的请联系我QQ706414069。

总结:

1、对HTTPS的协议不熟悉,以为和HTTP一样,直接通过socket连接就可以post。

2、编程思维有点头痛医头,脚痛医脚的感觉,不会站在全局上去思考问题。

3、写这个博客是因为记忆力越来越差了,人到中年了嘛,所以还是动动笔记一下比较好,忘记了就回来看看。

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liquanuo87

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值