[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:
parent
80b6fc6022
commit
b7afe2faf1
5
.gitignore
vendored
5
.gitignore
vendored
@ -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
7
.vscode/cspell.json
vendored
@ -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": [
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,13 @@
|
||||
"version-string": "1.0.0",
|
||||
"dependencies": [
|
||||
"azure-core-cpp",
|
||||
"openssl"
|
||||
{
|
||||
"name": "openssl",
|
||||
"platform": "!windows & !uwp"
|
||||
},
|
||||
{
|
||||
"name": "wil",
|
||||
"platform": "windows"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user