首先,微软提供的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, );
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 = ;
DWORD dwConnectContext = ;
//连接
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 = ;
//初始化Request
HINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion,
lpszReferrer, lplpszAcceptTypes,
dwOpenRequestFlags, dwOpenRequestContext);
//发送Request
HttpSendRequest(hRequest, NULL, , NULL, );
//获得HTTP Response Header信息
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = ;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + );
while(!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL)) {
DWORD dwError = GetLastError();
if(dwError == ERROR_INSUFFICIENT_BUFFER) {
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + );
} else {
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)/n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '/0';
pInfoBuffer[dwInfoBufferLength + ] = '/0';
printf("%S", pInfoBuffer); //很奇怪HttpQueryInfo保存的格式是wchar_t 和下面的InternetReadFile不一样
free(pInfoBuffer);
//HTTP Response 的 Body, 需要的内容就在里面
DWORD dwBytesAvailable;
while(InternetQueryDataAvailable(hRequest, &dwBytesAvailable, , )) {
BYTE *pMessageBody = (BYTE *)malloc(dwBytesAvailable + );
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 == )
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, );
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 = ;
DWORD dwConnectContext = ;
//连接
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 = ;
//初始化Request
HINTERNET hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion,
lpszReferrer, lplpszAcceptTypes,
dwOpenRequestFlags, dwOpenRequestContext);
//发送Request
again:
DWORD dwError = ;
if (!HttpSendRequest(hRequest, NULL, , NULL, ))
{
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 = ;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + );
while(!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL)) {
DWORD dwError = GetLastError();
if(dwError == ERROR_INSUFFICIENT_BUFFER) {
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + );
} else {
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)/n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '/0';
pInfoBuffer[dwInfoBufferLength + ] = '/0';
printf("%S", pInfoBuffer); //很奇怪HttpQueryInfo保存的格式是wchar_t 和下面的InternetReadFile不一样
free(pInfoBuffer);
//HTTP Response 的 Body, 需要的内容就在里面
DWORD dwBytesAvailable;
while(InternetQueryDataAvailable(hRequest, &dwBytesAvailable, , )) {
BYTE *pMessageBody = (BYTE *)malloc(dwBytesAvailable + );
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 == )
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 应用程序中的错误,而不安装证书。有两种方法来处理该错误。您可以使用类似于以下示例的代码。

方法 。与用户界面 (生成类似于 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;
}
... 方法 。而无需用户界面:
...
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 源代码文件的信息如何使用此方法。 注 : InternetErrorDlg 可能会返回下列值:
ERROR_SUCCESS
ERROR_CANCELLED
ERROR_INTERNET_FORCE_RETRY. 仅当返回 ERROR_INTERNET_FORCE_RETRY 时,才应重新提交请求。在 Internet Explorer 4.0 和 4.01 中,但是,该请求必须重新提交即使 ERROR_SUCCESS 将返回。 Microsoft 已经确认这是 InternetErrorDlg API 中的问题。注 : SECURITY_FLAG_IGNORE_UNKNOWN_CA 在 Internet Explorer 3.0 和 3.02 未实现。 InternetErrorDlg 仍然起作用,但有以下例外。此 api 生成对话框中不允许忽略无效的证书颁发机构的错误 ;它是只是通知页该用户不能查看。 注 : 在错误发生之前,不能设置选项,将忽略此错误。您首先必须尝试发送请求、 收到错误消息,然后设置选项 (或调用 InternetErrorDlg),然后重新提交。

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

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

WinInet:HTTPS 请求出现无效的证书颁发机构的处理的更多相关文章

  1. 无效的 URI: 未能分析证书颁发机构/主机

    出 现该错误的原因是URL中少了一个斜杠,正常的URL是“http:”后边有两个斜杠,而我在修改配置文件中的URL的IP地址部分时,不小心删掉了一个 斜杠,例如:http:/blog.csdn.net ...

  2. 无效 URI: 故障分析证书颁发机构/主机

    无效 URI: 分析证书颁发机构/主机 出现该错误的原因是URL中少了一个斜杠.正常的URL是"http:"后边有两个斜杠,而我在改动配置文件里的URL的IP地址部分时.不小心删掉 ...

  3. 全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL

    全球可信并且唯一免费的HTTPS(SSL)证书颁发机构:StartSSL http://blog.s135.com/startssl/ 购买权威机构的证书一年大概得七八千元,其实这是不值得的,所以一直 ...

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

    本文转载自SSL/TLS协议详解(中)--证书颁发机构 导语 上一篇中,我们讨论了关于Diffie Hellman算法的SSL/TLS密钥交换.我们最终认为需要第三方来验证服务器的真实性,并提出了证书 ...

  5. CA机构介绍(Certificate Authority 域名SSL证书颁发机构)

    SSL证书机构即CA机构的全称为Certificate Authority证书认证中心,只有通过WebTrust国际安全审计认证,根证书才能预装到主流浏览器,成为全球可信的ssl证书颁发机构. HTT ...

  6. 如何在Windows中查询证书颁发机构已颁发的证书

    有时候需要看一下证书颁发机构已经颁发出去的证书,看看某个用户或者某个计算机获取过的证书有哪些.通常可以在证书颁发机构的MMC中查看.对于测试环境或者刚开始用的CA来说,这样查看挺简单的.但是对于用了一 ...

  7. 安装VS2012 update3提示缺少Microsoft根证书颁发机构2010或2011的解决方法

    警告提示如图: (copy的百度贴吧的童鞋的截图) 解决方法: 下载2010.10或2011.10的根证书即可 直通车:http://maxsky.ys168.com/ ——05.||浮云文件||—— ...

  8. 你正在从一个声称代表如下的证书颁发机构安装证书 alipay truest network,希望能知道程序是怎么实现的或相关资料

    你正在从一个声称代表如下的证书颁发机构安装证书 alipay truest network,希望能知道程序是怎么实现的或相关资料

  9. windows服务器怎么将证书添加到受信任证书颁发机构

    1.键盘输入win+r 快键键,出现运行,输入mmc. 2.打开控制台根节点,点击上方导航栏的文件-->添加删除管理单元.如下图. 3.在可用的管理单元中选择"证书",计算机 ...

随机推荐

  1. 管理员 修改MySQL 5.7.9 新版本的root密码方法以及一些新变化整理

    MySQL 5.7版本开始,增强密码验证机制,网上说安装的时候会在/root/.mysql_secret  文件中生成默认密码,这一点自 5.7.6版本以后也去掉了. 针对如果生成默认密码,网上有一个 ...

  2. C# 程序开始主要是写类和方法 的基本步骤和调用方法

    主程序的使用方式以及调用方法字段.属性.方法 using System; using System.Collections.Generic; using System.Linq; using Syst ...

  3. 对于java反射的理解

    java中的反射是一种强大的工具,它能够创建灵活的代码,这些代码可以在运行时装配,无序在组件之间进行链接. 反射允许在编写与执行时,使程序代码能够接入装载到JVM的类的内部信息,而不是源代码中选定的类 ...

  4. 小米miui5系统的webview在处理动画事件transitionEnd事件时,竟然要用transitionend才行

    一般的安卓系统用的是webkitTransitionEnd, 而小米的系统我用了webkitTransitionEnd事件无法执行,只能用transitionend才会被执行,怪

  5. Swing基础

    Swing基础 JFrame JPanel 绘图:paint 监听事件: ActionListener  KeyListener Listener和Adapter 计时器:Timer     Time ...

  6. NOJ1066-堆排序

    堆排序 时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte总提交 : 414            测试通过 : 220  比 ...

  7. SAP CRM 项目笔记(一) SOW(工作说明书)讨论

    前记 前两天在搜索资料时,看到一个网友在博客里面记录下了自己参于项目中的所有笔记.我觉得这个想法很不错,所以决定开笔记录下SAP CRM整个项目的实施和开发过程. 之前参加集团的SAP ERP(FI/ ...

  8. 【js类库AngularJs】web前端的mvc框架angularjs之hello world

    AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核 ...

  9. Linux开机启动程序详解

    Linux开机启动程序详解我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Linux开机引导的步骤. ...

  10. Swift Explore - 关于 Swift 中的 isEqual 的一点探索

    在我们进行 App 开发的时候,经常会用到的一个操作就是判断两个对象是否相等.比如两个字符串是否相等.而所谓的 相等 有着两层含义.一个是值相等,还有一个是引用相等.如果熟悉 Objective-C ...