[identity] Remove OpenSSL dependency on Windows. (#4747)

* Add dependency to WIL in azure-identity-cpp when on WIndows.

* Use Win32 APIs to get the thumbprint and private key of a certificate.

* Don't use `out_param_ptr` in `GetPrivateKey`.

`NCRYPT_KEY_HANDLE` is not a pointer but a uintptr and using `out_param_ptr` with it causes compiler errors.

* Use Win32 APIs to sign messages with a private key.

* Remove the OpenSSL dependency from azure-identity-cpp on Windows.

We can't remove it from the root vcpkg.json that gets used for development, because an attestation example uses OpenSSL for certificate code.

* Update the changelog.

* Fix compile errors.

We can't use WIL in the public headers; we will use instead the same approach with the OpenSSL handles.

* Fix more compile errors.

* Use `AZ_PLATFORM_WINDOWS`.

* clang-format

* Update cspell.json

* Update CHANGELOG.md

* Update cspell.json

* Update cspell.json

* Attempt to make spell checker happy with minimum changes

* Ignore `CMakeUserPresets.json`.

* Move to the BCrypt APIs in anticipation of the rewrite.

* Use Windows APIs to read the certificate and the private key in PEM format.

* Move decoding the PEM string to its own function.

* Remove redundant error messages from some checks.

WIL does not recommend them if they do not provide additional information, and it already captures the source location.

* Support opening raw RSA and ECC private keys.

* Stop using pseudo-handles.

They are not supported in Windows Server 2012 R2, which is still supported.

* Fix CI.

* Refactor the ClientCertificateCredential tests and test more certificates.

Thanks to googletest's parameterized tests, duplicate code was eliminated and we test twelve cases with only two test methods.

* Improve code sharing across OpenSSL and CNG.

* Fix error checks when signing.

* Remove ECC certificate support.

Turns out Microsoft identity platform itself does not support them.

* Replace `#ifdef` with `#if defined`.

* Move some CryptoAPI-related terms to a `cspell.json` file local to `azure-identity`.

* Exclude the root `cspell.json` file from gitignore.

* Clean-up `cspell.json`.

* Fix linked libraries of `azure-identity`.
We don't use ncrypt anymore.

* Move `platform.hpp` to `client_certificate_credential.cpp`.

* Clean-up `UniquePrivateKey` and reduce mentions of `pkey`.

---------

Co-authored-by: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com>
This commit is contained in:
Theodore Tsirpanis 2023-08-28 20:55:49 +03:00 committed by GitHub
parent 80b6fc6022
commit b7afe2faf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 613 additions and 479 deletions

5
.gitignore vendored
View File

@ -348,8 +348,11 @@ build/
[Dd]ocs/
# vscode
.vscode/
.vscode/*
!.vscode/cspell.json
.factorypath
# Default Assets restore directory
.assets
CMakeUserPresets.json

7
.vscode/cspell.json vendored
View File

@ -82,6 +82,7 @@
"gtest",
"Gzrs",
"HKEY",
"HLOCAL",
"HRESULT",
"IMDS",
"immutability",
@ -244,6 +245,12 @@
"Sylvain"
]
},
{
"filename": "**/sdk/identity/azure-identity/**",
"words": [
"PCCERT"
]
},
{
"filename": "**/sdk/storage/azure-storage-common/CHANGELOG.md",
"words": [

View File

@ -8,6 +8,8 @@
### Bugs Fixed
- [[#4084]](https://github.com/Azure/azure-sdk-for-cpp/issues/4084) Remove OpenSSL dependency on Windows.
### Other Changes
## 1.6.0-beta.1 (2023-08-11)

View File

@ -101,8 +101,13 @@ target_include_directories(
target_link_libraries(azure-identity PUBLIC Azure::azure-core)
find_package(OpenSSL REQUIRED)
target_link_libraries(azure-identity PRIVATE OpenSSL::Crypto)
if(WIN32)
find_package(wil CONFIG REQUIRED)
target_link_libraries(azure-identity PRIVATE WIL::WIL bcrypt crypt32)
else()
find_package(OpenSSL REQUIRED)
target_link_libraries(azure-identity PRIVATE OpenSSL::Crypto)
endif()
get_az_version("${CMAKE_CURRENT_SOURCE_DIR}/src/private/package_version.hpp")
generate_documentation(azure-identity ${AZ_LIBRARY_VERSION})

View File

@ -24,16 +24,16 @@ namespace Azure { namespace Identity {
namespace _detail {
class TokenCredentialImpl;
void FreePkeyImpl(void* pkey);
void FreePrivateKeyImpl(void* pkey);
template <typename> struct UniquePkeyHelper;
template <> struct UniquePkeyHelper<void*>
template <typename> struct UniquePrivateKeyHelper;
template <> struct UniquePrivateKeyHelper<void*>
{
static void FreePkey(void* pkey) { FreePkeyImpl(pkey); }
using type = Azure::Core::_internal::BasicUniqueHandle<void, FreePkey>;
static void FreePrivateKey(void* pkey) { FreePrivateKeyImpl(pkey); }
using type = Azure::Core::_internal::BasicUniqueHandle<void, FreePrivateKey>;
};
using UniquePkeyHandle = Azure::Core::_internal::UniqueHandle<void*, UniquePkeyHelper>;
using UniquePrivateKey = Azure::Core::_internal::UniqueHandle<void*, UniquePrivateKeyHelper>;
} // namespace _detail
/**
@ -73,7 +73,7 @@ namespace Azure { namespace Identity {
std::string m_requestBody;
std::string m_tokenPayloadStaticPart;
std::string m_tokenHeaderEncoded;
_detail::UniquePkeyHandle m_pkey;
_detail::UniquePrivateKey m_pkey;
explicit ClientCertificateCredential(
std::string tenantId,

View File

@ -8,6 +8,9 @@
#include <azure/core/base64.hpp>
#include <azure/core/datetime.hpp>
#include <azure/core/internal/cryptography/sha_hash.hpp>
#include <azure/core/io/body_stream.hpp>
#include <azure/core/platform.hpp>
#include <azure/core/uuid.hpp>
#include <chrono>
@ -15,12 +18,19 @@
#include <sstream>
#include <vector>
#if defined(AZ_PLATFORM_WINDOWS)
#include <Windows.h>
#include <wil/resource.h>
#include <wil/result.h>
#else
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/ossl_typ.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#endif
using Azure::Identity::ClientCertificateCredential;
@ -34,6 +44,7 @@ using Azure::Core::Credentials::AuthenticationException;
using Azure::Core::Credentials::TokenCredentialOptions;
using Azure::Core::Credentials::TokenRequestContext;
using Azure::Core::Http::HttpMethod;
using Azure::Core::IO::FileBodyStream;
using Azure::Identity::_detail::TenantIdResolver;
using Azure::Identity::_detail::TokenCredentialImpl;
@ -50,6 +61,204 @@ template <typename T> std::vector<uint8_t> ToUInt8Vector(T const& in)
return outVec;
}
using CertificateThumbprint = std::vector<unsigned char>;
using UniquePrivateKey = Azure::Identity::_detail::UniquePrivateKey;
using PrivateKey = decltype(std::declval<UniquePrivateKey>().get());
#if defined(AZ_PLATFORM_WINDOWS)
enum PrivateKeyType
{
Rsa,
Ecdsa,
Pkcs
};
std::vector<uint8_t> PemToBinary(LPCSTR str, DWORD count)
{
DWORD size = 0;
THROW_IF_WIN32_BOOL_FALSE(CryptStringToBinaryA(
str, count, CRYPT_STRING_BASE64HEADER, nullptr, &size, nullptr, nullptr));
std::vector<uint8_t> buffer(size);
THROW_IF_WIN32_BOOL_FALSE(CryptStringToBinaryA(
str, count, CRYPT_STRING_BASE64HEADER, buffer.data(), &size, nullptr, nullptr));
return buffer;
}
CertificateThumbprint GetThumbprint(PCCERT_CONTEXT cert)
{
DWORD size = 0;
THROW_IF_WIN32_BOOL_FALSE(
CertGetCertificateContextProperty(cert, CERT_SHA1_HASH_PROP_ID, nullptr, &size));
std::vector<unsigned char> thumbprint(size);
THROW_IF_WIN32_BOOL_FALSE(
CertGetCertificateContextProperty(cert, CERT_SHA1_HASH_PROP_ID, thumbprint.data(), &size));
return thumbprint;
}
wil::unique_cert_context ImportPemCertificate(std::string const& pem)
{
auto headerStart = pem.find("-----BEGIN CERTIFICATE-----");
if (headerStart == std::string::npos)
{
throw AuthenticationException("PEM file does not contain certificate.");
}
auto certBuffer
= PemToBinary(pem.c_str() + headerStart, static_cast<DWORD>(pem.size() - headerStart));
auto cert = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
certBuffer.data(),
static_cast<DWORD>(certBuffer.size()));
THROW_LAST_ERROR_IF_NULL(cert);
return wil::unique_cert_context{cert};
}
size_t FindPemPrivateKeyHeader(std::string const& pem, PrivateKeyType& keyType)
{
auto headerStart = pem.find("-----BEGIN RSA PRIVATE KEY-----");
if (headerStart != std::string::npos)
{
keyType = PrivateKeyType::Rsa;
return headerStart;
}
headerStart = pem.find("-----BEGIN EC PRIVATE KEY-----");
if (headerStart != std::string::npos)
{
keyType = PrivateKeyType::Ecdsa;
return headerStart;
}
keyType = PrivateKeyType::Pkcs;
return pem.find("-----BEGIN PRIVATE KEY-----");
}
wil::unique_bcrypt_algorithm OpenAlgorithm(LPCWSTR algId)
{
wil::unique_bcrypt_algorithm alg;
THROW_IF_NTSTATUS_FAILED(BCryptOpenAlgorithmProvider(wil::out_param(alg), algId, nullptr, 0));
return alg;
}
UniquePrivateKey ImportRsaPrivateKey(const BYTE* data, DWORD size)
{
DWORD keySize = 0;
wil::unique_hlocal_ptr<BCRYPT_RSAKEY_BLOB> rsaKeyBlob;
THROW_IF_WIN32_BOOL_FALSE(CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CNG_RSA_PRIVATE_KEY_BLOB,
data,
size,
CRYPT_DECODE_ALLOC_FLAG,
nullptr,
wil::out_param(rsaKeyBlob),
&keySize));
BCRYPT_KEY_HANDLE key;
auto alg = OpenAlgorithm(BCRYPT_RSA_ALGORITHM);
THROW_IF_NTSTATUS_FAILED(BCryptImportKeyPair(
alg.get(),
nullptr,
BCRYPT_RSAPRIVATE_BLOB,
&key,
reinterpret_cast<uint8_t*>(rsaKeyBlob.get()),
keySize,
0));
return UniquePrivateKey{key};
}
UniquePrivateKey ImportEccPrivateKey(const BYTE*, DWORD)
{
throw AuthenticationException("ECDSA private keys are not supported.");
}
UniquePrivateKey ImportPemPrivateKey(std::string const& pem)
{
PrivateKeyType keyType{};
auto headerStart = FindPemPrivateKeyHeader(pem, keyType);
if (headerStart == std::string::npos)
{
throw AuthenticationException("PEM file does not contain private key.");
}
auto keyBuffer{
PemToBinary(pem.c_str() + headerStart, static_cast<DWORD>(pem.size() - headerStart))};
if (keyType == PrivateKeyType::Rsa)
{
return ImportRsaPrivateKey(keyBuffer.data(), static_cast<DWORD>(keyBuffer.size()));
}
if (keyType == PrivateKeyType::Ecdsa)
{
return ImportEccPrivateKey(keyBuffer.data(), static_cast<DWORD>(keyBuffer.size()));
}
wil::unique_hlocal_ptr<CRYPT_PRIVATE_KEY_INFO> privateKeyInfo;
DWORD keySize = 0;
THROW_IF_WIN32_BOOL_FALSE(CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
keyBuffer.data(),
static_cast<DWORD>(keyBuffer.size()),
CRYPT_DECODE_ALLOC_FLAG,
nullptr,
wil::out_param(privateKeyInfo),
&keySize));
if (strcmp(privateKeyInfo->Algorithm.pszObjId, szOID_RSA_RSA) == 0)
{
return ImportRsaPrivateKey(
privateKeyInfo->PrivateKey.pbData, privateKeyInfo->PrivateKey.cbData);
}
if (strcmp(privateKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY) == 0)
{
return ImportEccPrivateKey(
privateKeyInfo->PrivateKey.pbData, privateKeyInfo->PrivateKey.cbData);
}
throw AuthenticationException("Invalid private key.");
}
std::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(std::string const& path)
{
auto pemContent{FileBodyStream(path).ReadToEnd()};
std::string pem{pemContent.begin(), pemContent.end()};
pemContent = {};
auto certContext = ImportPemCertificate(pem);
return std::make_tuple(GetThumbprint(certContext.get()), ImportPemPrivateKey(pem));
}
std::vector<unsigned char> SignPkcs1Sha256(PrivateKey key, const uint8_t* data, size_t size)
{
auto hash = Azure::Core::Cryptography::_internal::Sha256Hash().Final(data, size);
BCRYPT_PKCS1_PADDING_INFO paddingInfo;
paddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
DWORD signatureSize = 0;
auto status = BCryptSignHash(
key,
&paddingInfo,
hash.data(),
static_cast<ULONG>(hash.size()),
nullptr,
0,
&signatureSize,
BCRYPT_PAD_PKCS1);
if (status != ERROR_SUCCESS)
{
return {};
}
std::vector<unsigned char> signature(signatureSize);
status = BCryptSignHash(
key,
&paddingInfo,
hash.data(),
static_cast<ULONG>(hash.size()),
signature.data(),
static_cast<ULONG>(signature.size()),
&signatureSize,
BCRYPT_PAD_PKCS1);
if (status != ERROR_SUCCESS)
{
return {};
}
return signature;
}
#else
template <typename> struct UniqueHandleHelper;
template <> struct UniqueHandleHelper<BIO>
@ -69,11 +278,87 @@ template <> struct UniqueHandleHelper<EVP_MD_CTX>
template <typename T>
using UniqueHandle = Azure::Core::_internal::UniqueHandle<T, UniqueHandleHelper>;
std::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(const std::string& path)
{
// Open certificate file, then get private key and X509:
UniqueHandle<BIO> bio(BIO_new_file(path.c_str(), "r"));
if (!bio)
{
throw AuthenticationException("Failed to open certificate file.");
}
UniquePrivateKey pkey{PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)};
if (!pkey)
{
throw AuthenticationException("Failed to read certificate private key.");
}
UniqueHandle<X509> x509{PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)};
if (!x509)
{
std::ignore = BIO_seek(bio.get(), 0);
x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (!x509)
{
throw AuthenticationException("Failed to read X509 section.");
}
}
CertificateThumbprint thumbprint(EVP_MAX_MD_SIZE);
// Get certificate thumbprint:
unsigned int mdLen = 0;
const auto digestResult = X509_digest(x509.get(), EVP_sha1(), thumbprint.data(), &mdLen);
if (!digestResult)
{
throw AuthenticationException("Failed to get certificate thumbprint.");
}
// Drop unused buffer space:
const auto mdLenSz = static_cast<decltype(thumbprint)::size_type>(mdLen);
if (thumbprint.size() > mdLenSz)
{
thumbprint.resize(mdLenSz);
}
return std::make_tuple(thumbprint, std::move(pkey));
}
std::vector<unsigned char> SignPkcs1Sha256(PrivateKey key, const uint8_t* data, size_t size)
{
UniqueHandle<EVP_MD_CTX> mdCtx(EVP_MD_CTX_new());
if (!mdCtx)
{
return {};
}
EVP_PKEY_CTX* signCtx = nullptr;
if ((EVP_DigestSignInit(mdCtx.get(), &signCtx, EVP_sha256(), nullptr, static_cast<EVP_PKEY*>(key))
== 1)
&& (EVP_PKEY_CTX_set_rsa_padding(signCtx, RSA_PKCS1_PADDING) == 1))
{
size_t sigLen = 0;
if (EVP_DigestSign(mdCtx.get(), nullptr, &sigLen, nullptr, 0) == 1)
{
std::vector<unsigned char> sigVec(sigLen);
if (EVP_DigestSign(mdCtx.get(), sigVec.data(), &sigLen, data, size) == 1)
{
return sigVec;
}
}
}
return {};
}
#endif
} // namespace
void Azure::Identity::_detail::FreePkeyImpl(void* pkey)
void Azure::Identity::_detail::FreePrivateKeyImpl(void* pkey)
{
#if defined(AZ_PLATFORM_WINDOWS)
BCryptDestroyKey(static_cast<BCRYPT_KEY_HANDLE>(pkey));
#else
EVP_PKEY_free(static_cast<EVP_PKEY*>(pkey));
#endif
}
ClientCertificateCredential::ClientCertificateCredential(
@ -100,50 +385,19 @@ ClientCertificateCredential::ClientCertificateCredential(
std::string thumbprintBase64Str;
{
std::vector<unsigned char> mdVec(EVP_MAX_MD_SIZE);
CertificateThumbprint mdVec;
try
{
UniqueHandle<X509> x509;
{
// Open certificate file, then get private key and X509:
UniqueHandle<BIO> bio(BIO_new_file(clientCertificatePath.c_str(), "r"));
if (!bio)
{
throw AuthenticationException("Failed to open certificate file.");
}
m_pkey.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
if (!m_pkey)
{
throw AuthenticationException("Failed to read certificate private key.");
}
x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (!x509)
{
static_cast<void>(BIO_seek(bio.get(), 0));
x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (!x509)
{
throw AuthenticationException("Failed to read X509 section.");
}
}
}
// Get certificate thumbprint:
unsigned int mdLen = 0;
const auto digestResult = X509_digest(x509.get(), EVP_sha1(), mdVec.data(), &mdLen);
if (!digestResult)
{
throw AuthenticationException("Failed to get certificate thumbprint.");
}
// Drop unused buffer space:
const auto mdLenSz = static_cast<decltype(mdVec)::size_type>(mdLen);
if (mdVec.size() > mdLenSz)
{
mdVec.resize(mdLenSz);
}
std::tie(mdVec, m_pkey) = ReadPemCertificate(clientCertificatePath);
}
catch (AuthenticationException&)
{
throw;
}
catch (std::exception& e)
{
// WIL does not throw AuthenticationException.
throw AuthenticationException(e.what());
}
// Get thumbprint as hex string:
@ -256,41 +510,14 @@ AccessToken ClientCertificateCredential::GetToken(
}
// Get assertion signature.
std::string signature;
{
UniqueHandle<EVP_MD_CTX> mdCtx(EVP_MD_CTX_new());
if (mdCtx)
{
EVP_PKEY_CTX* signCtx = nullptr;
if ((EVP_DigestSignInit(
mdCtx.get(),
&signCtx,
EVP_sha256(),
nullptr,
static_cast<EVP_PKEY*>(m_pkey.get()))
== 1)
&& (EVP_PKEY_CTX_set_rsa_padding(signCtx, RSA_PKCS1_PADDING) == 1))
{
size_t sigLen = 0;
if (EVP_DigestSign(mdCtx.get(), nullptr, &sigLen, nullptr, 0) == 1)
{
const auto bufToSign = reinterpret_cast<const unsigned char*>(assertion.data());
const auto bufToSignLen = static_cast<size_t>(assertion.size());
std::vector<unsigned char> sigVec(sigLen);
if (EVP_DigestSign(mdCtx.get(), sigVec.data(), &sigLen, bufToSign, bufToSignLen)
== 1)
{
signature = Base64Url::Base64UrlEncode(ToUInt8Vector(sigVec));
}
}
}
}
}
std::string signature = Base64Url::Base64UrlEncode(SignPkcs1Sha256(
m_pkey.get(),
reinterpret_cast<const unsigned char*>(assertion.data()),
static_cast<size_t>(assertion.size())));
if (signature.empty())
{
throw Azure::Core::Credentials::AuthenticationException("Failed to sign token request.");
throw AuthenticationException("Failed to sign token request.");
}
// Add signature to the end of assertion

View File

@ -17,11 +17,24 @@ using Azure::Identity::ClientCertificateCredentialOptions;
using Azure::Identity::Test::_detail::CredentialTestHelper;
namespace {
enum CertFormat
{
RsaPkcs,
RsaRaw
};
enum TestType
{
Regular,
AzureStack,
Authority
};
struct TempCertFile final
{
static const char* const Path;
~TempCertFile();
TempCertFile();
TempCertFile(CertFormat algorithm = RsaPkcs);
};
std::vector<std::string> SplitString(const std::string& s, char separator);
@ -29,9 +42,104 @@ std::vector<std::string> SplitString(const std::string& s, char separator);
std::string ToString(std::vector<uint8_t> const& vec);
} // namespace
TEST(ClientCertificateCredential, GetCredentialName)
class GetCredentialName : public ::testing::TestWithParam<CertFormat> {
TempCertFile m_certFile{GetParam()};
};
class GetToken : public ::testing::TestWithParam<std::tuple<TestType, CertFormat>> {
public:
TestType GetTestType() { return std::get<0>(GetParam()); }
CertFormat GetCertFormat() { return std::get<1>(GetParam()); }
std::string GetTenantId()
{
return GetTestType() == TestType::AzureStack ? "adfs" : "01234567-89ab-cdef-fedc-ba8976543210";
}
std::string GetRequestUri()
{
switch (GetTestType())
{
case TestType::Regular:
return "https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/"
"token";
case TestType::AzureStack:
return "https://login.microsoftonline.com/adfs/oauth2/token";
case TestType::Authority:
return "https://microsoft.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token";
}
AZURE_UNREACHABLE_CODE();
}
std::string GetBodyStart0()
{
switch (GetTestType())
{
case TestType::Regular:
case TestType::Authority: // cspell:disable
return "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-"
"bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&scope=https%3A%2F%2Fazure.com%2F.default"
"&client_assertion="; // cspell:enable
case TestType::AzureStack: // cspell:disable
return "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-"
"bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&scope=https%3A%2F%2Fazure.com"
"&client_assertion="; // cspell:enable
}
AZURE_UNREACHABLE_CODE();
}
std::string GetBodyStart1()
{
// cspell:disable
return "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&client_assertion="; // cspell:enable
}
std::string GetHeader()
{ // cspell:disable
return "{\"x5t\":\"V0pIIQwSzNn6vfSTPv-1f7Vt_Pw\",\"kid\":"
"\"574A48210C12CCD9FABDF4933EFFB57FB56DFCFC\",\"alg\":\"RS256\",\"typ\":\"JWT\"}";
}
std::string GetPayloadStart()
{
switch (GetTestType())
{
case TestType::Regular:
return "{\"aud\":\"https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/"
"oauth2/v2.0/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
case TestType::AzureStack:
return "{\"aud\":\"https://login.microsoftonline.com/adfs/oauth2/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
case TestType::Authority:
return "{\"aud\":\"https://microsoft.com/01234567-89ab-cdef-fedc-ba8976543210/"
"oauth2/v2.0/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
}
AZURE_UNREACHABLE_CODE();
}
size_t GetSignatureSize() { return 256; }
private:
TempCertFile m_certFile{GetCertFormat()};
};
TEST_P(GetCredentialName, )
{
TempCertFile const tempCertFile;
ClientCertificateCredential const cred(
"01234567-89ab-cdef-fedc-ba8976543210",
"fedcba98-7654-3210-0123-456789abcdef",
@ -40,20 +148,19 @@ TEST(ClientCertificateCredential, GetCredentialName)
EXPECT_EQ(cred.GetCredentialName(), "ClientCertificateCredential");
}
TEST(ClientCertificateCredential, Regular)
TEST_P(GetToken, )
{
TempCertFile const tempCertFile;
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
[this](auto transport) {
ClientCertificateCredentialOptions options;
if (GetTestType() == TestType::Authority)
{
options.AuthorityHost = "https://microsoft.com/";
}
options.Transport.Transport = transport;
return std::make_unique<ClientCertificateCredential>(
"01234567-89ab-cdef-fedc-ba8976543210",
"fedcba98-7654-3210-0123-456789abcdef",
TempCertFile::Path,
options);
GetTenantId(), "fedcba98-7654-3210-0123-456789abcdef", TempCertFile::Path, options);
},
{{{"https://azure.com/.default"}}, {{}}},
std::vector<std::string>{
@ -72,49 +179,35 @@ TEST(ClientCertificateCredential, Regular)
EXPECT_EQ(request0.HttpMethod, HttpMethod::Post);
EXPECT_EQ(request1.HttpMethod, HttpMethod::Post);
EXPECT_EQ(
request0.AbsoluteUrl,
"https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
EXPECT_EQ(request0.AbsoluteUrl, GetRequestUri());
EXPECT_EQ(
request1.AbsoluteUrl,
"https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
EXPECT_EQ(request1.AbsoluteUrl, GetRequestUri());
{
constexpr char expectedBodyStart0[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&scope=https%3A%2F%2Fazure.com%2F.default"
"&client_assertion="; // cspell:enable
auto expectedBodyStart0 = GetBodyStart0();
auto expectedBodyStart1 = GetBodyStart1();
constexpr char expectedBodyStart1[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&client_assertion="; // cspell:enable
EXPECT_GT(request0.Body.size(), expectedBodyStart0.size());
EXPECT_GT(request1.Body.size(), expectedBodyStart1.size());
EXPECT_GT(request0.Body.size(), (sizeof(expectedBodyStart0) - 1));
EXPECT_GT(request1.Body.size(), (sizeof(expectedBodyStart1) - 1));
EXPECT_EQ(request0.Body.substr(0, (sizeof(expectedBodyStart0) - 1)), expectedBodyStart0);
EXPECT_EQ(request1.Body.substr(0, (sizeof(expectedBodyStart1) - 1)), expectedBodyStart1);
EXPECT_EQ(request0.Body.substr(0, expectedBodyStart0.size()), expectedBodyStart0);
EXPECT_EQ(request1.Body.substr(0, expectedBodyStart1.size()), expectedBodyStart1);
EXPECT_NE(request0.Headers.find("Content-Length"), request0.Headers.end());
EXPECT_GT(
std::stoi(request0.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart0) - 1));
static_cast<int>(expectedBodyStart0.size()));
EXPECT_NE(request1.Headers.find("Content-Length"), request1.Headers.end());
EXPECT_GT(
std::stoi(request1.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart1) - 1));
static_cast<int>(expectedBodyStart1.size()));
{
using Azure::Core::_internal::Base64Url;
const auto assertion0 = request0.Body.substr((sizeof(expectedBodyStart0) - 1));
const auto assertion1 = request1.Body.substr((sizeof(expectedBodyStart1) - 1));
const auto assertion0 = request0.Body.substr(expectedBodyStart0.size());
const auto assertion1 = request1.Body.substr(expectedBodyStart1.size());
const auto assertion0Parts = SplitString(assertion0, '.');
const auto assertion1Parts = SplitString(assertion1, '.');
@ -137,24 +230,18 @@ TEST(ClientCertificateCredential, Regular)
const auto payload0 = ToString(payload0Vec);
const auto payload1 = ToString(payload1Vec);
constexpr auto ExpectedHeader
= "{\"x5t\":\"V0pIIQwSzNn6vfSTPv-1f7Vt_Pw\",\"kid\":"
"\"574A48210C12CCD9FABDF4933EFFB57FB56DFCFC\",\"alg\":\"RS256\",\"typ\":\"JWT\"}";
auto ExpectedHeader = GetHeader();
EXPECT_EQ(header0, ExpectedHeader);
EXPECT_EQ(header1, ExpectedHeader);
constexpr char ExpectedPayloadStart[]
= "{\"aud\":\"https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/"
"oauth2/v2.0/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
auto ExpectedPayloadStart = GetPayloadStart();
EXPECT_EQ(payload0.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(payload1.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(payload0.substr(0, ExpectedPayloadStart.size()), ExpectedPayloadStart);
EXPECT_EQ(payload1.substr(0, ExpectedPayloadStart.size()), ExpectedPayloadStart);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature0).size(), 256U);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature1).size(), 256U);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature0).size(), GetSignatureSize());
EXPECT_EQ(Base64Url::Base64UrlDecode(signature1).size(), GetSignatureSize());
}
}
@ -175,357 +262,142 @@ TEST(ClientCertificateCredential, Regular)
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
}
TEST(ClientCertificateCredential, AzureStack)
{
TempCertFile const tempCertFile;
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
ClientCertificateCredentialOptions options;
options.Transport.Transport = transport;
return std::make_unique<ClientCertificateCredential>(
"adfs", "fedcba98-7654-3210-0123-456789abcdef", TempCertFile::Path, options);
},
{{{"https://azure.com/.default"}}, {{}}},
std::vector<std::string>{
"{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}",
"{\"expires_in\":7200, \"access_token\":\"ACCESSTOKEN2\"}"});
EXPECT_EQ(actual.Requests.size(), 2U);
EXPECT_EQ(actual.Responses.size(), 2U);
auto const& request0 = actual.Requests.at(0);
auto const& request1 = actual.Requests.at(1);
auto const& response0 = actual.Responses.at(0);
auto const& response1 = actual.Responses.at(1);
EXPECT_EQ(request0.HttpMethod, HttpMethod::Post);
EXPECT_EQ(request1.HttpMethod, HttpMethod::Post);
EXPECT_EQ(request0.AbsoluteUrl, "https://login.microsoftonline.com/adfs/oauth2/token");
EXPECT_EQ(request1.AbsoluteUrl, "https://login.microsoftonline.com/adfs/oauth2/token");
{
constexpr char expectedBodyStart0[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&scope=https%3A%2F%2Fazure.com"
"&client_assertion="; // cspell:enable
constexpr char expectedBodyStart1[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&client_assertion="; // cspell:enable
EXPECT_GT(request0.Body.size(), (sizeof(expectedBodyStart0) - 1));
EXPECT_GT(request1.Body.size(), (sizeof(expectedBodyStart1) - 1));
EXPECT_EQ(request0.Body.substr(0, (sizeof(expectedBodyStart0) - 1)), expectedBodyStart0);
EXPECT_EQ(request1.Body.substr(0, (sizeof(expectedBodyStart1) - 1)), expectedBodyStart1);
EXPECT_NE(request0.Headers.find("Content-Length"), request0.Headers.end());
EXPECT_GT(
std::stoi(request0.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart0) - 1));
EXPECT_NE(request1.Headers.find("Content-Length"), request1.Headers.end());
EXPECT_GT(
std::stoi(request1.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart1) - 1));
{
using Azure::Core::_internal::Base64Url;
const auto assertion0 = request0.Body.substr((sizeof(expectedBodyStart0) - 1));
const auto assertion1 = request1.Body.substr((sizeof(expectedBodyStart1) - 1));
const auto assertion0Parts = SplitString(assertion0, '.');
const auto assertion1Parts = SplitString(assertion1, '.');
EXPECT_EQ(assertion0Parts.size(), 3U);
EXPECT_EQ(assertion1Parts.size(), 3U);
const auto header0Vec = Base64Url::Base64UrlDecode(assertion0Parts[0]);
const auto header1Vec = Base64Url::Base64UrlDecode(assertion1Parts[0]);
const auto payload0Vec = Base64Url::Base64UrlDecode(assertion0Parts[1]);
const auto payload1Vec = Base64Url::Base64UrlDecode(assertion1Parts[1]);
const auto signature0 = assertion0Parts[2];
const auto signature1 = assertion1Parts[2];
const auto header0 = ToString(header0Vec);
const auto header1 = ToString(header1Vec);
const auto payload0 = ToString(payload0Vec);
const auto payload1 = ToString(payload1Vec);
constexpr auto ExpectedHeader
= "{\"x5t\":\"V0pIIQwSzNn6vfSTPv-1f7Vt_Pw\",\"kid\":"
"\"574A48210C12CCD9FABDF4933EFFB57FB56DFCFC\",\"alg\":\"RS256\",\"typ\":\"JWT\"}";
EXPECT_EQ(header0, ExpectedHeader);
EXPECT_EQ(header1, ExpectedHeader);
constexpr char ExpectedPayloadStart[]
= "{\"aud\":\"https://login.microsoftonline.com/adfs/oauth2/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
EXPECT_EQ(payload0.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(payload1.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature0).size(), 256U);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature1).size(), 256U);
}
}
EXPECT_NE(request0.Headers.find("Content-Type"), request0.Headers.end());
EXPECT_EQ(request0.Headers.at("Content-Type"), "application/x-www-form-urlencoded");
EXPECT_NE(request1.Headers.find("Content-Type"), request1.Headers.end());
EXPECT_EQ(request1.Headers.at("Content-Type"), "application/x-www-form-urlencoded");
EXPECT_EQ(response0.AccessToken.Token, "ACCESSTOKEN1");
EXPECT_EQ(response1.AccessToken.Token, "ACCESSTOKEN2");
using namespace std::chrono_literals;
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
}
TEST(ClientCertificateCredential, Authority)
{
TempCertFile const tempCertFile;
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
ClientCertificateCredentialOptions options;
options.AuthorityHost = "https://microsoft.com/";
options.Transport.Transport = transport;
return std::make_unique<ClientCertificateCredential>(
"01234567-89ab-cdef-fedc-ba8976543210",
"fedcba98-7654-3210-0123-456789abcdef",
TempCertFile::Path,
options);
},
{{{"https://azure.com/.default"}}, {{}}},
std::vector<std::string>{
"{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}",
"{\"expires_in\":7200, \"access_token\":\"ACCESSTOKEN2\"}"});
EXPECT_EQ(actual.Requests.size(), 2U);
EXPECT_EQ(actual.Responses.size(), 2U);
auto const& request0 = actual.Requests.at(0);
auto const& request1 = actual.Requests.at(1);
auto const& response0 = actual.Responses.at(0);
auto const& response1 = actual.Responses.at(1);
EXPECT_EQ(request0.HttpMethod, HttpMethod::Post);
EXPECT_EQ(request1.HttpMethod, HttpMethod::Post);
EXPECT_EQ(
request0.AbsoluteUrl,
"https://microsoft.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
EXPECT_EQ(
request1.AbsoluteUrl,
"https://microsoft.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
{
constexpr char expectedBodyStart0[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&scope=https%3A%2F%2Fazure.com%2F.default"
"&client_assertion="; // cspell:enable
constexpr char expectedBodyStart1[] // cspell:disable
= "grant_type=client_credentials"
"&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
"&client_id=fedcba98-7654-3210-0123-456789abcdef"
"&client_assertion="; // cspell:enable
EXPECT_GT(request0.Body.size(), (sizeof(expectedBodyStart0) - 1));
EXPECT_GT(request1.Body.size(), (sizeof(expectedBodyStart1) - 1));
EXPECT_EQ(request0.Body.substr(0, (sizeof(expectedBodyStart0) - 1)), expectedBodyStart0);
EXPECT_EQ(request1.Body.substr(0, (sizeof(expectedBodyStart1) - 1)), expectedBodyStart1);
EXPECT_NE(request0.Headers.find("Content-Length"), request0.Headers.end());
EXPECT_GT(
std::stoi(request0.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart0) - 1));
EXPECT_NE(request1.Headers.find("Content-Length"), request1.Headers.end());
EXPECT_GT(
std::stoi(request1.Headers.at("Content-Length")),
static_cast<int>(sizeof(expectedBodyStart1) - 1));
{
using Azure::Core::_internal::Base64Url;
const auto assertion0 = request0.Body.substr((sizeof(expectedBodyStart0) - 1));
const auto assertion1 = request1.Body.substr((sizeof(expectedBodyStart1) - 1));
const auto assertion0Parts = SplitString(assertion0, '.');
const auto assertion1Parts = SplitString(assertion1, '.');
EXPECT_EQ(assertion0Parts.size(), 3U);
EXPECT_EQ(assertion1Parts.size(), 3U);
const auto header0Vec = Base64Url::Base64UrlDecode(assertion0Parts[0]);
const auto header1Vec = Base64Url::Base64UrlDecode(assertion1Parts[0]);
const auto payload0Vec = Base64Url::Base64UrlDecode(assertion0Parts[1]);
const auto payload1Vec = Base64Url::Base64UrlDecode(assertion1Parts[1]);
const auto signature0 = assertion0Parts[2];
const auto signature1 = assertion1Parts[2];
const auto header0 = ToString(header0Vec);
const auto header1 = ToString(header1Vec);
const auto payload0 = ToString(payload0Vec);
const auto payload1 = ToString(payload1Vec);
constexpr auto ExpectedHeader
= "{\"x5t\":\"V0pIIQwSzNn6vfSTPv-1f7Vt_Pw\",\"kid\":"
"\"574A48210C12CCD9FABDF4933EFFB57FB56DFCFC\",\"alg\":\"RS256\",\"typ\":\"JWT\"}";
EXPECT_EQ(header0, ExpectedHeader);
EXPECT_EQ(header1, ExpectedHeader);
constexpr char ExpectedPayloadStart[]
= "{\"aud\":\"https://microsoft.com/01234567-89ab-cdef-fedc-ba8976543210/"
"oauth2/v2.0/token\","
"\"iss\":\"fedcba98-7654-3210-0123-456789abcdef\","
"\"sub\":\"fedcba98-7654-3210-0123-456789abcdef\",\"jti\":\"";
EXPECT_EQ(payload0.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(payload1.substr(0, (sizeof(ExpectedPayloadStart) - 1)), ExpectedPayloadStart);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature0).size(), 256U);
EXPECT_EQ(Base64Url::Base64UrlDecode(signature1).size(), 256U);
}
}
EXPECT_NE(request0.Headers.find("Content-Type"), request0.Headers.end());
EXPECT_EQ(request0.Headers.at("Content-Type"), "application/x-www-form-urlencoded");
EXPECT_NE(request1.Headers.find("Content-Type"), request1.Headers.end());
EXPECT_EQ(request1.Headers.at("Content-Type"), "application/x-www-form-urlencoded");
EXPECT_EQ(response0.AccessToken.Token, "ACCESSTOKEN1");
EXPECT_EQ(response1.AccessToken.Token, "ACCESSTOKEN2");
using namespace std::chrono_literals;
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
}
INSTANTIATE_TEST_SUITE_P(
ClientCertificateCredential,
GetCredentialName,
testing::Values(RsaPkcs, RsaRaw));
INSTANTIATE_TEST_SUITE_P(
ClientCertificateCredential,
GetToken,
testing::Combine(
testing::Values(Regular, AzureStack, Authority),
testing::Values(RsaPkcs, RsaRaw)));
namespace {
const char* const TempCertFile::Path = "azure-identity-test.pem";
TempCertFile::~TempCertFile() { std::remove(Path); }
TempCertFile::TempCertFile()
TempCertFile::TempCertFile(CertFormat format)
{
std::ofstream cert(Path, std::ios_base::out | std::ios_base::trunc);
cert << // cspell:disable
"Bag Attributes\n"
" Microsoft Local Key set: <No Values>\n"
" localKeyID: 01 00 00 00 \n"
" friendlyName: te-66f5c973-4fc8-4cd3-8acc-64964d79b693\n"
" Microsoft CSP Name: Microsoft Software Key Storage Provider\n"
"Key Attributes\n"
" X509v3 Key Usage: 90 \n"
"-----BEGIN PRIVATE KEY-----\n";
if (format == RsaPkcs)
cert << // cspell:disable
"Bag Attributes\n"
" Microsoft Local Key set: <No Values>\n"
" localKeyID: 01 00 00 00 \n"
" friendlyName: te-66f5c973-4fc8-4cd3-8acc-64964d79b693\n"
" Microsoft CSP Name: Microsoft Software Key Storage Provider\n"
"Key Attributes\n"
" X509v3 Key Usage: 90 \n"
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPdm4pukO7ugEx\n"
"8wXrmo4VIEoicp7w3QsEJGA2bMx9nHMvwugG54t14QpfqBQYQWLeL1HmpcDeivVD\n"
"+15ZXeGLCPVZBHhoY8ZWGibfhAAzqQ0P9Ca1kydjvB4uJcEnF/RYtQv6n6OwmdO1\n"
"wJ22JNcRlMtZqmnb/Q0In2fjXEbdl85/GZlYzMQRdyfI0yriSRBcYV2kg0zeXCxf\n"
"mCvB3rb6I1KpoUFHlkeHtkeDwm0VHUEt4Hz8ghcB00tI5eS2fH2rPkINQKc6+0QU\n"
"C2KICQC+GzJsYDbwQOao5Vhk80H5LRuM9Ndzv+fU3lLnktYCgXgL9AX4L/R9Z4Pz\n"
"tuao/qbRAgMBAAECggEBAMQZIrooiTuZ7uVC3Ja96Y1IjyqOg3QSzAXnSFZJcuVM\n"
"i4hayC02khkjVUXjvtLKg2SW/+hvRqZUXM8cfCsm1Tkxh4/T7OhnXyMl5xahU/uA\n"
"0IsC8c/xv2rDdxeRskh8mQd8Yk1MtlIIpRgIcEqp+exxY+FmdldtkvNSkcVUBNwQ\n"
"nXi+oWPhE2guo2g1BPk2gbF0+3FvSrQ8QwGHg+uQJwrQpJ+SB9TyuQFauGR5/wSq\n"
"H93cFH5YC/+v5I7qW6ZQe0f7rEKQDybGVzkBlKJyGCVYmPn7Xa/wJriws+FZIfHz\n"
"f3m0kJigxJd/HwTrnKSg+H8oBgng7lZLdBYWHMGJhA0CgYEA48moW7szegvfLuUF\n"
"a0sHfyKuNyvOv7Wud4sa0lwdKPHS+atwL6TNUWCAGkomYADEe3qiYgMXDX9U3hlW\n"
"6zktYFj03tnRg4iBjp8nchLBVLf3Wd5TPRw1VKu4ZW43y8BRhYWV+3Z4s1nyMEDA\n"
"NFbKRmL7LDB05oWHdJMjFK/L6YcCgYEA6ShV4v2RQiXzkW6GHSBZDIVHCeWwvIld\n"
"OlEfG7wzZW4e8wNDhfSMtXyJrzfbEyXBtVKoESdP6Nnm9W7ftcynW965S94THuy7\n"
"+ofvHo6JAm8g/0uX70wZ26LU8qhkJMTWmsONBNKLwUzkFT7VGsdaBliam1RLvjeT\n"
"URdQgnftIucCgYEA4FYamT0k1W4bv/OOAr1CBNQDABME64ni6Zj2MXbGwSxou7s8\n"
"IbANBbgkcb/VS3d2CqYchqrEaWaeDp6mG8OUDO+POmsLDJ/D+NKF5rLR9L25vahY\n"
"EjdVzq3QTRTfnqspnnaR37Yt6XUMMLmUkfdn/yo8dKjEeMPJQ+YlBpqcGMECgYBZ\n"
"rmIaxV2yC9b8AX8khOS7pCgG7opkepGZdMp6aJF8WjcdUgwO4lmdFSIAe4OQgd1Y\n"
"WUq8Dlr2PZpQnSz/SJC3DZxISksggf5sBw06u6iHfyc6C2GNccAgcylljM+4NN42\n"
"+TCswi9vUpwIb/qYKkW+WyZcyLe5mrbXYhhdlrNn0QKBgDe8aRG+MOSUTTXjAVss\n"
"bDY0Us943FN91qBmagNqDyozKAAqDoKvdRxM0IlIDnOptj4AfbpJ1JThNOJDYBpU\n"
"+Azo8UoedANgndtZ2n11RSjmlQ6TE/WGlsirHExqr6y/l71znoQm1y3E2cArbsmy\n"
"hp0P5v42PKxmAx4pR0EjNKsd\n"
"-----END PRIVATE KEY-----\n"
"Bag Attributes\n"
" localKeyID: 01 00 00 00 \n"
" 1.3.6.1.4.1.311.17.3.71: 61 00 6E 00 74 00 6B 00 2D 00 6C 00 61 00 70 00 "
"74 00 6F 00 70 00 00 00 \n"
"subject=CN = azure-identity-test\n"
"\n"
"issuer=CN = azure-identity-test\n"
"\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+XRkzANBgkqhkiG9w0BAQsFADAe\n"
"MRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0MCAXDTIyMDQyMjE1MDYwNloY\n"
"DzIyMjIwMTAxMDcwMDAwWjAeMRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3ZuKbpDu7oBMfMF65qO\n"
"FSBKInKe8N0LBCRgNmzMfZxzL8LoBueLdeEKX6gUGEFi3i9R5qXA3or1Q/teWV3h\n"
"iwj1WQR4aGPGVhom34QAM6kND/QmtZMnY7weLiXBJxf0WLUL+p+jsJnTtcCdtiTX\n"
"EZTLWapp2/0NCJ9n41xG3ZfOfxmZWMzEEXcnyNMq4kkQXGFdpINM3lwsX5grwd62\n"
"+iNSqaFBR5ZHh7ZHg8JtFR1BLeB8/IIXAdNLSOXktnx9qz5CDUCnOvtEFAtiiAkA\n"
"vhsybGA28EDmqOVYZPNB+S0bjPTXc7/n1N5S55LWAoF4C/QF+C/0fWeD87bmqP6m\n"
"0QIDAQABo3AwbjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIG\n"
"CCsGAQUFBwMBMB4GA1UdEQQXMBWCE2F6dXJlLWlkZW50aXR5LXRlc3QwHQYDVR0O\n"
"BBYEFCoJ5tInmafyNuR0tGxZOz522jlWMA0GCSqGSIb3DQEBCwUAA4IBAQBzLXpw\n"
"Xmrg1sQTmzMnS24mREKxj9B3YILmgsdBMrHkH07QUROee7IbQ8gfBKeln0dEcfYi\n"
"Jyh42jn+fmg9AR17RP80wPthD2eKOt4WYNkNM3H8U4JEo+0ML0jZyswynpR48h/E\n"
"m96sm/NUeKUViD5iVTb1uHL4j8mQAN1IbXcunXvrrek1CzFVn5Rpah0Tn+6cYVKd\n"
"Jg531i53udzusgZtV1NPZ82tzYkPQG1vxB//D9vd0LzmcfCvT50MKhz0r/c5yJYk\n"
"i9q94DBuzMhe+O9j+Ob2pVQt5akVFJVtIVSfBZzRBAd66u9JeADlT4sxwS4QAUHi\n"
"RrCsEpJsnJXkx/6O\n"
"-----END CERTIFICATE-----\n";
// cspell:enable
cert << // cspell:disable
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPdm4pukO7ugEx\n"
"8wXrmo4VIEoicp7w3QsEJGA2bMx9nHMvwugG54t14QpfqBQYQWLeL1HmpcDeivVD\n"
"+15ZXeGLCPVZBHhoY8ZWGibfhAAzqQ0P9Ca1kydjvB4uJcEnF/RYtQv6n6OwmdO1\n"
"wJ22JNcRlMtZqmnb/Q0In2fjXEbdl85/GZlYzMQRdyfI0yriSRBcYV2kg0zeXCxf\n"
"mCvB3rb6I1KpoUFHlkeHtkeDwm0VHUEt4Hz8ghcB00tI5eS2fH2rPkINQKc6+0QU\n"
"C2KICQC+GzJsYDbwQOao5Vhk80H5LRuM9Ndzv+fU3lLnktYCgXgL9AX4L/R9Z4Pz\n"
"tuao/qbRAgMBAAECggEBAMQZIrooiTuZ7uVC3Ja96Y1IjyqOg3QSzAXnSFZJcuVM\n"
"i4hayC02khkjVUXjvtLKg2SW/+hvRqZUXM8cfCsm1Tkxh4/T7OhnXyMl5xahU/uA\n"
"0IsC8c/xv2rDdxeRskh8mQd8Yk1MtlIIpRgIcEqp+exxY+FmdldtkvNSkcVUBNwQ\n"
"nXi+oWPhE2guo2g1BPk2gbF0+3FvSrQ8QwGHg+uQJwrQpJ+SB9TyuQFauGR5/wSq\n"
"H93cFH5YC/+v5I7qW6ZQe0f7rEKQDybGVzkBlKJyGCVYmPn7Xa/wJriws+FZIfHz\n"
"f3m0kJigxJd/HwTrnKSg+H8oBgng7lZLdBYWHMGJhA0CgYEA48moW7szegvfLuUF\n"
"a0sHfyKuNyvOv7Wud4sa0lwdKPHS+atwL6TNUWCAGkomYADEe3qiYgMXDX9U3hlW\n";
// cspell:enable
cert << // cspell:disable
"6zktYFj03tnRg4iBjp8nchLBVLf3Wd5TPRw1VKu4ZW43y8BRhYWV+3Z4s1nyMEDA\n"
"NFbKRmL7LDB05oWHdJMjFK/L6YcCgYEA6ShV4v2RQiXzkW6GHSBZDIVHCeWwvIld\n"
"OlEfG7wzZW4e8wNDhfSMtXyJrzfbEyXBtVKoESdP6Nnm9W7ftcynW965S94THuy7\n"
"+ofvHo6JAm8g/0uX70wZ26LU8qhkJMTWmsONBNKLwUzkFT7VGsdaBliam1RLvjeT\n"
"URdQgnftIucCgYEA4FYamT0k1W4bv/OOAr1CBNQDABME64ni6Zj2MXbGwSxou7s8\n"
"IbANBbgkcb/VS3d2CqYchqrEaWaeDp6mG8OUDO+POmsLDJ/D+NKF5rLR9L25vahY\n"
"EjdVzq3QTRTfnqspnnaR37Yt6XUMMLmUkfdn/yo8dKjEeMPJQ+YlBpqcGMECgYBZ\n"
"rmIaxV2yC9b8AX8khOS7pCgG7opkepGZdMp6aJF8WjcdUgwO4lmdFSIAe4OQgd1Y\n"
"WUq8Dlr2PZpQnSz/SJC3DZxISksggf5sBw06u6iHfyc6C2GNccAgcylljM+4NN42\n"
"+TCswi9vUpwIb/qYKkW+WyZcyLe5mrbXYhhdlrNn0QKBgDe8aRG+MOSUTTXjAVss\n"
"bDY0Us943FN91qBmagNqDyozKAAqDoKvdRxM0IlIDnOptj4AfbpJ1JThNOJDYBpU\n"
"+Azo8UoedANgndtZ2n11RSjmlQ6TE/WGlsirHExqr6y/l71znoQm1y3E2cArbsmy\n"
"hp0P5v42PKxmAx4pR0EjNKsd\n";
// cspell:enable
cert << // cspell:disable
"-----END PRIVATE KEY-----\n"
"Bag Attributes\n"
" localKeyID: 01 00 00 00 \n"
" 1.3.6.1.4.1.311.17.3.71: 61 00 6E 00 74 00 6B 00 2D 00 6C 00 61 00 70 00 "
"74 00 6F 00 70 00 00 00 \n"
"subject=CN = azure-identity-test\n"
"\n"
"issuer=CN = azure-identity-test\n"
"\n"
"-----BEGIN CERTIFICATE-----\n";
// cspell:enable
cert << // cspell:disable
"MIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+XRkzANBgkqhkiG9w0BAQsFADAe\n"
"MRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0MCAXDTIyMDQyMjE1MDYwNloY\n"
"DzIyMjIwMTAxMDcwMDAwWjAeMRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3ZuKbpDu7oBMfMF65qO\n"
"FSBKInKe8N0LBCRgNmzMfZxzL8LoBueLdeEKX6gUGEFi3i9R5qXA3or1Q/teWV3h\n"
"iwj1WQR4aGPGVhom34QAM6kND/QmtZMnY7weLiXBJxf0WLUL+p+jsJnTtcCdtiTX\n"
"EZTLWapp2/0NCJ9n41xG3ZfOfxmZWMzEEXcnyNMq4kkQXGFdpINM3lwsX5grwd62\n"
"+iNSqaFBR5ZHh7ZHg8JtFR1BLeB8/IIXAdNLSOXktnx9qz5CDUCnOvtEFAtiiAkA\n"
"vhsybGA28EDmqOVYZPNB+S0bjPTXc7/n1N5S55LWAoF4C/QF+C/0fWeD87bmqP6m\n";
// cspell:enable
cert << // cspell:disable
"0QIDAQABo3AwbjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIG\n"
"CCsGAQUFBwMBMB4GA1UdEQQXMBWCE2F6dXJlLWlkZW50aXR5LXRlc3QwHQYDVR0O\n"
"BBYEFCoJ5tInmafyNuR0tGxZOz522jlWMA0GCSqGSIb3DQEBCwUAA4IBAQBzLXpw\n"
"Xmrg1sQTmzMnS24mREKxj9B3YILmgsdBMrHkH07QUROee7IbQ8gfBKeln0dEcfYi\n"
"Jyh42jn+fmg9AR17RP80wPthD2eKOt4WYNkNM3H8U4JEo+0ML0jZyswynpR48h/E\n"
"m96sm/NUeKUViD5iVTb1uHL4j8mQAN1IbXcunXvrrek1CzFVn5Rpah0Tn+6cYVKd\n"
"Jg531i53udzusgZtV1NPZ82tzYkPQG1vxB//D9vd0LzmcfCvT50MKhz0r/c5yJYk\n"
"i9q94DBuzMhe+O9j+Ob2pVQt5akVFJVtIVSfBZzRBAd66u9JeADlT4sxwS4QAUHi\n"
"RrCsEpJsnJXkx/6O\n"
"-----END CERTIFICATE-----\n";
else if (format == RsaRaw)
cert << // cspell:disable
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpAIBAAKCAQEAz3ZuKbpDu7oBMfMF65qOFSBKInKe8N0LBCRgNmzMfZxzL8Lo\n"
"BueLdeEKX6gUGEFi3i9R5qXA3or1Q/teWV3hiwj1WQR4aGPGVhom34QAM6kND/Qm\n"
"tZMnY7weLiXBJxf0WLUL+p+jsJnTtcCdtiTXEZTLWapp2/0NCJ9n41xG3ZfOfxmZ\n"
"WMzEEXcnyNMq4kkQXGFdpINM3lwsX5grwd62+iNSqaFBR5ZHh7ZHg8JtFR1BLeB8\n"
"/IIXAdNLSOXktnx9qz5CDUCnOvtEFAtiiAkAvhsybGA28EDmqOVYZPNB+S0bjPTX\n"
"c7/n1N5S55LWAoF4C/QF+C/0fWeD87bmqP6m0QIDAQABAoIBAQDEGSK6KIk7me7l\n"
"QtyWvemNSI8qjoN0EswF50hWSXLlTIuIWsgtNpIZI1VF477SyoNklv/ob0amVFzP\n"
"HHwrJtU5MYeP0+zoZ18jJecWoVP7gNCLAvHP8b9qw3cXkbJIfJkHfGJNTLZSCKUY\n"
"CHBKqfnscWPhZnZXbZLzUpHFVATcEJ14vqFj4RNoLqNoNQT5NoGxdPtxb0q0PEMB\n"
"h4PrkCcK0KSfkgfU8rkBWrhkef8Eqh/d3BR+WAv/r+SO6lumUHtH+6xCkA8mxlc5\n"
"AZSichglWJj5+12v8Ca4sLPhWSHx8395tJCYoMSXfx8E65ykoPh/KAYJ4O5WS3QW\n"
"FhzBiYQNAoGBAOPJqFu7M3oL3y7lBWtLB38irjcrzr+1rneLGtJcHSjx0vmrcC+k\n"
"zVFggBpKJmAAxHt6omIDFw1/VN4ZVus5LWBY9N7Z0YOIgY6fJ3ISwVS391neUz0c\n"
"NVSruGVuN8vAUYWFlft2eLNZ8jBAwDRWykZi+ywwdOaFh3STIxSvy+mHAoGBAOko\n"
"VeL9kUIl85Fuhh0gWQyFRwnlsLyJXTpRHxu8M2VuHvMDQ4X0jLV8ia832xMlwbVS\n"
"qBEnT+jZ5vVu37XMp1veuUveEx7su/qH7x6OiQJvIP9Ll+9MGdui1PKoZCTE1prD\n"
"jQTSi8FM5BU+1RrHWgZYmptUS743k1EXUIJ37SLnAoGBAOBWGpk9JNVuG7/zjgK9\n"
"QgTUAwATBOuJ4umY9jF2xsEsaLu7PCGwDQW4JHG/1Ut3dgqmHIaqxGlmng6ephvD\n"
"lAzvjzprCwyfw/jSheay0fS9ub2oWBI3Vc6t0E0U356rKZ52kd+2Lel1DDC5lJH3\n"
"Z/8qPHSoxHjDyUPmJQaanBjBAoGAWa5iGsVdsgvW/AF/JITku6QoBu6KZHqRmXTK\n"
"emiRfFo3HVIMDuJZnRUiAHuDkIHdWFlKvA5a9j2aUJ0s/0iQtw2cSEpLIIH+bAcN\n"
"Oruoh38nOgthjXHAIHMpZYzPuDTeNvkwrMIvb1KcCG/6mCpFvlsmXMi3uZq212IY\n"
"XZazZ9ECgYA3vGkRvjDklE014wFbLGw2NFLPeNxTfdagZmoDag8qMygAKg6Cr3Uc\n"
"TNCJSA5zqbY+AH26SdSU4TTiQ2AaVPgM6PFKHnQDYJ3bWdp9dUUo5pUOkxP1hpbI\n"
"qxxMaq+sv5e9c56EJtctxNnAK27JsoadD+b+NjysZgMeKUdBIzSrHQ==\n"
"-----END RSA PRIVATE KEY-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+XRkzANBgkqhkiG9w0BAQsFADAe\n"
"MRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0MCAXDTIyMDQyMjE1MDYwNloY\n"
"DzIyMjIwMTAxMDcwMDAwWjAeMRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3ZuKbpDu7oBMfMF65qO\n"
"FSBKInKe8N0LBCRgNmzMfZxzL8LoBueLdeEKX6gUGEFi3i9R5qXA3or1Q/teWV3h\n"
"iwj1WQR4aGPGVhom34QAM6kND/QmtZMnY7weLiXBJxf0WLUL+p+jsJnTtcCdtiTX\n"
"EZTLWapp2/0NCJ9n41xG3ZfOfxmZWMzEEXcnyNMq4kkQXGFdpINM3lwsX5grwd62\n"
"+iNSqaFBR5ZHh7ZHg8JtFR1BLeB8/IIXAdNLSOXktnx9qz5CDUCnOvtEFAtiiAkA\n"
"vhsybGA28EDmqOVYZPNB+S0bjPTXc7/n1N5S55LWAoF4C/QF+C/0fWeD87bmqP6m\n"
"0QIDAQABo3AwbjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIG\n"
"CCsGAQUFBwMBMB4GA1UdEQQXMBWCE2F6dXJlLWlkZW50aXR5LXRlc3QwHQYDVR0O\n"
"BBYEFCoJ5tInmafyNuR0tGxZOz522jlWMA0GCSqGSIb3DQEBCwUAA4IBAQBzLXpw\n"
"Xmrg1sQTmzMnS24mREKxj9B3YILmgsdBMrHkH07QUROee7IbQ8gfBKeln0dEcfYi\n"
"Jyh42jn+fmg9AR17RP80wPthD2eKOt4WYNkNM3H8U4JEo+0ML0jZyswynpR48h/E\n"
"m96sm/NUeKUViD5iVTb1uHL4j8mQAN1IbXcunXvrrek1CzFVn5Rpah0Tn+6cYVKd\n"
"Jg531i53udzusgZtV1NPZ82tzYkPQG1vxB//D9vd0LzmcfCvT50MKhz0r/c5yJYk\n"
"i9q94DBuzMhe+O9j+Ob2pVQt5akVFJVtIVSfBZzRBAd66u9JeADlT4sxwS4QAUHi\n"
"RrCsEpJsnJXkx/6O\n"
"-----END CERTIFICATE-----";
// cspell:enable
}

View File

@ -3,6 +3,13 @@
"version-string": "1.0.0",
"dependencies": [
"azure-core-cpp",
"openssl"
{
"name": "openssl",
"platform": "!windows & !uwp"
},
{
"name": "wil",
"platform": "windows"
}
]
}

View File

@ -6,7 +6,11 @@
include(CMakeFindDependencyMacro)
find_dependency(azure-core-cpp)
find_dependency(OpenSSL)
if (WIN32)
find_dependency(wil)
else()
find_dependency(OpenSSL)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/azure-identity-cppTargets.cmake")

View File

@ -16,7 +16,14 @@
"default-features": false,
"version>=": "1.9.0"
},
"openssl",
{
"name": "openssl",
"platform": "!windows & !uwp"
},
{
"name": "wil",
"platform": "windows"
},
{
"name": "vcpkg-cmake",
"host": true