Skip to content

WinHttp with SSL enabled is not binding valid certificates to https requests #2593

Open
@anonKangaroo

Description

@anonKangaroo

Describe the bug

On Windows 10 the S3Crt client will not send requests when SSL is enabled. The issue lies where the system is not processing certificates specifically in this case self signed ones in the local windows certification store. In effect I am struggling to load a custom cert similar to this github issue #1863.

Expected Behavior

I expected to be able to configure the tls configuration options of the S3Crt client and be able to send a request to the endpoint via https and my own self signed certification. The reproduction step more or less demonstrates what I am trying to do there might be more issues with putObject and others but right now this simple example does not work.

Current Behavior

For the first few warns they time out from my testing this is from the initialization of Aws::S3Crt::ClientConfiguration object which pings for EC2 metadata or something, this is not the subject of the bug. Search for WinHttpSyncHttpClient [47408] Send request failed: A security error occurred. This is from the request being sent. When I looked into the lower level calls this is from 'WinHttp' returned 'ERROR_WINHTTP_SECURE_FAILURE' error, which is about how the certificate provided is not good for SSL communication.

aws_sdk_2023-07-24-19.log

However, you can look in the logs and see earlier on we have the lines

[DEBUG] 2023-07-24 19:32:35.152 tls-handler [47408] static: assuming certificate is in a system store, loading now.
[INFO] 2023-07-24 19:32:35.152 pki-utils [47408] static: loading certificate at windows cert manager path 'LocalMachine\MY\7F680C430C91C55AE8CCCCE63D1043A4E61919E4'.
[DEBUG] 2023-07-24 19:32:35.152 pki-utils [47408] static: determined registry value for lookup as 131072.
[DEBUG] 2023-07-24 19:32:35.154 HttpClientFactory [47408] Initializing Http Static State

The certificate is a self signed one from OpenSSL which has been installed in the windows system store. pki-utils does check if it is in the system store so the cert does exist.

Therefore as we have a network error for the certificate and debugging into WinHttpSyncHttpClient showed there was no

 WinHttpSetOption( hRequest, 
                            WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                            (LPVOID) pCertContext, 
                            sizeof(CERT_CONTEXT) );

as seen https://learn.microsoft.com/en-us/windows/win32/winhttp/ssl-in-winhttp, then perhaps the cert is not being added to handle.

Reproduction Steps

#pragma once 
#include <iostream>
#include <aws/core/Aws.h>
#include <aws/core/utils/logging/DefaultLogSystem.h>
#include <aws/core/utils/logging/AWSLogging.h>
#include <aws/core/utils/memory/stl/AWSStringStream.h>
#include <aws/core/utils/logging/CRTLogSystem.h>
#include <aws/s3-crt/S3CrtClient.h>
#include <aws/s3-crt/model/CreateBucketRequest.h>
#include <aws/s3-crt/model/BucketLocationConstraint.h>
#include <aws/s3-crt/model/DeleteBucketRequest.h>
#include <aws/s3-crt/model/PutObjectRequest.h>
#include <aws/s3-crt/model/GetObjectRequest.h>
#include <aws/s3-crt/model/DeleteObjectRequest.h>
#include <aws/core/utils/UUID.h>

int main(int argc, char* argv[])
{
	const char *systemStoreCertPath; 
	const char *exampleCert = "CurrentUser\\MY\\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6"; 
	systemStoreCertPath = exampleCert; 
	Aws::SDKOptions options;
	options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;

#ifdef _WIN32
	options.ioOptions.tlsConnectionOptions_create_fn = [systemStoreCertPath]() {

		Aws::Crt::Io::TlsContextOptions tlsCtxOptions;
		tlsCtxOptions = tlsCtxOptions.InitClientWithMtlsSystemPath(systemStoreCertPath);
		Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT);
		return Aws::MakeShared<Aws::Crt::Io::TlsConnectionOptions>("connection", tlsContext.NewConnectionOptions());
   };
#endif

	Aws::InitAPI(options);
	//make your SDK calls in here.
	{
		Aws::S3Crt::ClientConfiguration config;
		config.region = Aws::Region::US_EAST_1;
		config.endpointOverride = Aws::String(("YOUR IP HERE" + ":" + "PORT HERE").c_str());
		config.verifySSL = true; config.scheme = Aws::Http::Scheme::HTTPS;

		Aws::S3Crt::S3CrtClient s3CrtClient(
			Aws::Auth::AWSCredentials("USERNAME", "PASSWORD"),
			config,
			Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never,
			false
		);

		Aws::S3Crt::Model::ListBucketsOutcome outcome = s3CrtClient.ListBuckets();

		if (outcome.IsSuccess()) {
			std::cout << "All buckets under my account:" << std::endl;

			for (auto const& bucket : outcome.GetResult().GetBuckets())
			{
				std::cout << "  * " << bucket.GetName() << std::endl;
			}
			std::cout << std::endl;

		}
		else {
			std::cout << "ListBuckets error:\n" << outcome.GetError() << std::endl << std::endl;
		}
	}
	Aws::ShutdownAPI(options);
}

Provide the necessary values. You can find relevant cert from the windows store by opening PowerShell and running

Set-Location Cert:\CurrentUser\My\
Get-ChildItem

Which should show some valid certs else cd up a level and then run Get-ChildItem and then cd into a non empty store.

Possible Solution

In WinHttpSyncHttpClient.cpp add the following for either Windows or if it has winhttp client enabled for the build of the sdk.


WinHttpSyncHttpClient::WinHttpSyncHttpClient(const ClientConfiguration& config){

    //code

    m_tlsOtptions = config.tlsConnectionOptions;
   
    //code
}

void* WinHttpSyncHttpClient::OpenRequest(const std::shared_ptr<HttpRequest>& request, void* connection, const Aws::StringStream& ss) const
{
    //code 

    // WinHttpOpenRequest uses a connection handle to create a request handle
    HINTERNET hHttpRequest = WinHttpOpenRequest(connection, StringUtils::ToWString(HttpMethodMapper::GetNameForHttpMethod(request->GetMethod())).c_str(),
        wss.c_str(), nullptr, nullptr, accept, requestFlags);

    //more code 

    if (m_verifySSL) //SSL on 
    {
#ifdef _WIN32 
       //https://learn.microsoft.com/en-us/windows/win32/winhttp/ssl-in-winhttp
       //alloc store and context like in above here not sure how you all want to handle that 
        if (!WinHttpSetOption( hHttpRequest, 
                            WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                            (LPVOID) pCertContext, 
                            sizeof(CERT_CONTEXT)){
            AWS_LOGSTREAM_FATAL(GetLogTag(), "Failed to sign with cert.");
         
    }
    CertFreeCertificateContext( pCertContext );
#endif 
    // more code 
}

Additional Information/Context

This was built from the package manager vcpkg build. I also built from source with both curl enabled and disabled, and the issue was similar. Perhaps I built with curl I followed the instructions found from here: but debugging into the program showed the sdk was still using the winhttp client instead so I gave up more or less on that route. Although I should let it be noted that using the command line cURL I was able to hit the endpoint if I used a correct cert. from the windows certificate store.

AWS CPP SDK version used

1.11.65 (vcpkg build)

Compiler and Version used

Microsoft Visual Studio 2017 Version 15.9.54

Operating System and version

Windows 10 Pro 22H2

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.p2This is a standard priority issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions