diff --git a/sdk/keyvault/CMakeLists.txt b/sdk/keyvault/CMakeLists.txt index 15cb3fc09..dcd5f5ff0 100644 --- a/sdk/keyvault/CMakeLists.txt +++ b/sdk/keyvault/CMakeLists.txt @@ -14,3 +14,4 @@ endif() add_subdirectory(azure-security-keyvault-keys) add_subdirectory(azure-security-keyvault-secrets) +add_subdirectory(azure-security-keyvault-certificates) diff --git a/sdk/keyvault/azure-security-keyvault-certificates/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-certificates/CHANGELOG.md new file mode 100644 index 000000000..8b56bf79b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/CHANGELOG.md @@ -0,0 +1,7 @@ +# Release History + +## 4.0.0-beta.1 (Unreleased) + +### New Features + +- Added `CertificateClient` with `GetCertificate()` and `GetCertificateVersion()` APIs. diff --git a/sdk/keyvault/azure-security-keyvault-certificates/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-certificates/CMakeLists.txt new file mode 100644 index 000000000..56e324f74 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) +project(azure-security-keyvault-certificates LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake-modules") + +include(AzureVcpkg) +include(AzureVersion) +include(AzureCodeCoverage) +include(AzureTransportAdapters) +include(AzureDoxygen) +include(AzureGlobalCompileOptions) + +az_vcpkg_integrate() + +if(NOT AZ_ALL_LIBRARIES) + find_package(azure-core-cpp "1.2.0" CONFIG QUIET) + if(NOT azure-core-cpp_FOUND) + find_package(azure-core-cpp "1.2.0" REQUIRED) + endif() +endif() + +set( + AZURE_KEYVAULT_CERTIFICATES_HEADER + inc/azure/keyvault/certificates/certificate_client.hpp + inc/azure/keyvault/certificates/certificate_client_models.hpp + inc/azure/keyvault/certificates/certificate_client_options.hpp +) + +set( + AZURE_KEYVAULT_CERTIFICATES_SOURCE + src/certificate_client.cpp + src/keyvault_certificate.cpp + src/keyvault_certificates_common_request.cpp +) + +add_library(azure-security-keyvault-certificates + ${AZURE_KEYVAULT_CERTIFICATES_HEADER} ${AZURE_KEYVAULT_CERTIFICATES_SOURCE} +) +add_library(Azure::azure-security-keyvault-certificates ALIAS azure-security-keyvault-certificates) + +target_include_directories( + azure-security-keyvault-certificates + PUBLIC + $ + $ +) + +target_link_libraries(azure-security-keyvault-certificates PUBLIC Azure::azure-core) + +# coverage. Has no effect if BUILD_CODE_COVERAGE is OFF +create_code_coverage(keyvault azure-security-keyvault-certificates azure-security-keyvault-certificates-test) + +get_az_version("${CMAKE_CURRENT_SOURCE_DIR}/src/private/package_version.hpp") +generate_documentation(azure-security-keyvault-certificates ${AZ_LIBRARY_VERSION}) + +if(BUILD_TESTING) + + if (NOT AZ_ALL_LIBRARIES) + include(AddGoogleTest) + enable_testing () + endif() + + add_subdirectory(test/ut) +endif() + +# if (BUILD_PERFORMANCE_TESTS) +# add_subdirectory(test/perf) +# endif() + +# if(BUILD_SAMPLES) +# add_subdirectory(test/samples) +# endif() + +az_vcpkg_export( + azure-security-keyvault-certificates + SECURITY_KEYVAULT_CERTIFICATES + "azure/keyvault/certificates/dll_import_export.hpp" + ) diff --git a/sdk/keyvault/azure-security-keyvault-certificates/NOTICE.txt b/sdk/keyvault/azure-security-keyvault-certificates/NOTICE.txt new file mode 100644 index 000000000..6e1bceb63 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/NOTICE.txt @@ -0,0 +1,32 @@ +azure-sdk-for-cpp + +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. Microsoft makes certain +open source code available at https://3rdpartysource.microsoft.com, or you may +send a check or money order for US $5.00, including the product name, the open +source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the +extent required to debug changes to any libraries licensed under the GNU Lesser +General Public License. + +------------------------------------------------------------------------------ + +Azure SDK for C++ uses third-party libraries or other resources that may be +distributed under licenses different than the Azure SDK for C++ software. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + azcppsdkhelp@microsoft.com + +The attached notices are provided for information only. + diff --git a/sdk/keyvault/azure-security-keyvault-certificates/README.md b/sdk/keyvault/azure-security-keyvault-certificates/README.md new file mode 100644 index 000000000..d959a2bd3 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/README.md @@ -0,0 +1,39 @@ +# Azure Key Vault Certificates client library for C++ + +Azure Key Vault is a cloud service that provides secure storage and automated management of certificates used throughout a cloud application. Multiple certificates, and multiple versions of the same certificate, can be kept in the Azure Key Vault. Each certificate in the vault has a policy associated with it which controls the issuance and lifetime of the certificate, along with actions to be taken as certificates near expiry. + +The Azure Key Vault certificates client library enables programmatically managing certificates, offering methods to get certificates, policies, issuers, and contacts. + +[Source code][certificate_client_src] | [API reference documentation][api_reference] | [Product documentation][keyvault_docs] + +### Additional concepts + + + +[Replaceable HTTP transport adapter](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/core/azure-core#http-transport-adapter) | +[Long-running operations](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/core/azure-core#long-running-operations) | + +### Additional Documentation + +- For more extensive documentation on Azure Key Vault, see the [API reference documentation][keyvault_rest]. + +## Contributing + +See the [CONTRIBUTING.md][contributing] for details on building, testing, and contributing to these libraries. + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) +declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). +Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct][code_of_conduct]. +For more information see the [Code of Conduct FAQ][coc_faq] or contact opencode@microsoft.com with any additional questions or comments. + + +[api_reference]: https://azure.github.io/azure-sdk-for-cpp/keyvault.html +[code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ +[keyvault_docs]: https://docs.microsoft.com/azure/key-vault/ +[keyvault_rest]: https://docs.microsoft.com/rest/api/keyvault/ +[contributing]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ diff --git a/sdk/keyvault/azure-security-keyvault-certificates/cgmanifest.json b/sdk/keyvault/azure-security-keyvault-certificates/cgmanifest.json new file mode 100644 index 000000000..7f2901e70 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/cgmanifest.json @@ -0,0 +1,36 @@ +{ + "Registrations": [ + { + "Component": { + "Type": "git", + "git": { + "RepositoryUrl": "https://github.com/google/googletest", + "CommitHash": "703bd9caab50b139428cea1aaff9974ebee5742e" + } + }, + "DevelopmentDependency": true + }, + { + "Component": { + "Type": "other", + "Other": { + "Name": "clang-format", + "Version": "9.0.0-2", + "DownloadUrl": "https://ubuntu.pkgs.org/18.04/ubuntu-updates-universe-amd64/clang-format-9_9-2~ubuntu18.04.2_amd64.deb.html" + } + }, + "DevelopmentDependency": true + }, + { + "Component": { + "Type": "other", + "Other": { + "Name": "doxygen", + "Version": "1.8.20", + "DownloadUrl": "http://doxygen.nl/files/doxygen-1.8.20-setup.exe" + } + }, + "DevelopmentDependency": true + } + ] +} diff --git a/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client.hpp b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client.hpp new file mode 100644 index 000000000..32f883ff9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client.hpp @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines the Key Vault Certificates client. + * + */ + +#pragma once + +#include "azure/keyvault/certificates/certificate_client_models.hpp" +#include "azure/keyvault/certificates/certificate_client_options.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + + /** + * @brief The CertificateClient provides synchronous methods to manage KeyVaultCertificate in + * Azure Key Vault. + * + * @details The client supports retrieving KeyVaultCertificate. + */ + class CertificateClient +#if !defined(TESTING_BUILD) + final +#endif + { + protected: + // Using a shared pipeline for a client to share it with LRO (like delete key) + Azure::Core::Url m_vaultUrl; + std::string m_apiVersion; + std::shared_ptr m_pipeline; + + public: + /** + * @brief Destructor. + * + */ + virtual ~CertificateClient() = default; + + /** + * @brief Construct a new Key Client object + * + * @param vaultUrl The URL address where the client will send the requests to. + * @param credential The authentication method to use. + * @param options The options to customize the client behavior. + */ + explicit CertificateClient( + std::string const& vaultUrl, + std::shared_ptr credential, + CertificateClientOptions options = CertificateClientOptions()); + + /** + * @brief Construct a new Key Client object from another key client. + * + * @param keyClient An existing key vault key client. + */ + explicit CertificateClient(CertificateClient const& keyClient) = default; + + /** + * @brief Returns the latest version of the KeyVaultCertificate along with its + * CertificatePolicy. + * + * @remark This operation requires the certificates/get permission. + * + * @param name The name of the certificate. + * @param context The context for the operation can be used for request cancellation. + * @return A response containing the certificate and policy as a KeyVaultCertificateWithPolicy + * instance. + */ + Azure::Response GetCertificate( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Returns a specific version of the certificate without its CertificatePolicy. + * + * @details If the version is not set in the options, the latest version is returned. + * + * @remark This operation requires the certificates/get permission. + * + * @param name The name of the certificate. + * @param options Optional parameters for this operation. + * @param context The context for the operation can be used for request cancellation. + * @return A response containing the certificate and policy as a KeyVaultCertificateWithPolicy + * instance. + */ + Azure::Response GetCertificateVersion( + std::string const& name, + GetCertificateOptions const& options = GetCertificateOptions(), + Azure::Core::Context const& context = Azure::Core::Context()) const; + + private: + std::unique_ptr SendRequest( + Azure::Core::Http::Request& request, + Azure::Core::Context const& context) const; + + Azure::Core::Http::Request CreateRequest( + Azure::Core::Http::HttpMethod method, + std::vector const& path = {}, + Azure::Core::IO::BodyStream* content = nullptr) const; + }; +}}}} // namespace Azure::Security::KeyVault::Certificates diff --git a/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_models.hpp b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_models.hpp new file mode 100644 index 000000000..5db82370a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_models.hpp @@ -0,0 +1,805 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines the Key Vault Certificates types. + * + */ + +#pragma once + +#include "azure/keyvault/certificates/dll_import_export.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + + /** + * @brief Contains identity and other basic properties of a Certificate. + * + */ + struct CertificateProperties final + { + // Attributes + + /** + * @brief Indicate when the certificate will be valid and can be used for cryptographic + * operations. + * + */ + Azure::Nullable NotBefore; + + /** + * @brief Indicate when the certificate will expire and cannot be used for cryptographic + * operations. + * + */ + Azure::Nullable ExpiresOn; + + /** + * @brief Indicate when the certificate was created. + * + */ + Azure::Nullable CreatedOn; + + /** + * @brief Indicate when the certificate was updated. + * + */ + Azure::Nullable UpdatedOn; + + /** + * @brief The number of days a certificate is retained before being deleted for a soft + * delete-enabled Key Vault. + * + */ + Azure::Nullable RecoverableDays; + + /** + * @brief The recovery level currently in effect for keys in the Key Vault. + * + * @remark If Purgeable, the certificate can be permanently deleted by an authorized user; + * otherwise, only the service can purge the keys at the end of the retention interval. + * + */ + Azure::Nullable RecoveryLevel; + + // Properties + + /** + * @brief Dictionary of tags with specific metadata about the certificate. + * + */ + std::unordered_map Tags; + + /** + * @brief The name of the certificate. + * + */ + std::string Name; + + /** + * @brief The certificate identifier. + * + */ + std::string Id; + + /** + * @brief The Key Vault base Url. + * + */ + std::string VaultUrl; + + /** + * @brief The version of the certificate. + * + */ + std::string Version; + + /** + * @brief Get the digital thumbprint of the certificate which can be used to uniquely identify + * it. + * + */ + std::vector X509Thumbprint; + + /** + * @brief Indicate whether the certificate is enabled and useable for cryptographic operations. + * + */ + Azure::Nullable Enabled; + + /** + * @brief Construct a new Certificate Properties object + * + */ + CertificateProperties() = default; + + /** + * @brief Construct a new Certificate Properties object + * + * @param name The name of the certificate. + */ + CertificateProperties(std::string const& name) : Name(name) {} + }; + + /** + * @brief An Azure Key Vault certificate. + * + */ + class KeyVaultCertificate { + public: + /** + * @brief Get the identifier of the certificate. + * + */ + std::string KeyId; + + /** + * @brief Get the identifier of the Key Vault Secret which contains the PEM of PFX formatted + * content of the certificate and its private key. + * + */ + std::string SecretId; + + /** + * @brief Additional fields for the certificate. + * + */ + CertificateProperties Properties; + + /** + * @brief Get the CER formatted public X509 certificate. + * + * @remarks This property contains only the public key. + * + */ + std::vector Cer; + + /** + * @brief Get the name of the certificate. + * + * @return The name of the certificate. + */ + std::string const& Name() const { return Properties.Name; } + + /** + * @brief Get the certificate Id, + * + * @return The id of the certificate. + */ + std::string const& Id() const { return Properties.Id; } + + /** + * @brief Construct a new Key Vault Certificate object + * + * @param properties The properties to create a new certificate. + */ + KeyVaultCertificate(CertificateProperties const& properties) : Properties(properties) {} + + /** + * @brief Construct a new Key Vault Certificate object + * + */ + KeyVaultCertificate() = default; + + /** + * @brief Destroy the Key Vault Certificate object + * + */ + virtual ~KeyVaultCertificate() = default; + }; + + /** + * @brief Supported JsonWebKey key types (kty). + * + */ + class CertificateKeyType final { + private: + std::string m_value; + + public: + /** + * @brief Construct a new Certificate Key Type object + * + * @param keyType The type of the certificate. + */ + explicit CertificateKeyType(std::string keyType) : m_value(std::move(keyType)) {} + + /** + * @brief Construct a new Certificate Key Type object + * + */ + CertificateKeyType() = default; + + /** + * @brief Enables using the equal operator for JWT. + * + * @param other A JWT to be compared. + */ + bool operator==(const CertificateKeyType& other) const noexcept + { + return m_value == other.m_value; + } + + /** + * @brief Return the JSON Web Token (JWT) as a string. + * + * @return The JWT represented as string. + */ + std::string const& ToString() const { return m_value; } + + /** + * @brief An Elliptic Curve Cryptographic (ECC) algorithm. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyType Ec; + + /** + * @brief An Elliptic Curve Cryptographic (ECC) algorithm backed by a Hardware Security Module + * (HSM). + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyType EcHsm; + + /** + * @brief An RSA cryptographic algorithm. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyType Rsa; + + /** + * @brief An RSA cryptographic algorithm backed by a Hardware Security Module (HSM). + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyType RsaHsm; + }; + + /** + * @brief Elliptic Curve Cryptography (ECC) curve names. + * + */ + class CertificateKeyCurveName final { + private: + std::string m_value; + + public: + /** + * @brief Construct a new Key Curve Name object. + * + * @param value The string value of the instance. + */ + explicit CertificateKeyCurveName(std::string value) + { + if (value.empty()) + { + throw std::invalid_argument("The value for the curve name can not be empty"); + } + m_value = std::move(value); + } + + /** + * @brief Construct a default key curve. + * + */ + CertificateKeyCurveName() = default; + + /** + * @brief Enables using the equal operator for key curve. + * + * @param other A key curve to be compared. + */ + bool operator==(const CertificateKeyCurveName& other) const noexcept + { + return m_value == other.m_value; + } + + /** + * @brief Get the string value of the key curve. + * + */ + std::string const& ToString() const { return m_value; } + + /** + * @brief Get the NIST P-256 elliptic curve, AKA SECG curve SECP256R1. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyCurveName P256; + + /** + * @brief Get the SECG SECP256K1 elliptic curve. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyCurveName P256K; + + /** + * @brief Get the NIST P-384 elliptic curve, AKA SECG curve SECP384R1. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyCurveName P384; + + /** + * @brief Get the NIST P-521 elliptic curve, AKA SECG curve SECP521R1. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyCurveName P521; + }; + + /** + * @brief A collection of subject alternative names (SANs) for a X.509 certificate. SANs can be + * DNS entries, emails, or unique principal names. + * + */ + struct SubjectAlternativeNames final + { + /** + * @brief Get a collection of DNS names. + * + */ + std::vector DnsNames; + + /** + * @brief Get a collection of email addresses. + * + */ + std::vector Emails; + + /** + * @brief Get a collection of user principal names (UPNs). + * + */ + std::vector UserPrincipalNames; + }; + + /** + * @brief Content type of the certificate when downloaded from getecret. + * + */ + class CertificateContentType final { + private: + std::string m_value; + + public: + /** + * @brief Construct a new Key Curve Name object. + * + * @param value The string value of the instance. + */ + explicit CertificateContentType(std::string value) + { + if (value.empty()) + { + throw std::invalid_argument("The value for the curve name can not be empty"); + } + m_value = std::move(value); + } + + /** + * @brief Construct a default key curve. + * + */ + CertificateContentType() = default; + + /** + * @brief Enables using the equal operator for key curve. + * + * @param other A key curve to be compared. + */ + bool operator==(const CertificateContentType& other) const noexcept + { + return m_value == other.m_value; + } + + /** + * @brief Get the string value of the key curve. + * + */ + std::string const& ToString() const { return m_value; } + + /** + * @brief Get the NIST P-256 elliptic curve, AKA SECG curve SECP256R1. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateContentType Pkcs12; + + /** + * @brief Get the SECG SECP256K1 elliptic curve. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateContentType Pem; + }; + + /** + * @brief Supported usages of a certificate key. + * + */ + class CertificateKeyUsage final { + private: + std::string m_value; + + public: + /** + * @brief Construct a new Key Curve Name object. + * + * @param value The string value of the instance. + */ + explicit CertificateKeyUsage(std::string value) + { + if (value.empty()) + { + throw std::invalid_argument("The value for the curve name can not be empty"); + } + m_value = std::move(value); + } + + /** + * @brief Construct a default key curve. + * + */ + CertificateKeyUsage() = default; + + /** + * @brief Enables using the equal operator for key curve. + * + * @param other A key curve to be compared. + */ + bool operator==(const CertificateKeyUsage& other) const noexcept + { + return m_value == other.m_value; + } + + /** + * @brief Get the string value of the key curve. + * + */ + std::string const& ToString() const { return m_value; } + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be + * used as a digital signatures. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage DigitalSignature; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used for + * authentication. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage NonRepudiation; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used for key + * encryption. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage KeyEncipherment; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used for data + * encryption. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage DataEncipherment; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used to determine + * key agreement, such as a key created using the Diffie-Hellman key agreement algorithm. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage KeyAgreement; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used to sign + * certificates. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage KeyCertSign; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used to sign a + * certificate revocation list. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage CrlSign; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used for + * encryption only. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage EncipherOnly; + + /** + * @brief Get a CertificateKeyUsage indicating that the certificate key can be used for + * decryption only. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificateKeyUsage DecipherOnly; + }; + + /** + * @brief An action that will be executed. + * + */ + class CertificatePolicyAction final { + private: + std::string m_value; + + public: + /** + * @brief Construct a new Key Curve Name object. + * + * @param value The string value of the instance. + */ + explicit CertificatePolicyAction(std::string value) + { + if (value.empty()) + { + throw std::invalid_argument("The value for the curve name can not be empty"); + } + m_value = std::move(value); + } + + /** + * @brief Construct a default key curve. + * + */ + CertificatePolicyAction() = default; + + /** + * @brief Enables using the equal operator for key curve. + * + * @param other A key curve to be compared. + */ + bool operator==(const CertificatePolicyAction& other) const noexcept + { + return m_value == other.m_value; + } + + /** + * @brief Get the string value of the key curve. + * + */ + std::string const& ToString() const { return m_value; } + + /** + * @brief Get the NIST P-256 elliptic curve, AKA SECG curve SECP256R1. + * + * @remark For more information, see + * Curve + * types. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificatePolicyAction + DigitalSignature; + + /** + * @brief Gets a CertificatePolicyAction that will auto-renew a certificate. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificatePolicyAction AutoRenew; + + /** + * @brief Get a CertificatePolicyAction action that will email certificate contacts. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const CertificatePolicyAction EmailContacts; + }; + + /** + * @brief An action to be executed at a prescribed time in a certificates lifecycle. + * + */ + struct LifetimeAction final + { + /** + * @brief Get the CertificatePolicyAction to be performed. + * + */ + CertificatePolicyAction Action; + + /** + * @brief Get the action should be performed the specified number of days before the certificate + * will expire. + * + */ + Azure::Nullable DaysBeforeExpiry; + + /** + * @brief Get the action should be performed when the certificate reaches the specified + * percentage of its lifetime. Valid values include 1-99. + * + */ + Azure::Nullable LifetimePercentage; + }; + + /** + * @brief A policy which governs the lifecycle a properties of a certificate managed by Azure Key + * Vault. + * + */ + struct CertificatePolicy final + { + /** + * @brief Get the type of backing key to be generated when issuing new certificates. + * + */ + Azure::Nullable KeyType; + + /** + * @brief Get a value indicating whether the certificate key should be reused when rotating the + * certificate. + * + */ + Azure::Nullable ReuseKey; + + /** + * @brief Get a value indicating whether the certificate key is exportable from the vault or + * secure certificate store. + * + */ + Azure::Nullable Exportable; + + /** + * @brief Get the curve which back an Elliptic Curve (EC) key. + * + */ + Azure::Nullable KeyCurveName; + + /** + * @brief Get the size of the RSA key. The value must be a valid RSA key length such as 2048 or + * 4092. + * + */ + Azure::Nullable KeySize; + + /** + * @brief Get the subject name of a certificate. + * + */ + std::string Subject; + + /** + * @brief Get the subject alternative names (SANs) of a certificate. + * + */ + struct SubjectAlternativeNames SubjectAlternativeNames; + + /** + * @brief Indicates if the certificates generated under this policy should be published to + * certificate transparency logs. + * + */ + Azure::Nullable CertificateTransparency; + + /** + * @brief Certificate type as supported by the provider (optional); for example 'OV-SSL', + * 'EV-SSL'. + * + */ + Azure::Nullable CertificateType; + + /** + * @brief Name of the referenced issuer object or reserved names; for example, 'Self' or + 'Unknown'. + * + */ + Azure::Nullable IssuerName; + + /** + * @brief Get the CertificateContentType of the certificate. + * + */ + Azure::Nullable ContentType; + + /** + * @brief Get the validity period for a certificate in months. + * + */ + Azure::Nullable ValidityInMonths; + + /** + * @brief Get a value indicating whether the certificate is currently enabled. If null, the + * server default will be used. + * + */ + Azure::Nullable Enabled; + + /** + * @brief Get a DateTime indicating when the certificate was updated. + * + */ + Azure::Nullable UpdatedOn; + + /** + * @brief Get a DateTime indicating when the certificate was created. + * + */ + Azure::Nullable CreatedOn; + + /** + * @brief Gets the allowed usages for the key of the certificate. + * + */ + std::vector KeyUsage; + + /** + * @brief Get the allowed enhanced key usages (EKUs) of the certificate. + * + */ + std::vector EnhancedKeyUsage; + + /** + * @brief Get the actions to be executed at specified times in the certificates lifetime. + * + */ + std::vector LifetimeActions; + }; + + /** + * @brief A KeyVaultCertificate along with its CertificatePolicy. + * + */ + struct KeyVaultCertificateWithPolicy final : public KeyVaultCertificate + { + /** + * @brief Gets the current policy for the certificate. + * + */ + CertificatePolicy Policy; + + /** + * @brief Construct a new Key Vault Certificate With Policy object + * + * @param properties The properties to create a new certificate. + */ + KeyVaultCertificateWithPolicy(CertificateProperties const& properties) + : KeyVaultCertificate(properties) + { + } + }; + + /** + * @brief The options for calling GetCertificate. + * + */ + struct GetCertificateOptions final + { + std::string Version; + }; + +}}}} // namespace Azure::Security::KeyVault::Certificates diff --git a/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_options.hpp b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_options.hpp new file mode 100644 index 000000000..6fcbcc84a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_options.hpp @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines the supported options to create a Key Vault Keys client. + * + */ + +#pragma once + +#include + +#include "azure/keyvault/certificates/certificate_client_models.hpp" +#include "azure/keyvault/certificates/dll_import_export.hpp" + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + + /** + * @brief The API version to use from Key Vault. + * + */ + class ServiceVersion final { + private: + std::string m_version; + + public: + /** + * @brief Construct a new Service Version object + * + * @param version The string version for the Key Vault keys service. + */ + ServiceVersion(std::string version) : m_version(std::move(version)) {} + + /** + * @brief Enable comparing the ext enum. + * + * @param other Another #ServiceVersion to be compared. + */ + bool operator==(ServiceVersion const& other) const { return m_version == other.m_version; } + + /** + * @brief Return the #ServiceVersion string representation. + * + */ + std::string const& ToString() const { return m_version; } + + /** + * @brief Use to send request to the 7.2 version of Key Vault service. + * + */ + AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT static const ServiceVersion V7_2; + }; + + /** + * @brief Define the options to create an SDK Keys client. + * + */ + struct CertificateClientOptions final : public Azure::Core::_internal::ClientOptions + { + ServiceVersion Version; + + /** + * @brief Construct a new Key Client Options object. + * + * @param version Optional version for the client. + */ + CertificateClientOptions(ServiceVersion version = ServiceVersion::V7_2) + : Azure::Core::_internal::ClientOptions(), Version(version) + { + } + }; + +}}}} // namespace Azure::Security::KeyVault::Certificates diff --git a/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/dll_import_export.hpp b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/dll_import_export.hpp new file mode 100644 index 000000000..a3e65be8e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/dll_import_export.hpp @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief DLL export macro. + */ + +// For explanation, see the comment in azure/core/dll_import_export.hpp + +#pragma once + +/** + * @def AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT + * @brief Applies DLL export attribute, when applicable. + * @note See https://docs.microsoft.com/cpp/cpp/dllexport-dllimport?view=msvc-160. + */ + +#if defined(AZ_SECURITY_KEYVAULT_CERTIFICATES_DLL) \ + || (0 /*@AZ_SECURITY_KEYVAULT_CERTIFICATES_DLL_INSTALLED_AS_PACKAGE@*/) +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL 1 +#else +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL 0 +#endif + +#if AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL +#if defined(_MSC_VER) +#if defined(AZ_SECURITY_KEYVAULT_CERTIFICATES_BEING_BUILT) +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT __declspec(dllexport) +#else // !defined(AZ_SECURITY_KEYVAULT_CERTIFICATES_BEING_BUILT) +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT __declspec(dllimport) +#endif // AZ_SECURITY_KEYVAULT_CERTIFICATES_BEING_BUILT +#else // !defined(_MSC_VER) +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT +#endif // _MSC_VER +#else // !AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL +#define AZ_SECURITY_KEYVAULT_CERTIFICATES_DLLEXPORT +#endif // AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL + +#undef AZ_SECURITY_KEYVAULT_CERTIFICATES_BUILT_AS_DLL diff --git a/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/keyvault_certificates.hpp b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/keyvault_certificates.hpp new file mode 100644 index 000000000..027891801 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/keyvault_certificates.hpp @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief Includes all public headers from Azure Key Vault Certificates SDK library. + * + */ + +#pragma once + +#include "azure/keyvault/certificates/dll_import_export.hpp" + +#include "azure/keyvault/certificates/certificate_client.hpp" +#include "azure/keyvault/certificates/certificate_client_models.hpp" +#include "azure/keyvault/certificates/certificate_client_options.hpp" diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/certificate_client.cpp b/sdk/keyvault/azure-security-keyvault-certificates/src/certificate_client.cpp new file mode 100644 index 000000000..4e19a21ae --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/certificate_client.cpp @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure/keyvault/certificates/certificate_client.hpp" + +#include "private/certificate_serializers.hpp" +#include "private/keyvault_certificates_common_request.hpp" +#include "private/package_version.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Certificates; +using namespace Azure::Security::KeyVault::Certificates::_detail; +using namespace Azure; +using namespace Azure::Core; +using namespace Azure::Core::Http; +using namespace Azure::Core::Http::Policies; +using namespace Azure::Core::Http::Policies::_internal; +using namespace Azure::Core::Http::_internal; +using namespace Azure::Security::KeyVault::_detail; + +namespace { +constexpr static const char KeyVaultServicePackageName[] = "keyvault-certificates"; +constexpr static const char CertificatesPath[] = "certificates"; + +// This is a Key-Vault only patch to calculate token scope/audience +std::string GetScopeFromUrl(Azure::Core::Url const& url) +{ + std::string calculatedScope(url.GetScheme() + "://"); + auto const& hostWithAccount = url.GetHost(); + auto hostNoAccountStart = std::find(hostWithAccount.begin(), hostWithAccount.end(), '.'); + + // Insert the calculated scope only when then host in the url contains at least a `.` + // Otherwise, only the default scope will be there. + // We don't want to throw/validate input but just leave the values go to azure to decide what to + // do. + if (hostNoAccountStart != hostWithAccount.end()) + { + calculatedScope.append(hostNoAccountStart + 1, hostWithAccount.end()); + calculatedScope.append("/.default"); + } + + return calculatedScope; +} +} // namespace + +std::unique_ptr CertificateClient::SendRequest( + Azure::Core::Http::Request& request, + Azure::Core::Context const& context) const +{ + return KeyVaultCertificatesCommonRequest::SendRequest(*m_pipeline, request, context); +} + +Request CertificateClient::CreateRequest( + HttpMethod method, + std::vector const& path, + Azure::Core::IO::BodyStream* content) const +{ + return KeyVaultCertificatesCommonRequest::CreateRequest( + m_vaultUrl, m_apiVersion, method, path, content); +} + +CertificateClient::CertificateClient( + std::string const& vaultUrl, + std::shared_ptr credential, + CertificateClientOptions options) + : m_vaultUrl(vaultUrl), m_apiVersion(options.Version.ToString()) +{ + auto apiVersion = options.Version.ToString(); + + std::vector> perRetrypolicies; + { + Azure::Core::Credentials::TokenRequestContext const tokenContext + = {{::GetScopeFromUrl(m_vaultUrl)}}; + + perRetrypolicies.emplace_back( + std::make_unique(credential, std::move(tokenContext))); + } + std::vector> perCallpolicies; + + m_pipeline = std::make_shared( + options, + KeyVaultServicePackageName, + PackageVersion::ToString(), + std::move(perRetrypolicies), + std::move(perCallpolicies)); +} + +Response CertificateClient::GetCertificate( + std::string const& name, + Context const& context) const +{ + auto request = CreateRequest(HttpMethod::Get, {CertificatesPath, name}); + + // Send and parse respone + auto rawResponse = SendRequest(request, context); + auto value + = _detail::KeyVaultCertificateSerializer::KeyVaultCertificateDeserialize(name, *rawResponse); + return Azure::Response(std::move(value), std::move(rawResponse)); +} + +Response CertificateClient::GetCertificateVersion( + std::string const& name, + GetCertificateOptions const& options, + Context const& context) const +{ + // Request with no payload + std::vector path{{CertificatesPath, name}}; + if (!options.Version.empty()) + { + path.emplace_back(options.Version); + } + + auto request = CreateRequest(HttpMethod::Get, std::move(path)); + + // Send and parse respone + auto rawResponse = SendRequest(request, context); + auto value + = _detail::KeyVaultCertificateSerializer::KeyVaultCertificateDeserialize(name, *rawResponse); + return Azure::Response(std::move(value), std::move(rawResponse)); +} + +const ServiceVersion ServiceVersion::V7_2("7.2"); diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificate.cpp b/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificate.cpp new file mode 100644 index 000000000..674c34064 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificate.cpp @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include + +#include "azure/keyvault/certificates/certificate_client_models.hpp" +#include "private/certificate_constants.hpp" +#include "private/certificate_serializers.hpp" + +using namespace Azure::Security::KeyVault::Certificates; +using namespace Azure::Core::Json::_internal; +using namespace Azure::Core::_internal; + +using Azure::Core::_internal::PosixTimeConverter; + +KeyVaultCertificateWithPolicy +_detail::KeyVaultCertificateSerializer::KeyVaultCertificateDeserialize( + std::string const& name, + Azure::Core::Http::RawResponse const& rawResponse) +{ + CertificateProperties properties(name); + auto const& body = rawResponse.GetBody(); + auto jsonResponse = json::parse(body); + using Azure::Core::_internal::PosixTimeConverter; + + // Parse URL for the name, vaultUrl and version + _detail::KeyVaultCertificateSerializer::ParseKeyUrl( + properties, jsonResponse[IdName].get()); + + // x5t + properties.X509Thumbprint = Base64Url::Base64UrlDecode(jsonResponse[X5tName].get()); + + // "Tags" + if (jsonResponse.contains(TagsName)) + { + properties.Tags = jsonResponse[TagsName].get>(); + } + + // "Attributes" + if (jsonResponse.contains(AttributesPropertyName)) + { + auto attributes = jsonResponse[AttributesPropertyName]; + + JsonOptional::SetIfExists(properties.Enabled, attributes, EnabledPropertyName); + + JsonOptional::SetIfExists( + properties.NotBefore, attributes, NbfPropertyName, PosixTimeConverter::PosixTimeToDateTime); + JsonOptional::SetIfExists( + properties.ExpiresOn, attributes, ExpPropertyName, PosixTimeConverter::PosixTimeToDateTime); + JsonOptional::SetIfExists( + properties.CreatedOn, + attributes, + CreatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + JsonOptional::SetIfExists( + properties.UpdatedOn, + attributes, + UpdatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + JsonOptional::SetIfExists(properties.RecoveryLevel, attributes, RecoveryLevelPropertyName); + JsonOptional::SetIfExists(properties.RecoverableDays, attributes, RecoverableDaysPropertyName); + } + + KeyVaultCertificateWithPolicy certificate(std::move(properties)); + + // kid + certificate.KeyId = jsonResponse[KidPropertyName].get(); + // sid + certificate.SecretId = jsonResponse[SidPropertyName].get(); + // cer + certificate.Cer = Base64Url::Base64UrlDecode(jsonResponse[CerPropertyName].get()); + + // policy + if (jsonResponse.contains(PolicyPropertyName)) + { + auto const policyJson = jsonResponse[PolicyPropertyName]; + // key_props + { + auto const keyPropsJson = policyJson[KeyPropsPropertyName]; + JsonOptional::SetIfExists( + certificate.Policy.KeyType, keyPropsJson, KeyTypePropertyName, [](std::string value) { + return CertificateKeyType(value); + }); + JsonOptional::SetIfExists(certificate.Policy.ReuseKey, keyPropsJson, ReuseKeyPropertyName); + JsonOptional::SetIfExists( + certificate.Policy.Exportable, keyPropsJson, ExportablePropertyName); + JsonOptional::SetIfExists( + certificate.Policy.KeyCurveName, + keyPropsJson, + CurveNamePropertyName, + [](std::string value) { return CertificateKeyCurveName(value); }); + JsonOptional::SetIfExists(certificate.Policy.KeySize, keyPropsJson, KeySizePropertyName); + } + // secret_props + { + auto const secretPropsJson = policyJson[SecretPropsPropertyName]; + JsonOptional::SetIfExists( + certificate.Policy.ContentType, + secretPropsJson, + ContentTypePropertyName, + [](std::string value) { return CertificateContentType(value); }); + } + // x509_props + { + auto const x509PropsJson = policyJson[X509PropsPropertyName]; + certificate.Policy.Subject = x509PropsJson[SubjectPropertyName].get(); + JsonOptional::SetIfExists, std::vector>( + certificate.Policy.SubjectAlternativeNames.DnsNames, + x509PropsJson, + DnsPropertyName, + [](std::vector const& values) { return values; }); + JsonOptional::SetIfExists, std::vector>( + certificate.Policy.SubjectAlternativeNames.Emails, + x509PropsJson, + EmailsPropertyName, + [](std::vector const& values) { return values; }); + JsonOptional::SetIfExists, std::vector>( + certificate.Policy.SubjectAlternativeNames.UserPrincipalNames, + x509PropsJson, + UserPrincipalNamesPropertyName, + [](std::vector const& values) { return values; }); + JsonOptional::SetIfExists, std::vector>( + certificate.Policy.KeyUsage, + x509PropsJson, + UserPrincipalNamesPropertyName, + [](std::vector const& values) { + std::vector keyUsage; + for (auto const& item : values) + { + keyUsage.emplace_back(CertificateKeyUsage(item)); + } + return keyUsage; + }); + JsonOptional::SetIfExists, std::vector>( + certificate.Policy.EnhancedKeyUsage, + x509PropsJson, + EkusPropertyName, + [](std::vector const& values) { return values; }); + JsonOptional::SetIfExists( + certificate.Policy.ValidityInMonths, x509PropsJson, ValidityMonthsPropertyName); + } + // issuer + { + auto const issuerJson = policyJson[IssuerPropertyName]; + JsonOptional::SetIfExists(certificate.Policy.IssuerName, issuerJson, IssuerNamePropertyName); + JsonOptional::SetIfExists( + certificate.Policy.CertificateTransparency, issuerJson, CertTransparencyPropertyName); + JsonOptional::SetIfExists(certificate.Policy.CertificateType, issuerJson, CtyPropertyName); + } + // attributes + { + auto const policyAttributesJson = policyJson[AttributesPropertyName]; + JsonOptional::SetIfExists( + certificate.Policy.Enabled, policyAttributesJson, EnabledPropertyName); + JsonOptional::SetIfExists( + certificate.Policy.CreatedOn, + policyAttributesJson, + CreatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + JsonOptional::SetIfExists( + certificate.Policy.UpdatedOn, + policyAttributesJson, + UpdatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + } + // lifetime_actions + { + auto const policyAttributesJson = policyJson[LifetimeActionsPropertyName]; + for (auto const& attributeItem : policyAttributesJson) + { + LifetimeAction action; + JsonOptional::SetIfExists( + action.Action, attributeItem, ActionPropertyName, [](json const& value) { + return CertificatePolicyAction(value[ActionTypePropertyName].get()); + }); + + if (attributeItem.contains(TriggerPropertyName)) + { + auto const triggerPropertyJson = attributeItem[TriggerPropertyName]; + JsonOptional::SetIfExists( + action.DaysBeforeExpiry, triggerPropertyJson, DaysBeforeExpiryPropertyName); + JsonOptional::SetIfExists( + action.LifetimePercentage, triggerPropertyJson, LifetimePercentagePropertyName); + } + // At this point the action is parsed from json and can be added to the LifeTimeActions from + // the policy. + certificate.Policy.LifetimeActions.emplace_back(action); + } + } + } + + return certificate; +} + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + + const CertificateKeyUsage CertificateKeyUsage::DigitalSignature(_detail::DigitalSignatureValue); + const CertificateKeyUsage CertificateKeyUsage::NonRepudiation(_detail::NonRepudiationValue); + const CertificateKeyUsage CertificateKeyUsage::KeyEncipherment(_detail::KeyEnciphermentValue); + const CertificateKeyUsage CertificateKeyUsage::DataEncipherment(_detail::DataEnciphermentValue); + const CertificateKeyUsage CertificateKeyUsage::KeyAgreement(_detail::KeyAgreementValue); + const CertificateKeyUsage CertificateKeyUsage::KeyCertSign(_detail::KeyCertSignValue); + const CertificateKeyUsage CertificateKeyUsage::CrlSign(_detail::CrlSignValue); + const CertificateKeyUsage CertificateKeyUsage::EncipherOnly(_detail::EncipherOnlyValue); + const CertificateKeyUsage CertificateKeyUsage::DecipherOnly(_detail::DecipherOnlyValue); + + const CertificateKeyType CertificateKeyType::Ec(_detail::EcValue); + const CertificateKeyType CertificateKeyType::EcHsm(_detail::EcHsmValue); + const CertificateKeyType CertificateKeyType::Rsa(_detail::RsaValue); + const CertificateKeyType CertificateKeyType::RsaHsm(_detail::RsaHsmValue); + + const CertificateKeyCurveName CertificateKeyCurveName::P256(_detail::P256Value); + const CertificateKeyCurveName CertificateKeyCurveName::P256K(_detail::P256KValue); + const CertificateKeyCurveName CertificateKeyCurveName::P384(_detail::P384Value); + const CertificateKeyCurveName CertificateKeyCurveName::P521(_detail::P521Value); + + const CertificateContentType CertificateContentType::Pkcs12(_detail::Pkc12Value); + const CertificateContentType CertificateContentType::Pem(_detail::PemValue); + + const CertificatePolicyAction CertificatePolicyAction::AutoRenew(_detail::AutoRenewValue); + const CertificatePolicyAction CertificatePolicyAction::EmailContacts(_detail::EmailContactsValue); + +}}}} // namespace Azure::Security::KeyVault::Certificates diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificates_common_request.cpp b/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificates_common_request.cpp new file mode 100644 index 000000000..743b89a01 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/keyvault_certificates_common_request.cpp @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "private/keyvault_certificates_common_request.hpp" + +#include +#include + +#include + +using namespace Azure::Security::KeyVault; +using namespace Azure::Core::Http::_internal; + +std::unique_ptr +_detail::KeyVaultCertificatesCommonRequest::SendRequest( + Azure::Core::Http::_internal::HttpPipeline const& pipeline, + Azure::Core::Http::Request& request, + Azure::Core::Context const& context) +{ + auto response = pipeline.Send(request, context); + auto responseCode = response->GetStatusCode(); + switch (responseCode) + { + + // 200, 2001, 202, 204 are accepted responses + case Azure::Core::Http::HttpStatusCode::Ok: + case Azure::Core::Http::HttpStatusCode::Created: + case Azure::Core::Http::HttpStatusCode::Accepted: + case Azure::Core::Http::HttpStatusCode::NoContent: + break; + default: + throw Azure::Core::RequestFailedException(response); + } + return response; +} + +Azure::Core::Http::Request _detail::KeyVaultCertificatesCommonRequest::CreateRequest( + Azure::Core::Url url, + std::string const& apiVersion, + Azure::Core::Http::HttpMethod method, + std::vector const& path, + Azure::Core::IO::BodyStream* content) +{ + using namespace Azure::Core::Http; + Request request = content == nullptr ? Request(method, url) : Request(method, url, content); + + request.GetUrl().AppendQueryParameter("api-version", apiVersion); + + for (std::string const& p : path) + { + if (!p.empty()) + { + request.GetUrl().AppendPath(p); + } + } + return request; +} diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_constants.hpp b/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_constants.hpp new file mode 100644 index 000000000..294d6b05b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_constants.hpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Centralize the string constants used by Key Vault Certificates Client. + * + */ + +#pragma once + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + namespace _detail { + + /***************** Certificates Properties *****************/ + constexpr static const char IdName[] = "id"; + constexpr static const char X5tName[] = "x5t"; + constexpr static const char TagsName[] = "tags"; + constexpr static const char AttributesPropertyName[] = "attributes"; + constexpr static const char EnabledPropertyName[] = "enabled"; + constexpr static const char NbfPropertyName[] = "nbf"; + constexpr static const char ExpPropertyName[] = "exp"; + constexpr static const char CreatedPropertyName[] = "created"; + constexpr static const char UpdatedPropertyName[] = "updated"; + constexpr static const char RecoverableDaysPropertyName[] = "recoverableDays"; + constexpr static const char RecoveryLevelPropertyName[] = "recoveryLevel"; + constexpr static const char KidPropertyName[] = "kid"; + constexpr static const char SidPropertyName[] = "sid"; + constexpr static const char CerPropertyName[] = "cer"; + + /***************** Certificates Policy *****************/ + constexpr static const char PolicyPropertyName[] = "policy"; + constexpr static const char KeyPropsPropertyName[] = "key_props"; + constexpr static const char KeyTypePropertyName[] = "kty"; + constexpr static const char ReuseKeyPropertyName[] = "reuse_key"; + constexpr static const char ExportablePropertyName[] = "exportable"; + constexpr static const char CurveNamePropertyName[] = "crv"; + constexpr static const char KeySizePropertyName[] = "key_size"; + constexpr static const char SecretPropsPropertyName[] = "secret_props"; + constexpr static const char ContentTypePropertyName[] = "contentType"; + constexpr static const char X509PropsPropertyName[] = "x509_props"; + constexpr static const char SubjectPropertyName[] = "subject"; + constexpr static const char SansPropertyName[] = "sans"; + constexpr static const char DnsPropertyName[] = "dns_names"; + constexpr static const char EmailsPropertyName[] = "emails"; + constexpr static const char UserPrincipalNamesPropertyName[] = "upns"; + constexpr static const char KeyUsagePropertyName[] = "key_usage"; + constexpr static const char EkusPropertyName[] = "ekus"; + constexpr static const char ValidityMonthsPropertyName[] = "validity_months"; + constexpr static const char IssuerPropertyName[] = "issuer"; + constexpr static const char CertTransparencyPropertyName[] = "cert_transparency"; + constexpr static const char CtyPropertyName[] = "cty"; + constexpr static const char IssuerNamePropertyName[] = "name"; + constexpr static const char LifetimeActionsPropertyName[] = "lifetime_actions"; + constexpr static const char TriggerPropertyName[] = "trigger"; + constexpr static const char ActionPropertyName[] = "action"; + constexpr static const char LifetimePercentagePropertyName[] = "lifetime_percentage"; + constexpr static const char DaysBeforeExpiryPropertyName[] = "days_before_expiry"; + constexpr static const char ActionTypePropertyName[] = "action_type"; + + /***************** Certificates Key Usage *****************/ + constexpr static const char DigitalSignatureValue[] = "digitalSignature"; + constexpr static const char NonRepudiationValue[] = "nonRepudiation"; + constexpr static const char KeyEnciphermentValue[] = "keyEncipherment"; + constexpr static const char DataEnciphermentValue[] = "dataEncipherment"; + constexpr static const char KeyAgreementValue[] = "keyAgreement"; + constexpr static const char KeyCertSignValue[] = "keyCertSign"; + constexpr static const char CrlSignValue[] = "crlSign"; + constexpr static const char EncipherOnlyValue[] = "encipherOnly"; + constexpr static const char DecipherOnlyValue[] = "decipherOnly"; + + /***************** Certificates Key Type *****************/ + constexpr static const char EcValue[] = "EC"; + constexpr static const char EcHsmValue[] = "EC-HSM"; + constexpr static const char RsaValue[] = "RSA"; + constexpr static const char RsaHsmValue[] = "RSA-HSM"; + + /***************** Certificates Curve Name *****************/ + constexpr static const char P256Value[] = "P-256"; + constexpr static const char P256KValue[] = "P-256K"; + constexpr static const char P384Value[] = "P-384"; + constexpr static const char P521Value[] = "P-521"; + + /***************** Certificates Content Type *****************/ + constexpr static const char Pkc12Value[] = "application/x-pkcs12"; + constexpr static const char PemValue[] = "application/x-pem-file"; + + /***************** Certificates Policy Action *****************/ + constexpr static const char AutoRenewValue[] = "AutoRenew"; + constexpr static const char EmailContactsValue[] = "EmailContacts"; +}}}}} // namespace Azure::Security::KeyVault::Certificates::_detail diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_serializers.hpp b/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_serializers.hpp new file mode 100644 index 000000000..d8935a795 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/private/certificate_serializers.hpp @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Centralize the serialize and de-serialize methods for the key vault keys models. + * + */ + +#pragma once + +#include + +#include "azure/keyvault/certificates/certificate_client_models.hpp" +#include "azure/keyvault/certificates/certificate_client_options.hpp" + +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + namespace _detail { + /***************** Certificate *****************/ + class KeyVaultCertificateSerializer final { + public: + // Creates a new key based on a name and an HTTP raw response. + static KeyVaultCertificateWithPolicy KeyVaultCertificateDeserialize( + std::string const& name, + Azure::Core::Http::RawResponse const& rawResponse); + + static std::string GetUrlAuthorityWithScheme(Azure::Core::Url const& url) + { + std::string urlString; + if (!url.GetScheme().empty()) + { + urlString += url.GetScheme() + "://"; + } + urlString += url.GetHost(); + if (url.GetPort() != 0) + { + urlString += ":" + std::to_string(url.GetPort()); + } + return urlString; + } + + void static inline ParseKeyUrl( + CertificateProperties& certificateProperties, + std::string const& url) + { + Azure::Core::Url kid(url); + certificateProperties.Id = url; + certificateProperties.VaultUrl = GetUrlAuthorityWithScheme(kid); + auto const& path = kid.GetPath(); + // path is in the form of `verb/keyName{/keyVersion}` + auto const separatorChar = '/'; + auto pathEnd = path.end(); + auto start = path.begin(); + start = std::find(start, pathEnd, separatorChar); + start += 1; + auto separator = std::find(start, pathEnd, separatorChar); + if (separator != pathEnd) + { + certificateProperties.Name = std::string(start, separator); + start = separator + 1; + certificateProperties.Version = std::string(start, pathEnd); + } + else + { + // Nothing but the name+ + certificateProperties.Name = std::string(start, pathEnd); + } + } + }; + +}}}}} // namespace Azure::Security::KeyVault::Certificates::_detail diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/private/keyvault_certificates_common_request.hpp b/sdk/keyvault/azure-security-keyvault-certificates/src/private/keyvault_certificates_common_request.hpp new file mode 100644 index 000000000..41bef141c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/private/keyvault_certificates_common_request.hpp @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief Provides a wrapper class for the Azure Core Pipeline for all Key Vault services where + * common functionality is set up. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace _detail { + + struct KeyVaultCertificatesCommonRequest final + { + static Azure::Core::Http::Request CreateRequest( + Azure::Core::Url url, + std::string const& apiVersion, + Azure::Core::Http::HttpMethod method, + std::vector const& path, + Azure::Core::IO::BodyStream* content); + + static std::unique_ptr SendRequest( + Azure::Core::Http::_internal::HttpPipeline const& pipeline, + Azure::Core::Http::Request& request, + Azure::Core::Context const& context); + }; + +}}}} // namespace Azure::Security::KeyVault::_detail diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/private/package_version.hpp b/sdk/keyvault/azure-security-keyvault-certificates/src/private/package_version.hpp new file mode 100644 index 000000000..9f3280c4f --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/private/package_version.hpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Provides version information. + */ + +#pragma once + +#include + +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MAJOR 4 +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MINOR 0 +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PATCH 0 +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PRERELEASE "beta.1" + +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA_HELPER(i) #i +#define AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA(i) \ + AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA_HELPER(i) + +namespace Azure { namespace Security { namespace KeyVault { namespace Certificates { + namespace _detail { + /** + * @brief Provides version information. + * + */ + class PackageVersion final { + public: + /// Major numeric identifier. + static constexpr int32_t Major = AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MAJOR; + + /// Minor numeric identifier. + static constexpr int32_t Minor = AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MINOR; + + /// Patch numeric identifier. + static constexpr int32_t Patch = AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PATCH; + + /// Indicates whether the SDK is in a pre-release state. + static constexpr bool IsPreRelease + = sizeof(AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PRERELEASE) != sizeof(""); + + /** + * @brief The version in string format used for telemetry following the `semver.org` standard + * (https://semver.org). + */ + static constexpr const char* ToString() + { + return IsPreRelease + ? AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA(AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MAJOR) "." AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA( + AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MINOR) "." AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA(AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PATCH) "-" AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PRERELEASE + : AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA(AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MAJOR) "." AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA( + AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MINOR) "." AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA(AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PATCH); + } + }; +}}}}} // namespace Azure::Security::KeyVault::Certificates::_detail + +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA_HELPER +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_ITOA + +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MAJOR +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_MINOR +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PATCH +#undef AZURE_SECURITY_KEYVAULT_CERTIFICATES_VERSION_PRERELEASE diff --git a/sdk/keyvault/azure-security-keyvault-certificates/test/ut/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/CMakeLists.txt new file mode 100644 index 000000000..80d4d2e50 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +project (azure-security-keyvault-certificates-test LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +include(GoogleTest) + +# Export the test folder for recordings access. +add_compile_definitions(AZURE_TEST_RECORDING_DIR="${CMAKE_CURRENT_LIST_DIR}") + +add_executable ( + azure-security-keyvault-certificates-test + macro_guard.cpp + certificate_client_test.cpp + certificate_client_base_test.hpp) + +if (MSVC) + target_compile_options(azure-security-keyvault-certificates-test PUBLIC /wd6326 /wd26495 /wd26812 /wd4389) +endif() + +target_link_libraries(azure-security-keyvault-certificates-test PRIVATE azure-security-keyvault-certificates azure-identity azure-core-test-fw gtest gtest_main gmock) + +# Adding private headers so we can test the private APIs with no relative paths include. +target_include_directories (azure-security-keyvault-certificates-test PRIVATE $) + +# gtest_add_tests will scan the test from azure-core-test and call add_test +# for each test to ctest. This enables `ctest -r` to run specific tests directly. +gtest_discover_tests(azure-security-keyvault-certificates-test + TEST_PREFIX azure-security-keyvault-certificates. + NO_PRETTY_TYPES + NO_PRETTY_VALUES +) diff --git a/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_base_test.hpp b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_base_test.hpp new file mode 100644 index 000000000..efc57b25f --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_base_test.hpp @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief The base class to construct and init a Key Vault client. + * + */ +#include + +#include +#include +#include + +#include + +using namespace std::chrono_literals; + +namespace Azure { + namespace Security { + namespace KeyVault { + namespace Certificates { + namespace Test { + /** + * @brief Client Certificate Credential authenticates with the Azure services using a Tenant ID, + * Client ID and a client secret. + * + */ + class TestClientSecretCredential final : public Core::Credentials::TokenCredential { + public: + Core::Credentials::AccessToken GetToken( + Core::Credentials::TokenRequestContext const& tokenRequestContext, + Core::Context const& context) const override + { + Core::Credentials::AccessToken accessToken; + accessToken.Token = "magicToken"; + accessToken.ExpiresOn = DateTime::max(); + + if (context.IsCancelled() || tokenRequestContext.Scopes.size() == 0) + { + accessToken.ExpiresOn = DateTime::min(); + } + + return accessToken; + } + }; + + class KeyVaultCertificateClientTest : public Azure::Core::Test::TestBase, + public ::testing::WithParamInterface { + private: + std::unique_ptr m_client; + std::string GetEnv(const std::string& name, std::string const& defaultValue = std::string()) + { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) + const char* ret = std::getenv(name.data()); +#pragma warning(pop) +#else + const char* ret = std::getenv(name.data()); +#endif + + if (!ret) + { + if (!defaultValue.empty()) + { + return defaultValue; + } + + throw std::runtime_error( + name + " is required to run the tests but not set as an environment variable."); + } + + return std::string(ret); + } + + protected: + std::shared_ptr m_credential; + std::shared_ptr m_testCredential; + std::string m_keyVaultUrl; + std::chrono::milliseconds m_defaultWait; + + Azure::Security::KeyVault::Certificates::CertificateClient const& GetClientForTest( + std::string const& testName) + { + InitializeClient(); + // set the interceptor for the current test + m_testContext.RenameTest(testName); + return *m_client; + } + + // Create + void InitializeClient() + { + // Init interceptor from PlayBackRecorder + std::string recordingPath(AZURE_TEST_RECORDING_DIR); + recordingPath.append("/recordings"); + Azure::Core::Test::TestBase::SetUpBase(recordingPath); + + std::string tenantId = GetEnv("AZURE_TENANT_ID", "tenant"); + std::string clientId = GetEnv("AZURE_CLIENT_ID", "client"); + std::string secretId = GetEnv("AZURE_CLIENT_SECRET", "secret"); + + m_keyVaultUrl = GetEnv("AZURE_KEYVAULT_URL", "https://REDACTED.vault.azure.net/"); + + // Create default client for the test + CertificateClientOptions options; + // Replace default transport adapter for playback + if (m_testContext.IsPlaybackMode()) + { + options.Transport.Transport = m_interceptor->GetPlaybackClient(); + } + // Insert Recording policy when Record mode is on (non playback and non LiveMode) + else if (!m_testContext.IsLiveMode()) + { + // AZURE_TEST_RECORDING_DIR is exported by CMAKE + options.PerRetryPolicies.push_back(m_interceptor->GetRecordPolicy()); + } + + if (m_testContext.IsPlaybackMode()) + { // inject fake token client here if it's test + m_testCredential = std::make_shared(); + m_client = std::make_unique(m_keyVaultUrl, m_testCredential, options); + // we really dont need to wait for results + m_defaultWait = 1ms; + } + else + { + m_credential = std::make_shared( + tenantId, clientId, secretId); + m_client = std::make_unique(m_keyVaultUrl, m_credential, options); + m_defaultWait = 30s; + } + + // When running live tests, service can return 429 error response if the client is sending + // multiple requests per second. This can happen if the network is fast and tests are running + // without any delay between them. + auto avoidTestThrottled = GetEnv("AZURE_KEYVAULT_AVOID_THROTTLED", "0"); + + if (avoidTestThrottled != "0") + { + std::cout << "- Wait to avoid server throttled..." << std::endl; + // 10 sec should be enough to prevent from 429 error + std::this_thread::sleep_for(std::chrono::seconds(10)); + } + } + + public: + template + static inline void CheckValidResponse( + Azure::Response& response, + Azure::Core::Http::HttpStatusCode expectedCode = Azure::Core::Http::HttpStatusCode::Ok) + { + auto const& rawResponse = response.RawResponse; + EXPECT_EQ( + static_cast::type>( + rawResponse->GetStatusCode()), + static_cast::type>( + expectedCode)); + } + }; + +}}}}} // namespace Azure::Security::KeyVault::Certificates::Test diff --git a/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_test.cpp b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_test.cpp new file mode 100644 index 000000000..f30c40ca3 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/certificate_client_test.cpp @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include + +#include "certificate_client_base_test.hpp" +#include +#include + +#include + +using namespace std::chrono_literals; +using namespace Azure::Security::KeyVault::Certificates; +using namespace Azure::Security::KeyVault::Certificates::Test; + +TEST_F(KeyVaultCertificateClientTest, GetCertificate) +{ + // cspell: disable-next-line + std::string const certificateName("vivazqu"); + + auto const& client + = GetClientForTest(::testing::UnitTest::GetInstance()->current_test_info()->name()); + + { + auto response = client.GetCertificate(certificateName); + CheckValidResponse(response); + auto cert = response.Value; + EXPECT_EQ(cert.Name(), cert.Properties.Name); + EXPECT_EQ(cert.Properties.Name, certificateName); + EXPECT_EQ(cert.Properties.VaultUrl, m_keyVaultUrl); + // There should be a version + EXPECT_NE(cert.Properties.Version, ""); + + // x5t + EXPECT_NE(cert.Properties.X509Thumbprint.size(), 0); + EXPECT_EQ(cert.Properties.Tags.size(), 0); + + // attributes + EXPECT_TRUE(cert.Properties.Enabled.HasValue()); + EXPECT_TRUE(cert.Properties.NotBefore.HasValue()); + EXPECT_TRUE(cert.Properties.ExpiresOn.HasValue()); + EXPECT_TRUE(cert.Properties.CreatedOn.HasValue()); + EXPECT_TRUE(cert.Properties.UpdatedOn.HasValue()); + EXPECT_TRUE(cert.Properties.RecoverableDays.HasValue()); + EXPECT_TRUE(cert.Properties.RecoveryLevel.HasValue()); + + // kid, sid, cer + EXPECT_NE(cert.KeyId, ""); + EXPECT_NE(cert.SecretId, ""); + EXPECT_NE(cert.Cer.size(), 0); + + // policy + { + auto const& policy = cert.Policy; + + // Key props + EXPECT_TRUE(policy.Exportable.HasValue()); + EXPECT_TRUE(policy.KeyType.HasValue()); + EXPECT_TRUE(policy.ReuseKey.HasValue()); + // Recording uses RSA with no curve-name. Use RSA key when running LIVE + EXPECT_FALSE(policy.KeyCurveName.HasValue()); + EXPECT_TRUE(policy.KeySize.HasValue()); + + // Secret props + EXPECT_TRUE(policy.ContentType.HasValue()); + + // x509_props + EXPECT_TRUE(policy.Subject.size() > 0); + + // issuer + EXPECT_TRUE(policy.IssuerName.HasValue()); + + // attributes + EXPECT_TRUE(policy.CreatedOn.HasValue()); + + // lifetime_actions + EXPECT_TRUE(policy.LifetimeActions.size() > 0); + EXPECT_NE(policy.LifetimeActions[0].Action.ToString(), ""); + } + } +} + +TEST_F(KeyVaultCertificateClientTest, GetCertificateVersion) +{ + // cspell: disable-next-line + std::string const certificateName("vivazqu"); + std::string const certificateVersion("8d532937fea74df58d9b18fca036ad51"); + + auto const& client + = GetClientForTest(::testing::UnitTest::GetInstance()->current_test_info()->name()); + + { + GetCertificateOptions options; + options.Version = certificateVersion; + auto response = client.GetCertificateVersion(certificateName, options); + CheckValidResponse(response); + auto cert = response.Value; + EXPECT_EQ(cert.Name(), cert.Properties.Name); + EXPECT_EQ(cert.Properties.Name, certificateName); + EXPECT_EQ(cert.Properties.VaultUrl, m_keyVaultUrl); + // There should be a version + EXPECT_NE(cert.Properties.Version, ""); + + // x5t + EXPECT_NE(cert.Properties.X509Thumbprint.size(), 0); + EXPECT_EQ(cert.Properties.Tags.size(), 0); + + // attributes + EXPECT_TRUE(cert.Properties.Enabled.HasValue()); + EXPECT_TRUE(cert.Properties.NotBefore.HasValue()); + EXPECT_TRUE(cert.Properties.ExpiresOn.HasValue()); + EXPECT_TRUE(cert.Properties.CreatedOn.HasValue()); + EXPECT_TRUE(cert.Properties.UpdatedOn.HasValue()); + EXPECT_TRUE(cert.Properties.RecoverableDays.HasValue()); + EXPECT_TRUE(cert.Properties.RecoveryLevel.HasValue()); + + // kid, sid, cer + EXPECT_NE(cert.KeyId, ""); + EXPECT_NE(cert.SecretId, ""); + EXPECT_NE(cert.Cer.size(), 0); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-certificates/test/ut/macro_guard.cpp b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/macro_guard.cpp new file mode 100644 index 000000000..125799333 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/test/ut/macro_guard.cpp @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +// Define `min` and `max` as function-like macros before including all public +// headers to ensure that uses of those identifiers are defended against +// expansion as function-like macros. Define `small` as an object-like macro to +// ensure that identifier isn't used at all. Windows.h is badly behaved and +// defines similar macros with these names and we want to ensure the SDK headers +// function even when a naive user includes Windows.h first. +// +#define small FAIL>=": "1.1.0" + }, + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +}