【问题标题】:how to Ignore certificate in HttpPOST request in WINAPI如何在 WINAPI 中忽略 HttpPOST 请求中的证书
【发布时间】:2016-12-28 07:05:25
【问题描述】:

我有一个适用于 80 和 443 端口的 VC++ HttpPOST 方法。 (在 google.com 等热门网站上)

现在,当我连接到具有 cgi 脚本的安全主机 (172.17.9.93) 时,现在当我使用 fiddler 连接时,我会收到证书无效的警告,然后打开接受警告我可以连接。

我必须在 C++ 中执行相同的行为,即使用以下标志 SECURITY_FLAG_IGNORE_UNKNOWN_CAINTERNET_FLAG_IGNORE_CERT_CN_INVALID 和函数中的一些组合忽略证书 HttpOpenRequest() 但它失败并给出以下输出。

C++ 控制台输出

 172.17.9.93 : 443  data base-bin/hello.cgi

Error 12045 has occurred while HttpSendRequest
INVALID CA request received (12045)

源代码 VC++

#define _WIN32_WINNT 0x600
#include <windows.h>
#include <stdio.h>
#include <string>
#include <wininet.h>
#pragma comment(lib,"Wininet.lib")
using namespace std;

int doPost(std::string send, std::string &receive, LPCTSTR host, int port, LPCTSTR url)
{

    char szData[1024];
    int winret = 0;
    TCHAR szAccept[] = L"*/*";
    LPWSTR AcceptTypes[2] = { 0 };
    AcceptTypes[0] = szAccept;

    HINTERNET hInternet = ::InternetOpen(TEXT("mandar"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (hInternet != NULL)
    {
        printf("\n %S : %d  data %S \n", host, port, url);

        // open HTTP session
        HINTERNET hConnect;
        if (port == 80)
        {
            hConnect = ::InternetConnect(hInternet, host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 0);
        }
        else
        {
            hConnect = ::InternetConnect(hInternet, host, port, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 0);
        }
        if (hConnect != NULL)
        {
            DWORD dwFlags = dwFlags |= INTERNET_FLAG_SECURE |
                SECURITY_FLAG_IGNORE_UNKNOWN_CA | INTERNET_FLAG_IGNORE_CERT_CN_INVALID|
                INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
            /*SECURITY_FLAG_IGNORE_UNKNOWN_CA |
            INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
            INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
            | SECURITY_FLAG_IGNORE_CERT_CN_INVALID
            | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;*/


            // open request
            HINTERNET hRequest;

            if (port == 80)
            {
                hRequest = ::HttpOpenRequest(hConnect, TEXT("POST"), url, HTTP_VERSION, NULL, 0, NULL, 0);
            }
            else
            {
                hRequest = ::HttpOpenRequest(hConnect, TEXT("POST"), url, HTTP_VERSION, NULL, 0, dwFlags, 0);
            }

            if (hRequest != NULL)
            {
                if (::HttpSendRequest(hRequest, NULL, 0, (LPVOID)send.c_str(), send.length()))
                {
                    for (;;)
                    {
                        // reading data
                        DWORD dwByteRead;
                        BOOL isRead = ::InternetReadFile(hRequest, szData, sizeof(szData) - 1, &dwByteRead);

                        // break cycle if error or end
                        if (isRead == FALSE || dwByteRead == 0)
                            break;

                        // saving result
                        szData[dwByteRead] = 0;
                        receive.append(szData);
                    }
                    printf(" receive data [%s]", receive.c_str());
                }
                else{
                    winret = GetLastError();
                    printf("\nError %d has occurred while HttpSendRequest", winret);
                    switch (winret)
                    {
                    case ERROR_INTERNET_INVALID_CA:
                        printf("\nINVALID CA request received (%d)\n", winret);
                        break;
                    case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
                        printf("\nResubmitting for CLIENT_AUTH_CERT_NEEDED (%d)\n", winret);
                        break;
                    default:
                        printf("\nError %d has occurred while HttpSendRequest", winret);
                    }
                }

                // close request
                ::InternetCloseHandle(hRequest);
            }
            else{
                winret = GetLastError();
                printf("\nError %d has occurred while HttpOpenRequest", winret);
            }
            // close session
            ::InternetCloseHandle(hConnect);
        }
        else{
            winret = GetLastError();
            printf("\nError %d has occurred while InternetConnect", winret);
        }
        // close WinInet
        ::InternetCloseHandle(hInternet);
    }
    else
    {
        winret = GetLastError();
        printf("\nError %d has occurred while InternetOpen", winret);
    }


    return winret;
}
int main()
{
    std::string send;
    std::string receive;
    LPCTSTR host =  L"172.17.9.93";
    int port = 443;// 80;
    LPCTSTR url = L"base-bin/hello.cgi";
    doPost("<XMLhello>1</XMLhello>",receive,host,port,url);
}

【问题讨论】:

  • 你写的DWORD dwFlags = dwFlags |=... . In this way you use an uninitialized var on the right side. When you declare a var, it is immediatly visible. I think that dwFlags`包含垃圾。

标签: c++ winapi ssl visual-c++


【解决方案1】:

HttpOpenRequest 仅适用于 INTERNET_FLAG_* 形式的标志 - 将标志 SECURITY_FLAG_IGNORE_UNKNOWN_CA 传递给 HttpOpenRequest 这是错误。

查找数值:

#define SECURITY_FLAG_IGNORE_UNKNOWN_CA         0x00000100

#define INTERNET_FLAG_PRAGMA_NOCACHE    0x00000100  // asking wininet to add "pragma: no-cache"

因此,如果您尝试将 SECURITY_FLAG_IGNORE_UNKNOWN_CA 传递给 HttpOpenRequest,那么您确实传递了 INTERNET_FLAG_PRAGMA_NOCACHE 标志

SECURITY_FLAG_IGNORE_UNKNOWN_CA 设计用于InternetSetOption 函数和INTERNET_OPTION_SECURITY_FLAGS

所以你需要这个代码使用:

DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);

if (InternetQueryOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBuffLen))
{
    dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
    InternetSetOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags));
}

另请阅读How To Handle Invalid Certificate Authority Error with WinInet - 方法 2。没有 UI: 正是你的情况

【讨论】:

  • 感谢它与 InternetsetOptions 一起使用,但需要 10 秒才能回复电话::HttpSendRequest(hRequest, NULL, 0, (LPVOID)send.c_str(), send.length())
  • @Mozfox - 但这也已经依赖于站点。有些网站可以和更多的时间打开。有些可以挂
  • 我用 curl 检查过它,它是瞬时的。我认为代码在 dll 中,所以它会影响它的工作?
  • @Mozfox - 在验证/检查证书上考虑延迟。并且通常只有在 Windows 看到未知证书时才第一次出现。所以问题不在您的代码中,而是在 api 实现中。
  • 所提到的 Microsoft 主页链接不再可用。我发现这篇文章很有帮助:m.blog.naver.com/shin450279/220450528121
【解决方案2】:

第一个答案非常准确,但我遇到了关于 WinInet-API 的另一个特殊性:

  1. 首先,您必须发送“正常”请求,该请求将因 SSL 错误而失败。
  2. 只有这样您才能设置安全标志...
  3. ...接着是第二个请求,它将成功。

例子:

// First Request...
if (!HttpSendRequest(hRequest, ...) && GetLastError() == ERROR_INTERNET_INVALID_CA)
{
   DWORD dwFlags = 0;
   DWORD dwBuffLen = sizeof(dwFlags);
   if (InternetQueryOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBuffLen))
   {
      dwFlags |= SECURITY_SET_MASK;
      InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags));
   }
   
   if(HttpSendRequest(hRequest, ...))
      /* ssl errors were ignores successfully */
   else
      /* still ssl error (should not happen) */
}

【讨论】:

    猜你喜欢
    • 2019-07-21
    • 2011-08-26
    • 2011-02-11
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    • 2016-05-11
    • 1970-01-01
    • 2016-01-28
    相关资源
    最近更新 更多