WinInet:HTTPS 请求出现无效的证书颁发机构的处理
来源:互联网 发布:赛扬n3150能否优化 编辑:程序博客网 时间:2024/05/22 06:12
首先,微软提供的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"; //设置server INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; // HTTPS端口443 LPCTSTR lpszUserName = NULL; //无登录用户名 LPCTSTR lpszPassword = NULL; //无登录密码 DWORD dwConnectFlags = 0; DWORD dwConnectContext = 0; //连接 HINTERNET hConnect = InternetConnect(hInternet, lpszServerName, nServerPort, lpszUserName, lpszPassword, INTERNET_SERVICE_HTTP, dwConnectFlags, dwConnectContext); //使用Get LPCTSTR 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 | //设置启用HTTPS INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD; DWORD dwOpenRequestContext = 0; //初始化Request HINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferrer, lplpszAcceptTypes, dwOpenRequestFlags, dwOpenRequestContext); //发送RequestHttpSendRequest(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"; //设置server INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; // HTTPS端口443 LPCTSTR lpszUserName = NULL; //无登录用户名 LPCTSTR lpszPassword = NULL; //无登录密码 DWORD dwConnectFlags = 0; DWORD dwConnectContext = 0; //连接 HINTERNET hConnect = InternetConnect(hInternet, lpszServerName, nServerPort, lpszUserName, lpszPassword, INTERNET_SERVICE_HTTP, dwConnectFlags, dwConnectContext); //使用Get LPCTSTR 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 | //设置启用HTTPS INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD; DWORD dwOpenRequestContext = 0; //初始化Request HINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferrer, lplpszAcceptTypes, dwOpenRequestFlags, dwOpenRequestContext); //发送Requestagain: 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 | //设置启用HTTPS INTERNET_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::SendRequestCInternetFile::QueryOptionCInternetFile::SetOptionCInternetFile::ErrorDlg请注意缺少 Visual C++ 5.0 CInternetFile::ErrorDlg,CInternetFile::QueryOption 和 CInternetFile::SetOption 上的文档。请参阅 Inet.cpp MFC 源代码文件的信息如何使用此方法。注 1: InternetErrorDlg 可能会返回下列值: ERROR_SUCCESS ERROR_CANCELLED ERROR_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),然后重新提交。
我用的是提供的第二个方法无用户界面的解决方法。然后这样大家应该就会明白我那里的处理逻辑为啥会那个样子了。
好了,问题就是这个样子了,我的问题解决了,你的呢?
- WinInet:HTTPS 请求出现无效的证书颁发机构的处理
- 无效的 URI: 未能分析证书颁发机构/主机
- iOS 遇到证书签名无效,且出现此证书是“由未知颁发机构签名的解决办法”
- iOS 遇到证书签名无效,且出现此证书是由未知颁发机构签名的解决办法
- Android的根证书颁发机构列表
- StartSSL免费的HTTPS证书颁发机构及nginx 的ssl配置
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL
- 备份和还原证书颁发机构的密钥和数据库
- 备份和还原证书颁发机构的密钥和数据库
- 此证书是由未知颁发机构签名的解决办法
- 颁发机构无效
- HTTPS证书颁发机构Startssl SSL申请图文详解
- 关于证书布置好了出现“无法将这个证书验证到一个受信任的证书颁发机构”及页面内容混搭问题
- [绍棠] ERROR ITMS-90206:"Invalid Bundle. The bundle at 'XXX.appex' contains disallowed file 'Framework
- 1 冒泡排序
- 伪类模仿 li 前面的圆点及各种符号
- java常用术语
- Spring源码解析-applicationContext
- WinInet:HTTPS 请求出现无效的证书颁发机构的处理
- [LeetCode] 49. Group Anagrams java
- WEB Service配置
- datepicker 设置默认日期
- AngularJS开发前端表格组件
- 算法笔记_061-蓝桥杯练习 字串统计(Java)
- 登录界面CheckBox记住密码
- IntelliJ IDEA 注册码
- js的算术运算