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",
|
"opentelemetry",
|
||||||
"Osterman",
|
"Osterman",
|
||||||
"otel",
|
"otel",
|
||||||
|
"PCCERT",
|
||||||
|
"PCERT",
|
||||||
"PBYTE",
|
"PBYTE",
|
||||||
"pdbs",
|
"pdbs",
|
||||||
"phoebusm",
|
"phoebusm",
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
### Features Added
|
### 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
|
### Breaking Changes
|
||||||
|
|
||||||
### Bugs Fixed
|
### Bugs Fixed
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
// cspell: words PCCERT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* @brief #Azure::Core::Http::HttpTransport implementation via WinHTTP.
|
* @brief #Azure::Core::Http::HttpTransport implementation via WinHTTP.
|
||||||
@ -20,10 +22,16 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#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 Azure { namespace Core { namespace Http {
|
||||||
namespace _detail {
|
namespace _detail {
|
||||||
|
class WinHttpTransportImpl;
|
||||||
class WinHttpRequest;
|
class WinHttpRequest;
|
||||||
}
|
} // namespace _detail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the WinHTTP session and connection options used to customize the behavior of the
|
* @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.
|
* the server.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> ExpectedTlsRootCertificates;
|
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 {
|
class WinHttpTransport : public HttpTransport {
|
||||||
private:
|
private:
|
||||||
WinHttpTransportOptions m_options;
|
std::unique_ptr<_detail::WinHttpTransportImpl> m_impl;
|
||||||
// m_sessionhandle is const to ensure immutability.
|
|
||||||
const Azure::Core::_internal::UniqueHandle<void*> m_sessionHandle;
|
|
||||||
|
|
||||||
Azure::Core::_internal::UniqueHandle<void*> CreateSessionHandle();
|
protected:
|
||||||
Azure::Core::_internal::UniqueHandle<void*> CreateConnectionHandle(
|
/** @brief Callback to allow a derived transport to extract the request handle. Used for
|
||||||
Azure::Core::Url const& url,
|
* WebSocket transports.
|
||||||
Azure::Core::Context const& context);
|
*
|
||||||
|
* @param request - Request which contains the WinHttp request handle.
|
||||||
std::unique_ptr<_detail::WinHttpRequest> CreateRequestHandle(
|
*/
|
||||||
Azure::Core::_internal::UniqueHandle<void*> const& connectionHandle,
|
virtual void OnUpgradedConnection(
|
||||||
Azure::Core::Url const& url,
|
std::unique_ptr<_detail::WinHttpRequest> const& request) const;
|
||||||
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&){};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -125,11 +132,6 @@ namespace Azure { namespace Core { namespace Http {
|
|||||||
*/
|
*/
|
||||||
WinHttpTransport(WinHttpTransportOptions const& options = WinHttpTransportOptions());
|
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
|
* @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
|
// 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)
|
// 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();
|
virtual ~WinHttpTransport();
|
||||||
|
|
||||||
|
// @cond
|
||||||
|
friend _detail::WinHttpTransportImpl;
|
||||||
|
// @endcond
|
||||||
};
|
};
|
||||||
|
|
||||||
}}} // namespace Azure::Core::Http
|
}}} // 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 : 6553)
|
||||||
#pragma warning(disable : 6387) // An argument in result_macros.h may be '0', for the function
|
#pragma warning(disable : 6387) // An argument in result_macros.h may be '0', for the function
|
||||||
// 'GetProcAddress'.
|
// 'GetProcAddress'.
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
#include <wil\resource.h>
|
#include <wil\resource.h>
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#include <wincrypt.h>
|
|
||||||
#include <winhttp.h>
|
#include <winhttp.h>
|
||||||
|
|
||||||
namespace Azure { namespace Core { namespace Http { namespace _detail {
|
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;
|
Azure::Core::_internal::UniqueHandle<HINTERNET> m_requestHandle;
|
||||||
std::unique_ptr<WinHttpAction> m_httpAction;
|
std::unique_ptr<WinHttpAction> m_httpAction;
|
||||||
std::vector<std::string> m_expectedTlsRootCertificates;
|
std::vector<std::string> m_expectedTlsRootCertificates;
|
||||||
|
wil::unique_cert_context m_tlsClientCertificate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adds the specified trusted certificates to the specified certificate store.
|
* 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::_internal::UniqueHandle<HINTERNET> const& connectionHandle,
|
||||||
Azure::Core::Url const& url,
|
Azure::Core::Url const& url,
|
||||||
Azure::Core::Http::HttpMethod const& method,
|
Azure::Core::Http::HttpMethod const& method,
|
||||||
|
PCCERT_CONTEXT tlsClientCertificate,
|
||||||
WinHttpTransportOptions const& options);
|
WinHttpTransportOptions const& options);
|
||||||
|
|
||||||
~WinHttpRequest();
|
~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 request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host);
|
||||||
auto response = m_pipeline->Send(request, Context{});
|
auto response = m_pipeline->Send(request, Context{});
|
||||||
checkResponseCode(response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent);
|
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);
|
CheckBodyFromBuffer(*response, expectedResponseBodySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user