c++headersignaturehmacwininet

Why C++ sending request with WinInet.h library always returned "HMAC signature does not match"?


I used to write Python codes to send requests and now trying to switch to C++. I’m new to sending HTTPS request with WinInet.h library in C++ code, and seeking assistance regarding the usage of the WinInet.h library for handling HTTPS requests with headers of X-Ca-Signature.

The “headers” settings are the same for both Python and C++ code. Python code could run perfectly, but C++ could not. The compiled C++ code would always returned: "HMAC signature does not match", which seems that C++ with “wininet.h” library didn’t handle them correctly.

The original Python code is: (note: all the values of 'X-Ca-Key', 'X-Ca-Signature', 'X-Ca-Nonce', 'X-Access-Token' have been calculated correctly, and are omitted in the following codes)

import requests

url = "https://......"

headers = {
  'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
  'Content-Type': 'application/json;charset=UTF-8',
  'X-Ca-Signature-Headers': 'x-ca-key,x-ca-nonce',
  'X-Ca-Key': '......',
  'X-Ca-Signature': '......'
  'X-Ca-Nonce': '......',
  'X-Access-Token': '......',
}

response = requests.get(url, headers=headers)

print(response.text)

And the equivalent C++ code with “wininet” library is:

#include <iostream>
#include <string>
#include <wininet.h>
using namespace std;
#pragma comment(lib, "wininet.lib")

void send_request(const string& url, const string& headers, string* htmltext) {
    HINTERNET Session = InternetOpenA("Mozilla/5.0 (Windows NT 10.0; Win64; x64)", INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
    if (!Session) {
        *htmltext = "InternetOpenA Error";
        return;
    }

    HINTERNET Connect = InternetOpenUrlA(Session, url.c_str(), headers.c_str(), headers.length(), INTERNET_FLAG_RELOAD, 0);
    if (!Connect) {
        *htmltext = "InternetOpenUrlA Error";
        InternetCloseHandle(Session);
        return;
    }

    DWORD BytesRead;
    string response;
    char buffer[4096];
    while (InternetReadFile(Connect, buffer, sizeof(buffer), &BytesRead) && BytesRead > 0) {
        response.append(buffer, BytesRead);
    }

    *htmltext = response;

    InternetCloseHandle(Connect);
    InternetCloseHandle(Session);
}

int main() {
    string url = "https://......";
    string headers = "User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"
                     "Content-Type: application/json;charset=UTF-8\r\n"
                     "X-Ca-Signature-Headers: x-ca-key,x-ca-nonce\r\n"
                     "X-Ca-Key: ......\r\n"
                     "X-Ca-Signature: ......\r\n"
                     "X-Ca-Nonce: ......\r\n"
                     "X-Access-Token: ......\r\n\r\n";
    string htmltext;
    send_request(url, headers, &htmltext);
    cout << htmltext << endl;
    return 0;
}

I have been struggling to try various approaches provided by ChatGPT, and spent considerable time debugging. Unfortunately, I haven't been able to find a solution.

I tried adding the following statements to retrieve the headers information:

    DWORD header_buffer_size = 4096;
    char headers_buffer[header_buffer_size];
    if (HttpQueryInfo(Connect, HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, headers_buffer, &header_buffer_size, NULL)) {
        cout << "Headers after sending request:\n" << headers_buffer << endl;
    } else {
        cout << "Failed retrieval of headers" << endl;
    }

The output result showed:

Headers after sending request:
GET /...... HTTP/1.1
User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Content-Type: application/json;charset=UTF-8
X-Ca-Nonce: ......
X-Access-Token: ......
X-Ca-Key: ......
X-Ca-Signature-Headers: x-ca-key,x-ca-nonce
X-Ca-Signature: ......
Host: ......
Cache-Control: no-cache
Cookie: ......

I compared that all the contents of the above "......" are exactly the same as those in the original code.

Why not using CURL library?

As a matter of fact, I have already tested another C++ code with CURL library. CURL is able to send GET and POST requests perfectly with the same headers as those in Python code.

The disadvantage of CURL is that the compiled executable file runs dependent with “libcurl-x64.dll” which may not be exist in every user’s Windows system.

In comparison, the Windows has built-in “wininet.dll” dynamic library, so that I can take full advantage of the “WinInet”.

I suspect there might be some limitations or specific requirements when using wininet.h library for requests with X-Ca-Signature headers, but I couldn't find any relevant information or examples in the documentation.

If anyone has experience with wininet.h or has encountered a similar issue with X-Ca-Signature headers in HTTPS requests, I would greatly appreciate your insights, suggestions, or any examples that could help me overcome this challenge.


Solution

  • thank you for your replies!

    By debugging the headers that the client sent to the server, I realized that the WinInet.h library did not automatically add the necessary header components such as "accept", "accept-encoding", "accept-language" by default, which behaves differently from the that of Python's requests functions.

    I had to manually add the headers with the following statements to the "headers" string:

    "Accept: */*\r\n"
    "Accept-Encoding: gzip, deflate, br\r\n"
    "Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7\r\n"
    

    Finally my C++ code with "wininet.h" library is able to send both GET and POST requests successfully! I'm quite satisfied that the compiled executable file is so tiny.

    That said, in case of Python's requests function, no worries about the default header settings, but when it comes to C++, we have to start from scratch.

    Thanks again for your ideas. I think this topic can be closed.