Implement mTLS support in WinHTTP transport. (#6131)
* Very preliminary mTLS implementation * Tests for TLS client certificate * Tested mTLS functionality * Added changelog entry; updated PCCERT_CONTEXT using declaration to be more succinct.
This commit is contained in:
parent
3687136d04
commit
9770fb77dc
2
.vscode/cspell.json
vendored
2
.vscode/cspell.json
vendored
@ -207,6 +207,8 @@
|
||||
"opentelemetry",
|
||||
"Osterman",
|
||||
"otel",
|
||||
"PCCERT",
|
||||
"PCERT",
|
||||
"PBYTE",
|
||||
"pdbs",
|
||||
"phoebusm",
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
|
||||
### Features Added
|
||||
|
||||
- Added mTLS support to WinHTTP transport.
|
||||
- To enable mTLS, first create an appropriate Windows `PCCERT_CONTEXT` object and set the `TlsClientCertificate` field in `WinHttpTransportOptions` to that certificate before creating the `WinHttpTransport` object.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// cspell: words PCCERT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief #Azure::Core::Http::HttpTransport implementation via WinHTTP.
|
||||
@ -20,10 +22,16 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Declaration of a Windows PCCERT_CONTEXT structure from the Windows SDK.
|
||||
*/
|
||||
using PCCERT_CONTEXT = const struct _CERT_CONTEXT*;
|
||||
|
||||
namespace Azure { namespace Core { namespace Http {
|
||||
namespace _detail {
|
||||
class WinHttpTransportImpl;
|
||||
class WinHttpRequest;
|
||||
}
|
||||
} // namespace _detail
|
||||
|
||||
/**
|
||||
* @brief Sets the WinHTTP session and connection options used to customize the behavior of the
|
||||
@ -91,6 +99,12 @@ namespace Azure { namespace Core { namespace Http {
|
||||
* the server.
|
||||
*/
|
||||
std::vector<std::string> ExpectedTlsRootCertificates;
|
||||
|
||||
/**
|
||||
* @brief TLS Client Certificate Context, used when the TLS Server requests mTLS client
|
||||
* authentication.
|
||||
*/
|
||||
PCCERT_CONTEXT TlsClientCertificate{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -99,23 +113,16 @@ namespace Azure { namespace Core { namespace Http {
|
||||
*/
|
||||
class WinHttpTransport : public HttpTransport {
|
||||
private:
|
||||
WinHttpTransportOptions m_options;
|
||||
// m_sessionhandle is const to ensure immutability.
|
||||
const Azure::Core::_internal::UniqueHandle<void*> m_sessionHandle;
|
||||
std::unique_ptr<_detail::WinHttpTransportImpl> m_impl;
|
||||
|
||||
Azure::Core::_internal::UniqueHandle<void*> CreateSessionHandle();
|
||||
Azure::Core::_internal::UniqueHandle<void*> CreateConnectionHandle(
|
||||
Azure::Core::Url const& url,
|
||||
Azure::Core::Context const& context);
|
||||
|
||||
std::unique_ptr<_detail::WinHttpRequest> CreateRequestHandle(
|
||||
Azure::Core::_internal::UniqueHandle<void*> const& connectionHandle,
|
||||
Azure::Core::Url const& url,
|
||||
Azure::Core::Http::HttpMethod const& method);
|
||||
|
||||
// Callback to allow a derived transport to extract the request handle. Used for WebSocket
|
||||
// transports.
|
||||
virtual void OnUpgradedConnection(std::unique_ptr<_detail::WinHttpRequest> const&){};
|
||||
protected:
|
||||
/** @brief Callback to allow a derived transport to extract the request handle. Used for
|
||||
* WebSocket transports.
|
||||
*
|
||||
* @param request - Request which contains the WinHttp request handle.
|
||||
*/
|
||||
virtual void OnUpgradedConnection(
|
||||
std::unique_ptr<_detail::WinHttpRequest> const& request) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -125,11 +132,6 @@ namespace Azure { namespace Core { namespace Http {
|
||||
*/
|
||||
WinHttpTransport(WinHttpTransportOptions const& options = WinHttpTransportOptions());
|
||||
|
||||
/**
|
||||
* @brief Constructs `%WinHttpTransport`.
|
||||
*
|
||||
* @param options Optional parameter to override the default settings.
|
||||
*/
|
||||
/**
|
||||
* @brief Constructs `%WinHttpTransport` object based on common Azure HTTP Transport Options
|
||||
*
|
||||
@ -148,6 +150,10 @@ namespace Azure { namespace Core { namespace Http {
|
||||
// and virtual or protected and
|
||||
// non-virtual"](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c35-a-base-class-destructor-should-be-either-public-and-virtual-or-protected-and-non-virtual)
|
||||
virtual ~WinHttpTransport();
|
||||
|
||||
// @cond
|
||||
friend _detail::WinHttpTransportImpl;
|
||||
// @endcond
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Core::Http
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
namespace Azure { namespace Core { namespace Http { namespace _detail {
|
||||
class WinHttpTransportImpl : HttpTransport {
|
||||
private:
|
||||
WinHttpTransport const* m_parent;
|
||||
|
||||
WinHttpTransportOptions m_options;
|
||||
// m_sessionhandle is const to ensure immutability.
|
||||
const Azure::Core::_internal::UniqueHandle<void*> m_sessionHandle;
|
||||
wil::unique_cert_context m_tlsClientCertificate;
|
||||
|
||||
Azure::Core::_internal::UniqueHandle<void*> CreateSessionHandle();
|
||||
Azure::Core::_internal::UniqueHandle<void*> CreateConnectionHandle(
|
||||
Azure::Core::Url const& url,
|
||||
Azure::Core::Context const& context);
|
||||
|
||||
std::unique_ptr<_detail::WinHttpRequest> CreateRequestHandle(
|
||||
Azure::Core::_internal::UniqueHandle<void*> const& connectionHandle,
|
||||
Azure::Core::Url const& url,
|
||||
Azure::Core::Http::HttpMethod const& method);
|
||||
|
||||
// Callback to allow a derived transport to extract the request handle. Used for WebSocket
|
||||
// transports.
|
||||
virtual void OnUpgradedConnection(std::unique_ptr<_detail::WinHttpRequest> const& request)
|
||||
{
|
||||
m_parent->OnUpgradedConnection(request);
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs `%WinHttpTransport`.
|
||||
*
|
||||
* @param options Optional parameter to override the default settings.
|
||||
*/
|
||||
WinHttpTransportImpl(
|
||||
WinHttpTransport const* parent,
|
||||
WinHttpTransportOptions const& options = WinHttpTransportOptions());
|
||||
|
||||
/**
|
||||
* @brief Constructs `%WinHttpTransport`.
|
||||
*
|
||||
* @param options Optional parameter to override the default settings.
|
||||
*/
|
||||
/**
|
||||
* @brief Constructs `%WinHttpTransport` object based on common Azure HTTP Transport Options
|
||||
*
|
||||
*/
|
||||
WinHttpTransportImpl(
|
||||
WinHttpTransport const* parent,
|
||||
Azure::Core::Http::Policies::TransportOptions const& options);
|
||||
|
||||
/**
|
||||
* @brief Implements the HTTP transport interface to send an HTTP Request and produce an
|
||||
* HTTP RawResponse.
|
||||
*
|
||||
*/
|
||||
virtual std::unique_ptr<RawResponse> Send(Request& request, Context const& context) override;
|
||||
|
||||
// See also:
|
||||
// [Core Guidelines C.35: "A base class destructor should be either public
|
||||
// and virtual or protected and
|
||||
// non-virtual"](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c35-a-base-class-destructor-should-be-either-public-and-virtual-or-protected-and-non-virtual)
|
||||
virtual ~WinHttpTransportImpl();
|
||||
};
|
||||
}}}} // namespace Azure::Core::Http::_detail
|
||||
@ -28,9 +28,10 @@
|
||||
#pragma warning(disable : 6553)
|
||||
#pragma warning(disable : 6387) // An argument in result_macros.h may be '0', for the function
|
||||
// 'GetProcAddress'.
|
||||
#include <wincrypt.h>
|
||||
|
||||
#include <wil\resource.h>
|
||||
#pragma warning(pop)
|
||||
#include <wincrypt.h>
|
||||
#include <winhttp.h>
|
||||
|
||||
namespace Azure { namespace Core { namespace Http { namespace _detail {
|
||||
@ -157,6 +158,7 @@ namespace Azure { namespace Core { namespace Http { namespace _detail {
|
||||
Azure::Core::_internal::UniqueHandle<HINTERNET> m_requestHandle;
|
||||
std::unique_ptr<WinHttpAction> m_httpAction;
|
||||
std::vector<std::string> m_expectedTlsRootCertificates;
|
||||
wil::unique_cert_context m_tlsClientCertificate;
|
||||
|
||||
/*
|
||||
* Adds the specified trusted certificates to the specified certificate store.
|
||||
@ -176,6 +178,7 @@ namespace Azure { namespace Core { namespace Http { namespace _detail {
|
||||
Azure::Core::_internal::UniqueHandle<HINTERNET> const& connectionHandle,
|
||||
Azure::Core::Url const& url,
|
||||
Azure::Core::Http::HttpMethod const& method,
|
||||
PCCERT_CONTEXT tlsClientCertificate,
|
||||
WinHttpTransportOptions const& options);
|
||||
|
||||
~WinHttpRequest();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,17 @@ namespace Azure { namespace Core { namespace Test {
|
||||
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
|
||||
auto response = m_pipeline->Send(request, Context{});
|
||||
checkResponseCode(response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent);
|
||||
auto expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
|
||||
std::uint64_t expectedResponseBodySize;
|
||||
if (response->GetStatusCode() == Azure::Core::Http::HttpStatusCode::NoContent)
|
||||
{
|
||||
// http://mt3.google.com/generate_204 returns 204 with no body and thus no content-length
|
||||
// header
|
||||
expectedResponseBodySize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedResponseBodySize = std::stoull(response->GetHeaders().at("content-length"));
|
||||
}
|
||||
CheckBodyFromBuffer(*response, expectedResponseBodySize);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user