Add support for passing in the x509 certificate and its corresponding private key directly to ClientCertificateCredential, rather than reading from a pem file. (#5989)
* Add support for passing in the x509 certificate and its corresponding private key directly to , rather than reading from a pem file. * Move the x509 and pkey objects * Add basic test. * Fix doc comment due to merge. * Fix merge, add back the bool * Pass in bool in other locations * Fix finding pem cert content from memory and add tests with send chain true. * Use d2i_PrivateKey_bio instead of PEM_read_bio_PrivateKey since the private key isn't in pem format. * Fix doc comments to match the type name rather than copy/paste typo from client secret cred. * Make options optional and add invalid content tests. * Disable cspell in some places within tests. * Make exception message consistent between platforms when reading a file.
This commit is contained in:
parent
e1afe4d7ea
commit
639fc9f594
@ -5,6 +5,7 @@
|
||||
### Features Added
|
||||
|
||||
- Added support for providing an object ID to `ManagedIdentityCredential`.
|
||||
- Added support for passing in the x509 certificate and its corresponding private key directly to `ClientCertificateCredential`, rather than reading from a pem file.
|
||||
- Added support for sending an x5c parameter in `ClientCertificateCredential`.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@ -96,9 +96,19 @@ namespace Azure { namespace Identity {
|
||||
bool sendCertificateChain,
|
||||
Core::Credentials::TokenCredentialOptions const& options);
|
||||
|
||||
explicit ClientCertificateCredential(
|
||||
std::string tenantId,
|
||||
std::string const& clientId,
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey,
|
||||
std::string const& authorityHost,
|
||||
std::vector<std::string> additionallyAllowedTenants,
|
||||
bool sendCertificateChain,
|
||||
Core::Credentials::TokenCredentialOptions const& options);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a Client Secret Credential.
|
||||
* @brief Constructs a Client Certificate Credential.
|
||||
*
|
||||
* @param tenantId Tenant ID.
|
||||
* @param clientId Client ID.
|
||||
@ -114,7 +124,25 @@ namespace Azure { namespace Identity {
|
||||
= Core::Credentials::TokenCredentialOptions());
|
||||
|
||||
/**
|
||||
* @brief Constructs a Client Secret Credential.
|
||||
* @brief Constructs a Client Certificate Credential.
|
||||
*
|
||||
* @param tenantId Tenant ID.
|
||||
* @param clientId Client ID.
|
||||
* @param clientCertificate The x509 certificate which is used for signing, in base64 string
|
||||
* format, including the begin and end headers.
|
||||
* @param privateKey The binary representation of the corresponding RSA private key of the
|
||||
* certificate.
|
||||
* @param options Options for token retrieval.
|
||||
*/
|
||||
explicit ClientCertificateCredential(
|
||||
std::string tenantId,
|
||||
std::string const& clientId,
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey,
|
||||
ClientCertificateCredentialOptions const& options = {});
|
||||
|
||||
/**
|
||||
* @brief Constructs a Client Certificate Credential.
|
||||
*
|
||||
* @param tenantId Tenant ID.
|
||||
* @param clientId Client ID.
|
||||
|
||||
@ -77,11 +77,21 @@ template <typename T> std::vector<uint8_t> ToUInt8Vector(T const& in)
|
||||
return outVec;
|
||||
}
|
||||
|
||||
std::string FindPemCertificateContent(std::string const& path)
|
||||
std::string FindPemCertificateContent(
|
||||
std::string const& path,
|
||||
std::vector<uint8_t> clientCertificate)
|
||||
{
|
||||
auto pemContent{FileBodyStream(path).ReadToEnd()};
|
||||
std::string pem{pemContent.begin(), pemContent.end()};
|
||||
pemContent = {};
|
||||
std::string pem{};
|
||||
if (clientCertificate.empty())
|
||||
{
|
||||
auto pemContent{FileBodyStream(path).ReadToEnd()};
|
||||
pem = std::string{pemContent.begin(), pemContent.end()};
|
||||
pemContent = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
pem = std::string{clientCertificate.begin(), clientCertificate.end()};
|
||||
}
|
||||
|
||||
const std::string beginHeader = std::string("-----BEGIN CERTIFICATE-----");
|
||||
auto headerStart = pem.find(beginHeader);
|
||||
@ -113,6 +123,50 @@ using CertificateThumbprint = std::vector<unsigned char>;
|
||||
using UniquePrivateKey = Azure::Identity::_detail::UniquePrivateKey;
|
||||
using PrivateKey = decltype(std::declval<UniquePrivateKey>().get());
|
||||
|
||||
std::string GetJwtToken(
|
||||
CertificateThumbprint mdVec,
|
||||
bool sendCertificateChain,
|
||||
std::string const& clientCertificatePath,
|
||||
std::vector<uint8_t> clientCertificate = {})
|
||||
{
|
||||
std::string thumbprintHexStr;
|
||||
std::string thumbprintBase64Str;
|
||||
|
||||
// Get thumbprint as hex string:
|
||||
{
|
||||
std::ostringstream thumbprintStream;
|
||||
for (const auto md : mdVec)
|
||||
{
|
||||
thumbprintStream << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
|
||||
<< static_cast<int>(md);
|
||||
}
|
||||
thumbprintHexStr = thumbprintStream.str();
|
||||
}
|
||||
|
||||
// Get thumbprint as Base64:
|
||||
thumbprintBase64Str = Base64Url::Base64UrlEncode(ToUInt8Vector(mdVec));
|
||||
|
||||
std::string x5cHeaderParam{};
|
||||
if (sendCertificateChain)
|
||||
{
|
||||
// Since there is only one base64 encoded cert string, it can be written as a JSON string rather
|
||||
// than a JSON array of strings.
|
||||
x5cHeaderParam = ",\"x5c\":\"";
|
||||
std::string certContent = FindPemCertificateContent(clientCertificatePath, clientCertificate);
|
||||
x5cHeaderParam += certContent;
|
||||
x5cHeaderParam += "\"";
|
||||
}
|
||||
|
||||
// Form a JWT token:
|
||||
const auto tokenHeader = std::string("{\"x5t\":\"") + thumbprintBase64Str + "\",\"kid\":\""
|
||||
+ thumbprintHexStr + "\",\"alg\":\"RS256\",\"typ\":\"JWT\"" + x5cHeaderParam + "}";
|
||||
|
||||
const auto tokenHeaderVec
|
||||
= std::vector<std::string::value_type>(tokenHeader.begin(), tokenHeader.end());
|
||||
|
||||
return Base64Url::Base64UrlEncode(ToUInt8Vector(tokenHeaderVec));
|
||||
}
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS) && (!defined(WINAPI_PARTITION_DESKTOP) || WINAPI_PARTITION_DESKTOP)
|
||||
enum PrivateKeyType
|
||||
{
|
||||
@ -143,6 +197,18 @@ CertificateThumbprint GetThumbprint(PCCERT_CONTEXT cert)
|
||||
return thumbprint;
|
||||
}
|
||||
|
||||
wil::unique_cert_context ImportPemCertificate(std::vector<uint8_t> clientCertificate)
|
||||
{
|
||||
std::string certStr(clientCertificate.begin(), clientCertificate.end());
|
||||
auto certBuffer = PemToBinary(certStr.c_str(), static_cast<DWORD>(certStr.size()));
|
||||
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};
|
||||
}
|
||||
|
||||
wil::unique_cert_context ImportPemCertificate(std::string const& pem)
|
||||
{
|
||||
auto headerStart = pem.find("-----BEGIN CERTIFICATE-----");
|
||||
@ -261,6 +327,18 @@ UniquePrivateKey ImportPemPrivateKey(std::string const& pem)
|
||||
throw AuthenticationException("Invalid private key.");
|
||||
}
|
||||
|
||||
std::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey)
|
||||
{
|
||||
auto certContext = ImportPemCertificate(clientCertificate);
|
||||
|
||||
// We only support the RSA private key type.
|
||||
return std::make_tuple(
|
||||
GetThumbprint(certContext.get()),
|
||||
ImportRsaPrivateKey(privateKey.data(), static_cast<DWORD>(privateKey.size())));
|
||||
}
|
||||
|
||||
std::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(std::string const& path)
|
||||
{
|
||||
auto pemContent{FileBodyStream(path).ReadToEnd()};
|
||||
@ -327,13 +405,76 @@ template <> struct UniqueHandleHelper<EVP_MD_CTX>
|
||||
template <typename T>
|
||||
using UniqueHandle = Azure::Core::_internal::UniqueHandle<T, UniqueHandleHelper>;
|
||||
|
||||
std::tuple<CertificateThumbprint, UniquePrivateKey> GetThumbprintAndKey(
|
||||
UniqueHandle<X509> x509,
|
||||
UniquePrivateKey pkey)
|
||||
{
|
||||
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::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey)
|
||||
{
|
||||
// Create a BIO from the private key vector data in memory.
|
||||
UniqueHandle<BIO> bioKey(BIO_new_mem_buf(privateKey.data(), static_cast<int>(privateKey.size())));
|
||||
if (!bioKey)
|
||||
{
|
||||
throw AuthenticationException("Failed to create BIO for the binary private key.");
|
||||
}
|
||||
|
||||
UniquePrivateKey pkey{d2i_PrivateKey_bio(bioKey.get(), nullptr)};
|
||||
if (!pkey)
|
||||
{
|
||||
throw AuthenticationException("Failed to read the binary private key for the certificate.");
|
||||
}
|
||||
|
||||
// Create a BIO from the client certificate vector data in memory.
|
||||
UniqueHandle<BIO> bioCert(
|
||||
BIO_new_mem_buf(clientCertificate.data(), static_cast<int>(clientCertificate.size())));
|
||||
if (!bioCert)
|
||||
{
|
||||
throw AuthenticationException("Failed to create BIO for the client certificate.");
|
||||
}
|
||||
|
||||
UniqueHandle<X509> x509{PEM_read_bio_X509(bioCert.get(), nullptr, nullptr, nullptr)};
|
||||
if (!x509)
|
||||
{
|
||||
std::ignore = BIO_seek(bioCert.get(), 0);
|
||||
x509.reset(PEM_read_bio_X509(bioCert.get(), nullptr, nullptr, nullptr));
|
||||
if (!x509)
|
||||
{
|
||||
throw AuthenticationException("Failed to read X509 section.");
|
||||
}
|
||||
}
|
||||
|
||||
return GetThumbprintAndKey(std::move(x509), std::move(pkey));
|
||||
}
|
||||
|
||||
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.");
|
||||
throw AuthenticationException("Failed to open file for reading. File name: '" + path + "'");
|
||||
}
|
||||
|
||||
UniquePrivateKey pkey{PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)};
|
||||
@ -353,24 +494,7 @@ std::tuple<CertificateThumbprint, UniquePrivateKey> ReadPemCertificate(const std
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return GetThumbprintAndKey(std::move(x509), std::move(pkey));
|
||||
}
|
||||
|
||||
std::vector<unsigned char> SignPkcs1Sha256(PrivateKey key, const uint8_t* data, size_t size)
|
||||
@ -430,76 +554,77 @@ ClientCertificateCredential::ClientCertificateCredential(
|
||||
m_tokenPayloadStaticPart(
|
||||
"\",\"iss\":\"" + clientId + "\",\"sub\":\"" + clientId + "\",\"jti\":\"")
|
||||
{
|
||||
std::string thumbprintHexStr;
|
||||
std::string thumbprintBase64Str;
|
||||
|
||||
CertificateThumbprint mdVec;
|
||||
try
|
||||
{
|
||||
CertificateThumbprint mdVec;
|
||||
try
|
||||
if (clientCertificatePath.empty())
|
||||
{
|
||||
if (clientCertificatePath.empty())
|
||||
{
|
||||
throw AuthenticationException("Certificate file path is empty.");
|
||||
}
|
||||
|
||||
using Azure::Core::_internal::StringExtensions;
|
||||
std::string const PemExtension = ".pem";
|
||||
auto const certFileExtensionStart = clientCertificatePath.find_last_of('.');
|
||||
auto const certFileExtension = certFileExtensionStart != std::string::npos
|
||||
? clientCertificatePath.substr(certFileExtensionStart)
|
||||
: std::string{};
|
||||
|
||||
if (!StringExtensions::LocaleInvariantCaseInsensitiveEqual(certFileExtension, PemExtension))
|
||||
{
|
||||
throw AuthenticationException(
|
||||
"Certificate format"
|
||||
+ (certFileExtension.empty() ? " " : " ('" + certFileExtension + "') ")
|
||||
+ "is not supported. Please convert your certificate to '" + PemExtension + "'.");
|
||||
}
|
||||
|
||||
std::tie(mdVec, m_pkey) = ReadPemCertificate(clientCertificatePath);
|
||||
throw AuthenticationException("Certificate file path is empty.");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
|
||||
using Azure::Core::_internal::StringExtensions;
|
||||
std::string const PemExtension = ".pem";
|
||||
auto const certFileExtensionStart = clientCertificatePath.find_last_of('.');
|
||||
auto const certFileExtension = certFileExtensionStart != std::string::npos
|
||||
? clientCertificatePath.substr(certFileExtensionStart)
|
||||
: std::string{};
|
||||
|
||||
if (!StringExtensions::LocaleInvariantCaseInsensitiveEqual(certFileExtension, PemExtension))
|
||||
{
|
||||
// WIL does not throw AuthenticationException.
|
||||
throw AuthenticationException(
|
||||
std::string("Identity: ClientCertificateCredential: ") + e.what());
|
||||
"Certificate format"
|
||||
+ (certFileExtension.empty() ? " " : " ('" + certFileExtension + "') ")
|
||||
+ "is not supported. Please convert your certificate to '" + PemExtension + "'.");
|
||||
}
|
||||
|
||||
// Get thumbprint as hex string:
|
||||
{
|
||||
std::ostringstream thumbprintStream;
|
||||
for (const auto md : mdVec)
|
||||
{
|
||||
thumbprintStream << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
|
||||
<< static_cast<int>(md);
|
||||
}
|
||||
thumbprintHexStr = thumbprintStream.str();
|
||||
}
|
||||
|
||||
// Get thumbprint as Base64:
|
||||
thumbprintBase64Str = Base64Url::Base64UrlEncode(ToUInt8Vector(mdVec));
|
||||
std::tie(mdVec, m_pkey) = ReadPemCertificate(clientCertificatePath);
|
||||
}
|
||||
|
||||
std::string x5cHeaderParam{};
|
||||
if (sendCertificateChain)
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
// Since there is only one base64 encoded cert string, it can be written as a JSON string rather
|
||||
// than a JSON array of strings.
|
||||
x5cHeaderParam = ",\"x5c\":\"";
|
||||
std::string certContent = FindPemCertificateContent(clientCertificatePath);
|
||||
x5cHeaderParam += certContent;
|
||||
x5cHeaderParam += "\"";
|
||||
// WIL does not throw AuthenticationException.
|
||||
throw AuthenticationException(
|
||||
std::string("Identity: ClientCertificateCredential: ") + e.what());
|
||||
}
|
||||
|
||||
// Form a JWT token:
|
||||
const auto tokenHeader = std::string("{\"x5t\":\"") + thumbprintBase64Str + "\",\"kid\":\""
|
||||
+ thumbprintHexStr + "\",\"alg\":\"RS256\",\"typ\":\"JWT\"" + x5cHeaderParam + "}";
|
||||
m_tokenHeaderEncoded = GetJwtToken(mdVec, sendCertificateChain, clientCertificatePath);
|
||||
}
|
||||
|
||||
const auto tokenHeaderVec
|
||||
= std::vector<std::string::value_type>(tokenHeader.begin(), tokenHeader.end());
|
||||
ClientCertificateCredential::ClientCertificateCredential(
|
||||
std::string tenantId,
|
||||
std::string const& clientId,
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey,
|
||||
std::string const& authorityHost,
|
||||
std::vector<std::string> additionallyAllowedTenants,
|
||||
bool sendCertificateChain,
|
||||
Core::Credentials::TokenCredentialOptions const& options)
|
||||
: TokenCredential("ClientCertificateCredential"),
|
||||
m_clientCredentialCore(tenantId, authorityHost, additionallyAllowedTenants),
|
||||
m_tokenCredentialImpl(std::make_unique<TokenCredentialImpl>(options)),
|
||||
m_requestBody(
|
||||
std::string(
|
||||
"grant_type=client_credentials"
|
||||
"&client_assertion_type="
|
||||
"urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer" // cspell:disable-line
|
||||
"&client_id=")
|
||||
+ Url::Encode(clientId)),
|
||||
m_tokenPayloadStaticPart(
|
||||
"\",\"iss\":\"" + clientId + "\",\"sub\":\"" + clientId + "\",\"jti\":\"")
|
||||
{
|
||||
|
||||
m_tokenHeaderEncoded = Base64Url::Base64UrlEncode(ToUInt8Vector(tokenHeaderVec));
|
||||
CertificateThumbprint mdVec;
|
||||
try
|
||||
{
|
||||
std::tie(mdVec, m_pkey) = ReadPemCertificate(clientCertificate, privateKey);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
// WIL does not throw AuthenticationException.
|
||||
throw AuthenticationException(
|
||||
std::string("Identity: ClientCertificateCredential: ") + e.what());
|
||||
}
|
||||
|
||||
m_tokenHeaderEncoded = GetJwtToken(mdVec, sendCertificateChain, {}, clientCertificate);
|
||||
}
|
||||
|
||||
ClientCertificateCredential::ClientCertificateCredential(
|
||||
@ -534,6 +659,24 @@ ClientCertificateCredential::ClientCertificateCredential(
|
||||
{
|
||||
}
|
||||
|
||||
ClientCertificateCredential::ClientCertificateCredential(
|
||||
std::string tenantId,
|
||||
std::string const& clientId,
|
||||
std::vector<uint8_t> clientCertificate,
|
||||
std::vector<uint8_t> privateKey,
|
||||
ClientCertificateCredentialOptions const& options)
|
||||
: ClientCertificateCredential(
|
||||
tenantId,
|
||||
clientId,
|
||||
clientCertificate,
|
||||
privateKey,
|
||||
options.AuthorityHost,
|
||||
options.AdditionallyAllowedTenants,
|
||||
options.SendCertificateChain,
|
||||
options)
|
||||
{
|
||||
}
|
||||
|
||||
ClientCertificateCredential::~ClientCertificateCredential() = default;
|
||||
|
||||
AccessToken ClientCertificateCredential::GetToken(
|
||||
|
||||
@ -36,17 +36,175 @@ struct TempCertFile final
|
||||
static const char* const Path;
|
||||
~TempCertFile();
|
||||
TempCertFile(CertFormat algorithm = RsaPkcs);
|
||||
TempCertFile(std::string content);
|
||||
};
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& s, char separator);
|
||||
|
||||
std::string ToString(std::vector<uint8_t> const& vec);
|
||||
|
||||
// cspell:disable
|
||||
std::string ExampleValidCertString
|
||||
= "-----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";
|
||||
|
||||
std::string ExampleValidPrivateKeyString
|
||||
= "-----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";
|
||||
// cspell:enable
|
||||
|
||||
std::vector<uint8_t> ExampleValidPrivateKey{
|
||||
48, 130, 4, 164, 2, 1, 0, 2, 130, 1, 1, 0, 207, 118, 110, 41, 186, 67, 187,
|
||||
186, 1, 49, 243, 5, 235, 154, 142, 21, 32, 74, 34, 114, 158, 240, 221, 11, 4, 36,
|
||||
96, 54, 108, 204, 125, 156, 115, 47, 194, 232, 6, 231, 139, 117, 225, 10, 95, 168, 20,
|
||||
24, 65, 98, 222, 47, 81, 230, 165, 192, 222, 138, 245, 67, 251, 94, 89, 93, 225, 139,
|
||||
8, 245, 89, 4, 120, 104, 99, 198, 86, 26, 38, 223, 132, 0, 51, 169, 13, 15, 244,
|
||||
38, 181, 147, 39, 99, 188, 30, 46, 37, 193, 39, 23, 244, 88, 181, 11, 250, 159, 163,
|
||||
176, 153, 211, 181, 192, 157, 182, 36, 215, 17, 148, 203, 89, 170, 105, 219, 253, 13, 8,
|
||||
159, 103, 227, 92, 70, 221, 151, 206, 127, 25, 153, 88, 204, 196, 17, 119, 39, 200, 211,
|
||||
42, 226, 73, 16, 92, 97, 93, 164, 131, 76, 222, 92, 44, 95, 152, 43, 193, 222, 182,
|
||||
250, 35, 82, 169, 161, 65, 71, 150, 71, 135, 182, 71, 131, 194, 109, 21, 29, 65, 45,
|
||||
224, 124, 252, 130, 23, 1, 211, 75, 72, 229, 228, 182, 124, 125, 171, 62, 66, 13, 64,
|
||||
167, 58, 251, 68, 20, 11, 98, 136, 9, 0, 190, 27, 50, 108, 96, 54, 240, 64, 230,
|
||||
168, 229, 88, 100, 243, 65, 249, 45, 27, 140, 244, 215, 115, 191, 231, 212, 222, 82, 231,
|
||||
146, 214, 2, 129, 120, 11, 244, 5, 248, 47, 244, 125, 103, 131, 243, 182, 230, 168, 254,
|
||||
166, 209, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 196, 25, 34, 186, 40, 137, 59,
|
||||
153, 238, 229, 66, 220, 150, 189, 233, 141, 72, 143, 42, 142, 131, 116, 18, 204, 5, 231,
|
||||
72, 86, 73, 114, 229, 76, 139, 136, 90, 200, 45, 54, 146, 25, 35, 85, 69, 227, 190,
|
||||
210, 202, 131, 100, 150, 255, 232, 111, 70, 166, 84, 92, 207, 28, 124, 43, 38, 213, 57,
|
||||
49, 135, 143, 211, 236, 232, 103, 95, 35, 37, 231, 22, 161, 83, 251, 128, 208, 139, 2,
|
||||
241, 207, 241, 191, 106, 195, 119, 23, 145, 178, 72, 124, 153, 7, 124, 98, 77, 76, 182,
|
||||
82, 8, 165, 24, 8, 112, 74, 169, 249, 236, 113, 99, 225, 102, 118, 87, 109, 146, 243,
|
||||
82, 145, 197, 84, 4, 220, 16, 157, 120, 190, 161, 99, 225, 19, 104, 46, 163, 104, 53,
|
||||
4, 249, 54, 129, 177, 116, 251, 113, 111, 74, 180, 60, 67, 1, 135, 131, 235, 144, 39,
|
||||
10, 208, 164, 159, 146, 7, 212, 242, 185, 1, 90, 184, 100, 121, 255, 4, 170, 31, 221,
|
||||
220, 20, 126, 88, 11, 255, 175, 228, 142, 234, 91, 166, 80, 123, 71, 251, 172, 66, 144,
|
||||
15, 38, 198, 87, 57, 1, 148, 162, 114, 24, 37, 88, 152, 249, 251, 93, 175, 240, 38,
|
||||
184, 176, 179, 225, 89, 33, 241, 243, 127, 121, 180, 144, 152, 160, 196, 151, 127, 31, 4,
|
||||
235, 156, 164, 160, 248, 127, 40, 6, 9, 224, 238, 86, 75, 116, 22, 22, 28, 193, 137,
|
||||
132, 13, 2, 129, 129, 0, 227, 201, 168, 91, 187, 51, 122, 11, 223, 46, 229, 5, 107,
|
||||
75, 7, 127, 34, 174, 55, 43, 206, 191, 181, 174, 119, 139, 26, 210, 92, 29, 40, 241,
|
||||
210, 249, 171, 112, 47, 164, 205, 81, 96, 128, 26, 74, 38, 96, 0, 196, 123, 122, 162,
|
||||
98, 3, 23, 13, 127, 84, 222, 25, 86, 235, 57, 45, 96, 88, 244, 222, 217, 209, 131,
|
||||
136, 129, 142, 159, 39, 114, 18, 193, 84, 183, 247, 89, 222, 83, 61, 28, 53, 84, 171,
|
||||
184, 101, 110, 55, 203, 192, 81, 133, 133, 149, 251, 118, 120, 179, 89, 242, 48, 64, 192,
|
||||
52, 86, 202, 70, 98, 251, 44, 48, 116, 230, 133, 135, 116, 147, 35, 20, 175, 203, 233,
|
||||
135, 2, 129, 129, 0, 233, 40, 85, 226, 253, 145, 66, 37, 243, 145, 110, 134, 29, 32,
|
||||
89, 12, 133, 71, 9, 229, 176, 188, 137, 93, 58, 81, 31, 27, 188, 51, 101, 110, 30,
|
||||
243, 3, 67, 133, 244, 140, 181, 124, 137, 175, 55, 219, 19, 37, 193, 181, 82, 168, 17,
|
||||
39, 79, 232, 217, 230, 245, 110, 223, 181, 204, 167, 91, 222, 185, 75, 222, 19, 30, 236,
|
||||
187, 250, 135, 239, 30, 142, 137, 2, 111, 32, 255, 75, 151, 239, 76, 25, 219, 162, 212,
|
||||
242, 168, 100, 36, 196, 214, 154, 195, 141, 4, 210, 139, 193, 76, 228, 21, 62, 213, 26,
|
||||
199, 90, 6, 88, 154, 155, 84, 75, 190, 55, 147, 81, 23, 80, 130, 119, 237, 34, 231,
|
||||
2, 129, 129, 0, 224, 86, 26, 153, 61, 36, 213, 110, 27, 191, 243, 142, 2, 189, 66,
|
||||
4, 212, 3, 0, 19, 4, 235, 137, 226, 233, 152, 246, 49, 118, 198, 193, 44, 104, 187,
|
||||
187, 60, 33, 176, 13, 5, 184, 36, 113, 191, 213, 75, 119, 118, 10, 166, 28, 134, 170,
|
||||
196, 105, 102, 158, 14, 158, 166, 27, 195, 148, 12, 239, 143, 58, 107, 11, 12, 159, 195,
|
||||
248, 210, 133, 230, 178, 209, 244, 189, 185, 189, 168, 88, 18, 55, 85, 206, 173, 208, 77,
|
||||
20, 223, 158, 171, 41, 158, 118, 145, 223, 182, 45, 233, 117, 12, 48, 185, 148, 145, 247,
|
||||
103, 255, 42, 60, 116, 168, 196, 120, 195, 201, 67, 230, 37, 6, 154, 156, 24, 193, 2,
|
||||
129, 128, 89, 174, 98, 26, 197, 93, 178, 11, 214, 252, 1, 127, 36, 132, 228, 187, 164,
|
||||
40, 6, 238, 138, 100, 122, 145, 153, 116, 202, 122, 104, 145, 124, 90, 55, 29, 82, 12,
|
||||
14, 226, 89, 157, 21, 34, 0, 123, 131, 144, 129, 221, 88, 89, 74, 188, 14, 90, 246,
|
||||
61, 154, 80, 157, 44, 255, 72, 144, 183, 13, 156, 72, 74, 75, 32, 129, 254, 108, 7,
|
||||
13, 58, 187, 168, 135, 127, 39, 58, 11, 97, 141, 113, 192, 32, 115, 41, 101, 140, 207,
|
||||
184, 52, 222, 54, 249, 48, 172, 194, 47, 111, 82, 156, 8, 111, 250, 152, 42, 69, 190,
|
||||
91, 38, 92, 200, 183, 185, 154, 182, 215, 98, 24, 93, 150, 179, 103, 209, 2, 129, 128,
|
||||
55, 188, 105, 17, 190, 48, 228, 148, 77, 53, 227, 1, 91, 44, 108, 54, 52, 82, 207,
|
||||
120, 220, 83, 125, 214, 160, 102, 106, 3, 106, 15, 42, 51, 40, 0, 42, 14, 130, 175,
|
||||
117, 28, 76, 208, 137, 72, 14, 115, 169, 182, 62, 0, 125, 186, 73, 212, 148, 225, 52,
|
||||
226, 67, 96, 26, 84, 248, 12, 232, 241, 74, 30, 116, 3, 96, 157, 219, 89, 218, 125,
|
||||
117, 69, 40, 230, 149, 14, 147, 19, 245, 134, 150, 200, 171, 28, 76, 106, 175, 172, 191,
|
||||
151, 189, 115, 158, 132, 38, 215, 45, 196, 217, 192, 43, 110, 201, 178, 134, 157, 15, 230,
|
||||
254, 54, 60, 172, 102, 3, 30, 41, 71, 65, 35, 52, 171, 29, 171};
|
||||
} // namespace
|
||||
|
||||
class GetCredentialName : public ::testing::TestWithParam<CertFormat> {
|
||||
TempCertFile m_certFile{GetParam()};
|
||||
};
|
||||
|
||||
class GetTokenFromCertInMemory : public ::testing::TestWithParam<bool> {
|
||||
public:
|
||||
bool GetSendCertChain() { return GetParam(); }
|
||||
|
||||
std::string GetHeader()
|
||||
{
|
||||
// cspell:disable
|
||||
std::string x5t = "\"V0pIIQwSzNn6vfSTPv-1f7Vt_Pw\"";
|
||||
std::string kid = "\"574A48210C12CCD9FABDF4933EFFB57FB56DFCFC\"";
|
||||
std::string x5c
|
||||
= "\"MIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+"
|
||||
"XRkzANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0MCAXDTIyMDQyMjE1MDYw"
|
||||
"NloYDzIyMjIwMTAxMDcwMDAwWjAeMRwwGgYDVQQDDBNhenVyZS1pZGVudGl0eS10ZXN0MIIBIjANBgkqhkiG9w"
|
||||
"0BAQEFAAOCAQ8AMIIBCgKCAQEAz3ZuKbpDu7oBMfMF65qOFSBKInKe8N0LBCRgNmzMfZxzL8LoBueLdeEKX6gU"
|
||||
"GEFi3i9R5qXA3or1Q/teWV3hiwj1WQR4aGPGVhom34QAM6kND/"
|
||||
"QmtZMnY7weLiXBJxf0WLUL+p+jsJnTtcCdtiTXEZTLWapp2/"
|
||||
"0NCJ9n41xG3ZfOfxmZWMzEEXcnyNMq4kkQXGFdpINM3lwsX5grwd62+iNSqaFBR5ZHh7ZHg8JtFR1BLeB8/"
|
||||
"IIXAdNLSOXktnx9qz5CDUCnOvtEFAtiiAkAvhsybGA28EDmqOVYZPNB+S0bjPTXc7/n1N5S55LWAoF4C/QF+C/"
|
||||
"0fWeD87bmqP6m0QIDAQABo3AwbjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFB"
|
||||
"wMBMB4GA1UdEQQXMBWCE2F6dXJlLWlkZW50aXR5LXRlc3QwHQYDVR0OBBYEFCoJ5tInmafyNuR0tGxZOz522jl"
|
||||
"WMA0GCSqGSIb3DQEBCwUAA4IBAQBzLXpwXmrg1sQTmzMnS24mREKxj9B3YILmgsdBMrHkH07QUROee7IbQ8gfB"
|
||||
"Keln0dEcfYiJyh42jn+fmg9AR17RP80wPthD2eKOt4WYNkNM3H8U4JEo+0ML0jZyswynpR48h/Em96sm/"
|
||||
"NUeKUViD5iVTb1uHL4j8mQAN1IbXcunXvrrek1CzFVn5Rpah0Tn+"
|
||||
"6cYVKdJg531i53udzusgZtV1NPZ82tzYkPQG1vxB//D9vd0LzmcfCvT50MKhz0r/"
|
||||
"c5yJYki9q94DBuzMhe+O9j+Ob2pVQt5akVFJVtIVSfBZzRBAd66u9JeADlT4sxwS4QAUHiRrCsEpJsnJXkx/"
|
||||
"6O\"";
|
||||
// cspell:enable
|
||||
|
||||
if (GetSendCertChain())
|
||||
{
|
||||
return "{\"x5t\":" + x5t + ",\"kid\":" + kid
|
||||
+ ",\"alg\":\"RS256\",\"typ\":\"JWT\","
|
||||
"\"x5c\":"
|
||||
+ x5c + "}";
|
||||
}
|
||||
return "{\"x5t\":" + x5t + ",\"kid\":" + kid + ",\"alg\":\"RS256\",\"typ\":\"JWT\"}";
|
||||
}
|
||||
};
|
||||
|
||||
class GetToken : public ::testing::TestWithParam<std::tuple<TestType, CertFormat, bool>> {
|
||||
public:
|
||||
TestType GetTestType() { return std::get<0>(GetParam()); }
|
||||
@ -129,6 +287,7 @@ public:
|
||||
"6cYVKdJg531i53udzusgZtV1NPZ82tzYkPQG1vxB//D9vd0LzmcfCvT50MKhz0r/"
|
||||
"c5yJYki9q94DBuzMhe+O9j+Ob2pVQt5akVFJVtIVSfBZzRBAd66u9JeADlT4sxwS4QAUHiRrCsEpJsnJXkx/"
|
||||
"6O\"";
|
||||
// cspell:enable
|
||||
|
||||
if (GetSendCertChain())
|
||||
{
|
||||
@ -180,6 +339,23 @@ TEST_P(GetCredentialName, )
|
||||
|
||||
TEST(ClientCertificateCredential, UnsupportedExtension)
|
||||
{
|
||||
try
|
||||
{
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
"doesnotexist.pem");
|
||||
|
||||
EXPECT_TRUE(!"ClientCertificateCredential with missing file is supposed to throw.");
|
||||
}
|
||||
catch (Azure::Core::Credentials::AuthenticationException const& ex)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
ex.what(),
|
||||
std::string("Identity: ClientCertificateCredential: "
|
||||
"Failed to open file for reading. File name: 'doesnotexist.pem'"));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ClientCertificateCredential const cred(
|
||||
@ -218,10 +394,12 @@ TEST(ClientCertificateCredential, UnsupportedExtension)
|
||||
|
||||
try
|
||||
{
|
||||
// cspell:disable
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
"noextension");
|
||||
// cspell:enable
|
||||
|
||||
EXPECT_TRUE(!"ClientCertificateCredential without an extension is supposed to throw.");
|
||||
}
|
||||
@ -249,6 +427,141 @@ TEST(ClientCertificateCredential, UnsupportedExtension)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ClientCertificateCredential, InvalidContentInFile)
|
||||
{
|
||||
{
|
||||
TempCertFile certFile{""};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
{
|
||||
TempCertFile certFile{ExampleValidPrivateKeyString};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
{
|
||||
TempCertFile certFile{ExampleValidCertString};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
|
||||
// cspell:disable
|
||||
std::string invalidContents[]
|
||||
= {"a",
|
||||
"-----BEGIN CERTIFICATE-----\na",
|
||||
"-----BEGIN RSA PRIVATE KEY-----\na",
|
||||
"-----BEGIN RSA PRIVATE KEY-----\na-----BEGIN CERTIFICATE-----\na",
|
||||
"-----BEGIN RSA PRIVATE "
|
||||
"KEY-----\nqxxMaq+sv5e9c56EJtctxNnAK27JsoadD+b+NjysZgMeKUdBIzSrHQ==\n-----END RSA "
|
||||
"PRIVATE KEY-----\n-----BEGIN "
|
||||
"CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+XRkzANBgkqhkiG9w0BAQsFADAe\n----"
|
||||
"-END CERTIFICATE-----"};
|
||||
// cspell:enable
|
||||
|
||||
for (std::string invalidContent : invalidContents)
|
||||
{
|
||||
{
|
||||
TempCertFile certFile{invalidContent};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
{
|
||||
TempCertFile certFile{ExampleValidPrivateKeyString + invalidContent};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
{
|
||||
TempCertFile certFile{ExampleValidCertString + invalidContent};
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
TempCertFile::Path),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ClientCertificateCredential, InvalidContentInMemory)
|
||||
{
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
{},
|
||||
{},
|
||||
{}),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
|
||||
// cspell:disable
|
||||
std::string invalidContents[]
|
||||
= {"a",
|
||||
"-----BEGIN CERTIFICATE-----\na",
|
||||
"-----BEGIN RSA PRIVATE KEY-----\na",
|
||||
"-----BEGIN RSA PRIVATE KEY-----\na-----BEGIN CERTIFICATE-----\na",
|
||||
ExampleValidPrivateKeyString,
|
||||
"-----BEGIN RSA PRIVATE "
|
||||
"KEY-----\nqxxMaq+sv5e9c56EJtctxNnAK27JsoadD+b+NjysZgMeKUdBIzSrHQ==\n-----END RSA "
|
||||
"PRIVATE KEY-----\n-----BEGIN "
|
||||
"CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIQNqa9U3MBxqBF7ksWk+XRkzANBgkqhkiG9w0BAQsFADAe\n----"
|
||||
"-END CERTIFICATE-----"};
|
||||
// cspell:enable
|
||||
|
||||
std::vector<uint8_t> validCert(ExampleValidCertString.begin(), ExampleValidCertString.end());
|
||||
|
||||
for (std::string invalidContent : invalidContents)
|
||||
{
|
||||
std::vector<uint8_t> invalid(invalidContent.begin(), invalidContent.end());
|
||||
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
validCert,
|
||||
invalid,
|
||||
{}),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
invalid,
|
||||
ExampleValidPrivateKey,
|
||||
{}),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
|
||||
EXPECT_THROW(
|
||||
ClientCertificateCredential const cred(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
invalid,
|
||||
invalid,
|
||||
{}),
|
||||
Azure::Core::Credentials::AuthenticationException);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ClientCertificateCredential, GetOptionsFromEnvironment)
|
||||
{
|
||||
{
|
||||
@ -269,6 +582,146 @@ TEST(ClientCertificateCredential, GetOptionsFromEnvironment)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(GetTokenFromCertInMemory, )
|
||||
{
|
||||
auto const actual = CredentialTestHelper::SimulateTokenRequest(
|
||||
[this](auto transport) {
|
||||
ClientCertificateCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
options.SendCertificateChain = GetSendCertChain();
|
||||
|
||||
std::vector<uint8_t> cert(ExampleValidCertString.begin(), ExampleValidCertString.end());
|
||||
|
||||
return std::make_unique<ClientCertificateCredential>(
|
||||
"01234567-89ab-cdef-fedc-ba8976543210",
|
||||
"fedcba98-7654-3210-0123-456789abcdef",
|
||||
cert,
|
||||
ExampleValidPrivateKey,
|
||||
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/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
|
||||
|
||||
EXPECT_EQ(
|
||||
request1.AbsoluteUrl,
|
||||
"https://login.microsoftonline.com/01234567-89ab-cdef-fedc-ba8976543210/oauth2/v2.0/token");
|
||||
|
||||
{
|
||||
// cspell:disable
|
||||
std::string expectedStr1
|
||||
= "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=";
|
||||
|
||||
std::string expectedStr2
|
||||
= "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
|
||||
|
||||
auto expectedBodyStart0 = expectedStr1;
|
||||
auto expectedBodyStart1 = expectedStr2;
|
||||
|
||||
EXPECT_GT(request0.Body.size(), expectedBodyStart0.size());
|
||||
EXPECT_GT(request1.Body.size(), expectedBodyStart1.size());
|
||||
|
||||
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>(expectedBodyStart0.size()));
|
||||
|
||||
EXPECT_NE(request1.Headers.find("Content-Length"), request1.Headers.end());
|
||||
EXPECT_GT(
|
||||
std::stoi(request1.Headers.at("Content-Length")),
|
||||
static_cast<int>(expectedBodyStart1.size()));
|
||||
|
||||
{
|
||||
using Azure::Core::_internal::Base64Url;
|
||||
|
||||
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, '.');
|
||||
|
||||
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);
|
||||
|
||||
std::string ExpectedHeader = GetHeader();
|
||||
|
||||
EXPECT_EQ(header0, ExpectedHeader);
|
||||
EXPECT_EQ(header1, ExpectedHeader);
|
||||
|
||||
std::string 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\":\"";
|
||||
|
||||
EXPECT_EQ(payload0.substr(0, ExpectedPayloadStart.size()), ExpectedPayloadStart);
|
||||
EXPECT_EQ(payload1.substr(0, ExpectedPayloadStart.size()), ExpectedPayloadStart);
|
||||
|
||||
EXPECT_EQ(Base64Url::Base64UrlDecode(signature0).size(), 256);
|
||||
EXPECT_EQ(Base64Url::Base64UrlDecode(signature1).size(), 256);
|
||||
}
|
||||
}
|
||||
|
||||
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_P(GetToken, )
|
||||
{
|
||||
auto const actual = CredentialTestHelper::SimulateTokenRequest(
|
||||
@ -389,6 +842,11 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
GetCredentialName,
|
||||
testing::Values(RsaPkcs, RsaRaw));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ClientCertificateCredential,
|
||||
GetTokenFromCertInMemory,
|
||||
testing::Values(true, false));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ClientCertificateCredential,
|
||||
GetToken,
|
||||
@ -451,127 +909,18 @@ TempCertFile::TempCertFile(CertFormat format)
|
||||
"\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";
|
||||
<< ExampleValidCertString;
|
||||
// cspell:enable
|
||||
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
|
||||
cert << ExampleValidPrivateKeyString << ExampleValidCertString;
|
||||
else if (format == RsaRawReverse)
|
||||
cert << // cspell:disable
|
||||
"-----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"
|
||||
"-----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-----";
|
||||
// cspell:enable
|
||||
cert << ExampleValidCertString << ExampleValidPrivateKeyString;
|
||||
}
|
||||
|
||||
TempCertFile::TempCertFile(std::string content)
|
||||
{
|
||||
std::ofstream cert(Path, std::ios_base::out | std::ios_base::trunc);
|
||||
cert << content;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& s, char separator)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user