首先,微软提供的WinInet库封装了对网页访问的方法。

最近工作需要从https服务器获取数据,都知道https和http网页的访问方式不同,多了一道证书认证程序,这样就使得https在请求起来比http要复杂的多;好在,WinInet库中提供了对https网页请求的处理,这样就不需要在使用openssl中的一些方法来复杂化程序了。

下面贴上我的解决前的代码,再对比我遇到问题之后的代码,在通过实际遇到的问题和环境来阐述:

  解决前代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include "Windows.h"
#include "wininet.h"
using namespace std;
//链接需要 wininet.lib
#pragma comment(lib,"wininet.lib")
int main(int argc, char* argv[])
{LPCTSTR lpszAgent = "WinInetGet/0.1";//初始化HINTERNET hInternet = InternetOpen(lpszAgent,INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);LPCTSTR lpszServerName = "data.btcchina.com";//"ssl.google-analytics.com"; //设置serverINTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; // HTTPS端口443LPCTSTR lpszUserName = NULL; //无登录用户名LPCTSTR lpszPassword = NULL; //无登录密码DWORD dwConnectFlags = 0;DWORD dwConnectContext = 0;//连接HINTERNET hConnect = InternetConnect(hInternet,lpszServerName, nServerPort,lpszUserName, lpszPassword,INTERNET_SERVICE_HTTP,dwConnectFlags, dwConnectContext);//使用GetLPCTSTR lpszVerb = "GET";LPCTSTR lpszObjectName = "/data/ticker";LPCTSTR lpszVersion = NULL;    // 默认.LPCTSTR lpszReferrer = NULL;   // 没有引用页LPCTSTR *lplpszAcceptTypes = NULL; // Accpet所有类型.DWORD dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |INTERNET_FLAG_KEEP_CONNECTION |INTERNET_FLAG_NO_AUTH |INTERNET_FLAG_NO_COOKIES |INTERNET_FLAG_NO_UI |//设置启用HTTPSINTERNET_FLAG_SECURE |INTERNET_FLAG_RELOAD;DWORD dwOpenRequestContext = 0;//初始化RequestHINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion,lpszReferrer, lplpszAcceptTypes,dwOpenRequestFlags, dwOpenRequestContext);//发送Request
HttpSendRequest(hRequest, NULL, 0, NULL, 0); //获得HTTP Response Header信息DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;DWORD dwInfoBufferLength = 2048;BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + 2);while(!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL)) {DWORD dwError = GetLastError();if(dwError == ERROR_INSUFFICIENT_BUFFER) {free(pInfoBuffer);pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + 2);} else {fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)/n",GetLastError(), GetLastError());break;}}pInfoBuffer[dwInfoBufferLength] = '/0';pInfoBuffer[dwInfoBufferLength + 1] = '/0';printf("%S", pInfoBuffer); //很奇怪HttpQueryInfo保存的格式是wchar_t 和下面的InternetReadFile不一样
    free(pInfoBuffer);//HTTP Response 的 Body, 需要的内容就在里面
    DWORD dwBytesAvailable;while(InternetQueryDataAvailable(hRequest, &dwBytesAvailable, 0, 0)) {BYTE *pMessageBody = (BYTE *)malloc(dwBytesAvailable + 1);DWORD dwBytesRead;BOOL bResult = InternetReadFile(hRequest, pMessageBody,dwBytesAvailable, &dwBytesRead);if(!bResult) {fprintf(stderr, "InternetReadFile failed, error = %d (0x%x)/n",GetLastError(), GetLastError());break;}if(dwBytesRead == 0)break; // End of File.pMessageBody[dwBytesRead] = '/0';printf("%s", pMessageBody); //InternetReadFile读出来的是普通的char. InternetReadFileEx 似乎是有宽字节版本的
ofstream out("ofs.txt");std::string s = (char *)pMessageBody;out << s.c_str()<< endl;free(pMessageBody);}getchar();
}

  解决后代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include "Windows.h"
#include "wininet.h"using namespace std;
//链接需要 wininet.lib
#pragma comment(lib,"wininet.lib")int main(int argc, char* argv[])
{LPCTSTR lpszAgent = "WinInetGet/0.1";//初始化HINTERNET hInternet = InternetOpen(lpszAgent,INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);LPCTSTR lpszServerName = "data.btcchina.com";//"ssl.google-analytics.com"; //设置serverINTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; // HTTPS端口443LPCTSTR lpszUserName = NULL; //无登录用户名LPCTSTR lpszPassword = NULL; //无登录密码DWORD dwConnectFlags = 0;DWORD dwConnectContext = 0;//连接HINTERNET hConnect = InternetConnect(hInternet,lpszServerName, nServerPort,lpszUserName, lpszPassword,INTERNET_SERVICE_HTTP,dwConnectFlags, dwConnectContext);//使用GetLPCTSTR lpszVerb = "GET";LPCTSTR lpszObjectName = "/data/ticker";LPCTSTR lpszVersion = NULL;    // 默认.LPCTSTR lpszReferrer = NULL;   // 没有引用页LPCTSTR *lplpszAcceptTypes = NULL; // Accpet所有类型.DWORD dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |INTERNET_FLAG_KEEP_CONNECTION |INTERNET_FLAG_NO_AUTH |INTERNET_FLAG_NO_COOKIES |INTERNET_FLAG_NO_UI |//设置启用HTTPSINTERNET_FLAG_SECURE |INTERNET_FLAG_RELOAD;DWORD dwOpenRequestContext = 0;//初始化RequestHINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion,lpszReferrer, lplpszAcceptTypes,dwOpenRequestFlags, dwOpenRequestContext);//发送Request
again:DWORD dwError = 0;if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)){dwError = GetLastError();}if (dwError == ERROR_INTERNET_INVALID_CA){fprintf(stderr, "HttpSendRequest failed, error = %d (0x%x)/n",dwError, dwError );DWORD dwFlags;DWORD dwBuffLen = sizeof(dwFlags);InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS,(LPVOID)&dwFlags, &dwBuffLen);dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;InternetSetOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS,&dwFlags, sizeof(dwFlags));goto again;}//获得HTTP Response Header信息DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;DWORD dwInfoBufferLength = 2048;BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + 2);while(!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL)) {DWORD dwError = GetLastError();if(dwError == ERROR_INSUFFICIENT_BUFFER) {free(pInfoBuffer);pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + 2);} else {fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)/n",GetLastError(), GetLastError());break;}}pInfoBuffer[dwInfoBufferLength] = '/0';pInfoBuffer[dwInfoBufferLength + 1] = '/0';printf("%S", pInfoBuffer); //很奇怪HttpQueryInfo保存的格式是wchar_t 和下面的InternetReadFile不一样
    free(pInfoBuffer);//HTTP Response 的 Body, 需要的内容就在里面
    DWORD dwBytesAvailable;while(InternetQueryDataAvailable(hRequest, &dwBytesAvailable, 0, 0)) {BYTE *pMessageBody = (BYTE *)malloc(dwBytesAvailable + 1);DWORD dwBytesRead;BOOL bResult = InternetReadFile(hRequest, pMessageBody,dwBytesAvailable, &dwBytesRead);if(!bResult) {fprintf(stderr, "InternetReadFile failed, error = %d (0x%x)/n",GetLastError(), GetLastError());break;}if(dwBytesRead == 0)break; // End of File.pMessageBody[dwBytesRead] = '/0';printf("%s", pMessageBody); //InternetReadFile读出来的是普通的char. InternetReadFileEx 似乎是有宽字节版本的
ofstream out("ofs.txt");std::string s = (char *)pMessageBody;out << s.c_str()<< endl;free(pMessageBody);}getchar();
}

大家看到HttpOpenRequest这个函数中,dwOpenRequestFlag参数:

    DWORD dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |INTERNET_FLAG_KEEP_CONNECTION |INTERNET_FLAG_NO_AUTH |INTERNET_FLAG_NO_COOKIES |INTERNET_FLAG_NO_UI |//设置启用HTTPSINTERNET_FLAG_SECURE |INTERNET_FLAG_RELOAD;

要request到https网页的数据,INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP和INTERNET_FLAG_SECURE两个选项要设置。

  从上面的前后两段代码,大家应该能看到逻辑的变化在哪里,就在于HttpSendRequest这个函数的返回值的处理上。

    看HttpSendRequest这层逻辑的处理,你会好奇为什么要用到这样的逻辑呢?答案其实并不是那么好告诉你的,因为,这样设置逻辑是因为微软当时在设计这个库的时候留下的一个漏洞。

  因为,https协议涉及到证书认证问题,而IE低版本内核的浏览器打开你要请求的https Url的时候,会出现证书认证失败,(比如我这里的:btc.china.com/data/ticker),而高级版本的浏览器可能就不会有任何问题。

  在解决问题前,我的环境是Win7系统,IE10浏览器,在我运行程序的时候一切正常,能正常获取到程序,浏览器也能打开网页看到网页上的数据,但是当我把程序发布release然后交给运维测试的时候,他那边环境是(win server 2003, IE7环境),这就出现了问题,他那边获取不到那个请求https网站的数据,于是我建议他们按照步骤通过浏览器端安装该网站的认证证书,安装之后浏览器可以看到数据,但是运行程序并不能正常获得数据,这就是我的问题所在。

  于是,就问Google大婶们,无果,所以只有解铃还须系铃人了,遂到微软的问题解决网站寻求帮助,结果,查出来这是微软设计的一个缺陷,但是他们给出了很好的解决办法,那就是忽略证书认证。

微软解决办法:http://support.microsoft.com/kb/182888/zh-cn

  考虑到有的时候,有些人会打不开微软的这个网站,我在这里把他复制粘贴出来,如下:

客户端不知道有关颁发服务器证书的证书颁发机构时,就会发生此错误。通过安装证书颁发机构的根证书,问题可能得到解决。可以从 Internet Explorer 查看所有已安装的证书列表。从视图菜单上,单击 Internet 选项,单击内容选项卡,单击机构。很可能绕过此 WinInet 应用程序中的错误,而不安装证书。有两种方法来处理该错误。您可以使用类似于以下示例的代码。方法 1。与用户界面 (生成类似于 Internet Explorer 的消息框):...Again:if (!HttpSendRequest (hReq,...))dwError = GetLastError ();if (dwError == ERROR_INTERNET_INVALID_CA){// Make sure to check return code from InternetErrorDlg// user may click either OK or Cancel. In case of Cancel// request should not be resumbitted.
       InternetErrorDlg (GetDesktopWindow(),hReq,ERROR_INTERNET_INVALID_CA,FLAGS_ERROR_UI_FILTER_FOR_ERRORS |FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,NULL);goto again;}...方法 2。而无需用户界面:...Again:if (!HttpSendRequest (hReq,...))dwError = GetLastError ();if (dwError == ERROR_INTERNET_INVALID_CA){DWORD dwFlags;DWORD dwBuffLen = sizeof(dwFlags);InternetQueryOption (hReq, INTERNET_OPTION_SECURITY_FLAGS,(LPVOID)&dwFlags, &dwBuffLen);dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS,&dwFlags, sizeof (dwFlags) );goto again;}...与 MFC WinInet 类可以使用相似的逻辑。在这种情况下,下列 MFC 方法对应于 WinInet 上面使用的 Api:CInternetFile::SendRequest
CInternetFile::QueryOption
CInternetFile::SetOption
CInternetFile::ErrorDlg
请注意缺少 Visual C++ 5.0 CInternetFile::ErrorDlg,CInternetFile::QueryOption 和 CInternetFile::SetOption 上的文档。请参阅 Inet.cpp MFC 源代码文件的信息如何使用此方法。注 1: InternetErrorDlg 可能会返回下列值:ERROR_SUCCESSERROR_CANCELLEDERROR_INTERNET_FORCE_RETRY.仅当返回 ERROR_INTERNET_FORCE_RETRY 时,才应重新提交请求。在 Internet Explorer 4.0 和 4.01 中,但是,该请求必须重新提交即使 ERROR_SUCCESS 将返回。Microsoft 已经确认这是 InternetErrorDlg API 中的问题。注 2: SECURITY_FLAG_IGNORE_UNKNOWN_CA 在 Internet Explorer 3.0 和 3.02 未实现。InternetErrorDlg 仍然起作用,但有以下例外。此 api 生成对话框中不允许忽略无效的证书颁发机构的错误 ;它是只是通知页该用户不能查看。注 3: 在错误发生之前,不能设置选项,将忽略此错误。您首先必须尝试发送请求、 收到错误消息,然后设置选项 (或调用 InternetErrorDlg),然后重新提交。

View Code

  我用的是提供的第二个方法无用户界面的解决方法。然后这样大家应该就会明白我那里的处理逻辑为啥会那个样子了。

  好了,问题就是这个样子了,我的问题解决了,你的呢?

转载于:https://www.cnblogs.com/foundwant/p/3453963.html

WinInet:HTTPS 请求出现无效的证书颁发机构的处理相关推荐

  1. SSL之CA证书颁发机构安装图文详解

    上一节我们说到,在验证公钥安全性时,是在CA机构颁发的包含用户的公钥及其身份信息的数字证书,数字证书由权威机构--CA签发.这个CA权威机构可以是自己的服务器也可以是国际公认的CA权威机构.下面我就来 ...

  2. 让自己的主机成为证书颁发机构

    让自己的主机成为证书颁发机构         在互联网的世界里证书就是我们在这个网络世界的"×××",通过这个"×××"我们来证明自己的真实性,判断对方的真 实 ...

  3. centos 配置证书_如何在CentOS 8上设置和配置证书颁发机构(CA)

    centos 配置证书 介绍 (Introduction) A Certificate Authority (CA) is an entity responsible for issuing digi ...

  4. 如何在Ubuntu 20.04上设置和配置证书颁发机构(CA)

    介绍 (Introduction) A Certificate Authority (CA) is an entity responsible for issuing digital certific ...

  5. SSL/TLS协议详解(中)——证书颁发机构

    SSL/TLS协议详解(中)--证书颁发机构 本文翻译自:https://www.wst.space/ssl-part-3-certificate-authority/ 上一篇中,我们讨论了关于Dif ...

  6. SSL/TLS协议详解(三)——证书颁发机构

    目录 证书颁发机构的需求 数字签名的定义 证书颁发机构的技术实现 如果攻击者篡改证书会怎样 信任链 数字签名的数学算法 浏览器如何实际验证给定服务器证书的有效性 TLS加密客户端-服务器通信并阻止中间 ...

  7. 自定义根证书颁发机构 CA 生成自签名证书

    本文为使用过程中的一个工具记录,可实现在本地开启一个 HTTPS 服务器用于开发或测试. 前面有写过使用 Node.js 搭建 HTTPS 服务器 其中的自签名生成证书方式比较简单,既充当 HTTPS ...

  8. SSL应用系列之三:CA证书颁发机构(中心)安装图文详解

    tag: windows server 2003 ca ssl 原文传送门     其实,本节讨论的内容应该归属于CA系列的,但其中涉及到的内容和SSL应用系列之二比较紧密,在之二一文中未能详细描述, ...

  9. 2.域控制器及证书颁发机构

    安装域控制器部分:1.修改计算机名称IP地址 2.安装AD DNS 角色  运行服务器向导  3.配置AD域 (选定域名,推荐公网内网一致)  安装配置证书颁发机构部分:1.运行服务器向导,安装以下证 ...

最新文章

  1. Java反射机制分析指南
  2. redis 实现分布式锁
  3. 《深度探索C++对象模型》--2 构造函数语意学
  4. linux下各种软件安装方法详解
  5. python中的数据类型有哪些是可阅读,Python中典型的数据类型中哪个只能阅读不能修改...
  6. codevs 2924 数独挑战
  7. BZOJ 2442: [Usaco2011 Open]修剪草坪 单调队列
  8. 培训是一种乐趣(3)
  9. Struts2中使用OGNL表达式投影(过滤)集合
  10. python实现批量图片/文件重命名
  11. java tls 实例_grpc加密TLS初体验(go、java版本)
  12. C# httpcookie asp.net中cookie的使用
  13. java antd实现登录,AntDesign(React)学习-4 登录页面提交数据简单实现
  14. java中的关键事件是_java – 自定义关键事件
  15. 博客园编辑器导致火狐崩溃?
  16. Gulp简明使用教程
  17. 分布式事务管理之分布式事务框架TX-LCN
  18. python地理位置聚类_python实现地理位置的聚类
  19. java判断闰年中闰月_闰年闰月查询表_闰月查询表_闰年查询表-万年历
  20. 安装 AD LTspice电路仿真软件

热门文章

  1. HTML文件上传对象file
  2. LeetCode 2011. 执行操作后的变量值
  3. LeetCode 1551. 使数组中所有元素相等的最小操作数(等差数列)
  4. LeetCode 348. 判定井字棋胜负(计数)
  5. [scikit-learn 机器学习] 8. 非线性分类和决策树
  6. 程序员面试金典 - 面试题 08.03. 魔术索引(二分递归)
  7. LeetCode 816. 模糊坐标
  8. afreecatv 回放下载_行车记录仪怎么看回放?行车记录仪停车后能自动录像吗
  9. oracle事务重要属性,Oracle中的事务(2)--属性和隔离级别
  10. python数据抓取技术与实战训练_师傅带徒弟学Python:项目实战1:网络爬虫与抓取股票数据...