From a4ae81708b2a83541386660d45eafc17e215c7fd Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Tue, 30 Mar 2021 13:12:48 -0700 Subject: [PATCH] [Keyvault] APIs (#1967) Adding the next APIs: UpdateKeyProperties, fixes: #1541 GetPropertiesOfKeys , fixes: #1542 GetPropertiesOfKeyVersions, fixes: #1543 GetDeletedKeys, fixes: #1546 PurgeDeletedKey, fixes: #1547 StartRecoveryDeletedKey, fixes: #1548 BackupKey, fixes: #1549 RestoreKeyBackup, fixes: #1550 ImportKey, fixes: #1551 Add samples, fixes: #1987 --- .../core/internal/json/json_optional.hpp | 59 ++++- .../core/internal/json/json_serializable.hpp | 6 + sdk/core/azure-core/inc/azure/core/url.hpp | 7 + sdk/core/azure-core/src/http/url.cpp | 26 +- .../azure/perf/test/http_client_get_test.hpp | 2 +- .../CMakeLists.txt | 2 + .../azure/keyvault/common/client_options.hpp | 75 ++++++ .../keyvault/common/internal/base64url.hpp | 59 +++++ .../common/internal/keyvault_pipeline.hpp | 28 +- .../common/internal/unix_time_helper.hpp | 13 + .../CMakeLists.txt | 15 +- .../inc/azure/keyvault/key_vault.hpp | 6 + .../inc/azure/keyvault/keys/deleted_key.hpp | 8 - .../keyvault/keys/details/key_backup.hpp | 29 +++ .../keyvault/keys/details/key_constants.hpp | 24 ++ .../keys/details/key_request_parameters.hpp | 33 ++- .../keyvault/keys/details/key_serializers.hpp | 93 +++++++ .../keyvault/keys/import_key_options.hpp | 66 +++++ .../inc/azure/keyvault/keys/json_web_key.hpp | 24 ++ .../inc/azure/keyvault/keys/key_client.hpp | 244 ++++++++++++++++-- .../keyvault/keys/key_client_options.hpp | 44 +--- .../azure/keyvault/keys/key_properties.hpp | 2 +- .../inc/azure/keyvault/keys/key_type.hpp | 9 +- .../inc/azure/keyvault/keys/key_vault_key.hpp | 16 +- .../keys/list_keys_single_page_result.hpp | 53 ++++ .../keys/recover_deleted_key_operation.hpp | 103 ++++++++ .../sample/get-key/CMakeLists.txt | 15 -- .../sample/get-key/main.cpp | 70 ----- .../samples/README.md | 19 ++ .../samples/sample1_hello_world.md | 101 ++++++++ .../samples/sample2_backup_and_restore.md | 64 +++++ .../samples/sample3_get_keys.md | 171 ++++++++++++ .../src/delete_key_operation.cpp | 3 +- .../src/deleted_key.cpp | 39 ++- .../src/import_key_options.cpp | 73 ++++++ .../src/json_web_key.cpp | 28 ++ .../src/key_backup.cpp | 38 +++ .../src/key_client.cpp | 211 ++++++++++++++- .../src/key_request_parameters.cpp | 27 +- .../src/key_type.cpp | 28 +- .../src/key_vault_key.cpp | 64 +++-- .../src/list_keys_single_page_result.cpp | 127 +++++++++ .../src/recover_deleted_key_operation.cpp | 78 ++++++ .../{sample => test/samples}/CMakeLists.txt | 4 +- .../sample1-hello-world/CMakeLists.txt | 15 ++ .../sample1_hello_world.cpp | 85 ++++++ .../sample2-backup-and-restore/CMakeLists.txt | 15 ++ .../sample2_backup_and_restore.cpp | 143 ++++++++++ .../samples/sample3-get-keys/CMakeLists.txt | 15 ++ .../sample3-get-keys/sample3_get_keys.cpp | 183 +++++++++++++ .../test/ut/CMakeLists.txt | 3 + .../test/ut/key_client_backup_test_live.cpp | 84 ++++++ .../test/ut/key_client_base_test.hpp | 73 +++++- .../test/ut/key_client_create_test_live.cpp | 16 +- .../test/ut/key_client_delete_test_live.cpp | 34 ++- .../test/ut/key_client_get_test_live.cpp | 158 +++++++++++- .../test/ut/key_client_import_test_live.cpp | 67 +++++ .../test/ut/key_client_update_test_live.cpp | 49 ++++ 58 files changed, 2885 insertions(+), 261 deletions(-) create mode 100644 sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/client_options.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/base64url.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_backup.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_serializers.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/import_key_options.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/list_keys_single_page_result.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp delete mode 100644 sdk/keyvault/azure-security-keyvault-keys/sample/get-key/CMakeLists.txt delete mode 100644 sdk/keyvault/azure-security-keyvault-keys/sample/get-key/main.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/samples/README.md create mode 100644 sdk/keyvault/azure-security-keyvault-keys/samples/sample1_hello_world.md create mode 100644 sdk/keyvault/azure-security-keyvault-keys/samples/sample2_backup_and_restore.md create mode 100644 sdk/keyvault/azure-security-keyvault-keys/samples/sample3_get_keys.md create mode 100644 sdk/keyvault/azure-security-keyvault-keys/src/import_key_options.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/src/json_web_key.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/src/key_backup.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/src/list_keys_single_page_result.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp rename sdk/keyvault/azure-security-keyvault-keys/{sample => test/samples}/CMakeLists.txt (52%) create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/CMakeLists.txt create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/sample1_hello_world.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/CMakeLists.txt create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/sample2_backup_and_restore.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/CMakeLists.txt create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/sample3_get_keys.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_backup_test_live.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_import_test_live.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_update_test_live.cpp diff --git a/sdk/core/azure-core/inc/azure/core/internal/json/json_optional.hpp b/sdk/core/azure-core/inc/azure/core/internal/json/json_optional.hpp index 59ce261e5..d6fb70eac 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/json/json_optional.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/json/json_optional.hpp @@ -25,7 +25,7 @@ namespace Azure { namespace Core { namespace Json { namespace _internal { { /** * @brief If the optional key \p key is present in the json node \p jsonKey set the value of \p - * destination. + * the Nullable destination. * * @remark If the key is not in the json node, the \p destination is not modified. * @@ -39,7 +39,7 @@ namespace Azure { namespace Core { namespace Json { namespace _internal { Azure::Core::Json::_internal::json const& jsonKey, std::string const& key) noexcept { - if (jsonKey.contains(key)) + if (jsonKey.contains(key) && !jsonKey[key].is_null()) // In Json and not-Null { destination = jsonKey[key].get(); } @@ -54,6 +54,36 @@ namespace Azure { namespace Core { namespace Json { namespace _internal { * @param jsonKey The json node to review. * @param key The key name for the optional property. * @param destination The value to update if the key name property is in the json node. + * @param decorator A callback used to convert the Json value from `V` type to the `T` type. For + * example, getting std::string from Json (the V type) and setting a Nullable (where T + * type is Datetime), the decorator would define how to create the Datetime from the + * std::string. + */ + template + static inline void SetIfExists( + T& destination, + Azure::Core::Json::_internal::json const& jsonKey, + std::string const& key, + std::function decorator) noexcept + { + if (jsonKey.contains(key)) + { + if (!jsonKey[key].is_null()) + { + destination = decorator(jsonKey[key].get()); + } + } + } + + /** + * @brief If the optional key \p key is present in the json node \p jsonKey set the value of \p + * the Nullable destination. + * + * @remark If the key is not in the json node, the \p destination is not modified. + * + * @param jsonKey The json node to review. + * @param key The key name for the optional property. + * @param destination The value to update if the key name property is in the json node. * @param decorator A optional function to update the json value before updating the \p * destination. */ @@ -71,4 +101,29 @@ namespace Azure { namespace Core { namespace Json { namespace _internal { } }; + template + static inline void SetFromNullable( + Azure::Nullable const& source, + Azure::Core::Json::_internal::json& jsonKey, + std::string const& keyName, + std::function factory) + { + if (source) + { + jsonKey[keyName] = factory(source.GetValue()); + } + } + + template + static inline void SetFromNullable( + Azure::Nullable const& source, + Azure::Core::Json::_internal::json& jsonKey, + std::string const& keyName) + { + if (source) + { + jsonKey[keyName] = source.GetValue(); + } + } + }}}} // namespace Azure::Core::Json::_internal diff --git a/sdk/core/azure-core/inc/azure/core/internal/json/json_serializable.hpp b/sdk/core/azure-core/inc/azure/core/internal/json/json_serializable.hpp index eba6f26ab..104d8f85b 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/json/json_serializable.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/json/json_serializable.hpp @@ -25,6 +25,12 @@ namespace Azure { namespace Core { namespace Json { namespace _internal { * @return The json string representation. */ virtual std::string Serialize() const = 0; + + /** + * @brief Default destructor for serializable objects. + * + */ + virtual ~JsonSerializable() = default; }; }}}} // namespace Azure::Core::Json::_internal diff --git a/sdk/core/azure-core/inc/azure/core/url.hpp b/sdk/core/azure-core/inc/azure/core/url.hpp index d53859977..270b5f336 100644 --- a/sdk/core/azure-core/inc/azure/core/url.hpp +++ b/sdk/core/azure-core/inc/azure/core/url.hpp @@ -243,6 +243,13 @@ namespace Azure { namespace Core { */ std::string GetUrlWithoutQuery() const { return GetUrlWithoutQuery(false); } + /** + * @brief Get Scheme, host and port. + * + * @return Url authority. + */ + std::string GetUrlAuthorityWithScheme() const; + /** * @brief Get the path and query parameters. * diff --git a/sdk/core/azure-core/src/http/url.cpp b/sdk/core/azure-core/src/http/url.cpp index fd75ced2c..d01c5a48e 100644 --- a/sdk/core/azure-core/src/http/url.cpp +++ b/sdk/core/azure-core/src/http/url.cpp @@ -178,21 +178,29 @@ void Url::AppendQueryParameters(const std::string& query) } } +std::string Url::GetUrlAuthorityWithScheme() const +{ + std::string url; + + if (!m_scheme.empty()) + { + url += m_scheme + "://"; + } + url += m_host; + if (m_port != 0) + { + url += ":" + std::to_string(m_port); + } + return url; +} + std::string Url::GetUrlWithoutQuery(bool relative) const { std::string url; if (!relative) { - if (!m_scheme.empty()) - { - url += m_scheme + "://"; - } - url += m_host; - if (m_port != 0) - { - url += ":" + std::to_string(m_port); - } + url += GetUrlAuthorityWithScheme(); } if (!m_encodedPath.empty()) diff --git a/sdk/core/perf/test/inc/azure/perf/test/http_client_get_test.hpp b/sdk/core/perf/test/inc/azure/perf/test/http_client_get_test.hpp index 636d74e5f..414653d27 100644 --- a/sdk/core/perf/test/inc/azure/perf/test/http_client_get_test.hpp +++ b/sdk/core/perf/test/inc/azure/perf/test/http_client_get_test.hpp @@ -59,7 +59,7 @@ namespace Azure { namespace Perf { namespace Test { Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, m_url); auto response = _detail::HttpClient->Send(request, ctx); // Read the body from network - auto bodyStream = response->GetBodyStream(); + auto bodyStream = response->ExtractBodyStream(); response->SetBody(bodyStream->ReadToEnd(ctx)); } diff --git a/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt index 4b5a89783..fb831695d 100644 --- a/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt @@ -28,8 +28,10 @@ endif() set( AZURE_KEYVAULT_COMMON_HEADER + inc/azure/keyvault/common/internal/base64url.hpp inc/azure/keyvault/common/internal/keyvault_pipeline.hpp inc/azure/keyvault/common/internal/unix_time_helper.hpp + inc/azure/keyvault/common/client_options.hpp inc/azure/keyvault/common/keyvault_constants.hpp inc/azure/keyvault/common/keyvault_exception.hpp inc/azure/keyvault/common/version.hpp diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/client_options.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/client_options.hpp new file mode 100644 index 000000000..865a75a1a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/client_options.hpp @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines the base options to create a Key Vault client. + * + */ + +#pragma once + +#include + +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Common { + + /** + * @brief Available and supported service versions. + * + */ + enum class ServiceVersion + { + /** + * @brief Use to send request to the 7.0 version of Key Vault service. + * + */ + V7_0, + /** + * @brief Use to send request to the 7.1 version of Key Vault service. + * + */ + V7_1, + /** + * @brief Use to send request to the 7.2 version of Key Vault service. + * + */ + V7_2 + }; + + /** + * @brief Define the base options to create an KeyVault SDK client. + * + */ + struct ClientOptions : public Azure::Core::_internal::ClientOptions + { + /** + * @brief The service version. All request are created with this version. + * + */ + ServiceVersion Version; + + ClientOptions(ServiceVersion version) + : Azure::Core::_internal::ClientOptions(), Version(version) + { + } + + std::string GetVersionString() const + { + switch (Version) + { + case ServiceVersion::V7_0: + return "7.0"; + case ServiceVersion::V7_1: + return "7.1"; + case ServiceVersion::V7_2: + return "7.2"; + default: + throw std::runtime_error("Version not found"); + } + } + }; + +}}}} // namespace Azure::Security::KeyVault::Common diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/base64url.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/base64url.hpp new file mode 100644 index 000000000..7fa482ed6 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/base64url.hpp @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief Provides helper method for base64url. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Common { namespace _internal { + + /** + * @brief Provides conversion methods for base64url. + */ + struct Base64Url + { + static inline std::string Base64UrlEncode(const std::vector& data) + { + auto base64 = Azure::Core::Convert::Base64Encode(data); + // update to base64url + auto trail = base64.find('='); + if (trail != std::string::npos) + { + base64 = base64.substr(0, trail); + } + std::replace(base64.begin(), base64.end(), '+', '-'); + std::replace(base64.begin(), base64.end(), '/', '_'); + return base64; + } + + static inline std::vector Base64UrlDecode(const std::string& text) + { + std::string base64url(text); + // base64url to base64 + std::replace(base64url.begin(), base64url.end(), '-', '+'); + std::replace(base64url.begin(), base64url.end(), '_', '/'); + switch (base64url.size() % 4) + { + case 0: + break; + case 2: + base64url.append("=="); + break; + case 3: + base64url.append("="); + break; + default: + throw new std::invalid_argument("Unexpected base64 encoding in the http response."); + } + return Azure::Core::Convert::Base64Decode(base64url); + } + }; +}}}}} // namespace Azure::Security::KeyVault::Common::_internal diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/keyvault_pipeline.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/keyvault_pipeline.hpp index 074791901..da2bffe9a 100644 --- a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/keyvault_pipeline.hpp +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/keyvault_pipeline.hpp @@ -90,6 +90,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common { n * @param method The method for the request. * @param factoryFn The function to deserialize and produce T from the raw response. * @param path A path for the request represented as a vector of strings. + * @param query Optional query parameters for constructing the request. * @return The object produced by the \p factoryFn and the raw response from the network. */ template @@ -97,9 +98,17 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common { n Azure::Core::Context const& context, Azure::Core::Http::HttpMethod method, std::function factoryFn, - std::vector const& path) + std::vector const& path, + std::unique_ptr> const& query = nullptr) { auto request = CreateRequest(method, path); + if (query != nullptr) + { + for (auto const& queryParameter : *query) + { + request.GetUrl().AppendQueryParameter(queryParameter.first, queryParameter.second); + } + } auto response = SendRequest(context, request); return Azure::Response(factoryFn(*response), std::move(response)); } @@ -132,6 +141,23 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common { n return Azure::Response(factoryFn(*response), std::move(response)); } + template + Azure::Response SendRequest( + Azure::Core::Context const& context, + Azure::Core::Http::HttpMethod method, + std::function serializeContentFn, + std::function factoryFn, + std::vector const& path) + { + auto serialContent = serializeContentFn(); + auto streamContent = Azure::Core::IO::MemoryBodyStream( + reinterpret_cast(serialContent.data()), serialContent.size()); + + auto request = CreateRequest(method, &streamContent, path); + auto response = SendRequest(context, request); + return Azure::Response(factoryFn(*response), std::move(response)); + } + /** * @brief Create a key vault request and send it using the Azure Core pipeline directly to avoid * checking the respone code. diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/unix_time_helper.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/unix_time_helper.hpp index b337686a0..b3d19cf09 100644 --- a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/unix_time_helper.hpp +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/internal/unix_time_helper.hpp @@ -29,5 +29,18 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common { n { return Azure::DateTime(1970) + std::chrono::seconds(unixTime); } + + /** + * @brief Converts a #Azure::Core::Datetime to unix time. + * + * @param dateTime The date time to convert. + */ + static inline uint64_t DatetimeToUnixTime(Azure::DateTime dateTime) + { + // This count starts at the Unix Epoch which was January 1st, 1970 at UTC. + auto secondsSince1970 + = std::chrono::duration_cast(dateTime - Azure::DateTime(1970)); + return secondsSince1970.count(); + } }; }}}}} // namespace Azure::Security::KeyVault::Common::_internal diff --git a/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt index c544628e7..1672c6451 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt @@ -30,17 +30,21 @@ set( AZURE_KEYVAULT_KEYS_HEADER inc/azure/keyvault/keys/delete_key_operation.hpp inc/azure/keyvault/keys/deleted_key.hpp + inc/azure/keyvault/keys/details/key_backup.hpp + inc/azure/keyvault/keys/details/key_constants.hpp + inc/azure/keyvault/keys/details/key_request_parameters.hpp + inc/azure/keyvault/keys/import_key_options.hpp inc/azure/keyvault/keys/json_web_key.hpp inc/azure/keyvault/keys/key_client.hpp - inc/azure/keyvault/keys/details/key_constants.hpp inc/azure/keyvault/keys/key_create_options.hpp inc/azure/keyvault/keys/key_curve_name.hpp inc/azure/keyvault/keys/key_client_options.hpp inc/azure/keyvault/keys/key_operation.hpp inc/azure/keyvault/keys/key_properties.hpp - inc/azure/keyvault/keys/details/key_request_parameters.hpp inc/azure/keyvault/keys/key_type.hpp inc/azure/keyvault/keys/key_vault_key.hpp + inc/azure/keyvault/keys/list_keys_single_page_result.hpp + inc/azure/keyvault/keys/recover_deleted_key_operation.hpp inc/azure/keyvault/keys/version.hpp ) @@ -48,11 +52,16 @@ set( AZURE_KEYVAULT_KEYS_SOURCE src/delete_key_operation.cpp src/deleted_key.cpp + src/import_key_options.cpp + src/json_web_key.cpp + src/key_backup.cpp src/key_client.cpp src/key_curve_name.cpp src/key_request_parameters.cpp src/key_type.cpp src/key_vault_key.cpp + src/list_keys_single_page_result.cpp + src/recover_deleted_key_operation.cpp ) add_library(azure-security-keyvault-keys @@ -90,7 +99,7 @@ if (BUILD_PERFORMANCE_TESTS) endif() if(BUILD_SAMPLES) - add_subdirectory(sample) + add_subdirectory(test/samples) endif() az_vcpkg_export( diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/key_vault.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/key_vault.hpp index 39413d4ae..408a4e228 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/key_vault.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/key_vault.hpp @@ -11,10 +11,16 @@ #include "azure/keyvault/keys/delete_key_operation.hpp" #include "azure/keyvault/keys/deleted_key.hpp" #include "azure/keyvault/keys/dll_import_export.hpp" +#include "azure/keyvault/keys/import_key_options.hpp" #include "azure/keyvault/keys/json_web_key.hpp" #include "azure/keyvault/keys/key_client.hpp" #include "azure/keyvault/keys/key_client_options.hpp" +#include "azure/keyvault/keys/key_create_options.hpp" +#include "azure/keyvault/keys/key_curve_name.hpp" #include "azure/keyvault/keys/key_operation.hpp" #include "azure/keyvault/keys/key_properties.hpp" #include "azure/keyvault/keys/key_type.hpp" #include "azure/keyvault/keys/key_vault_key.hpp" +#include "azure/keyvault/keys/list_keys_single_page_result.hpp" +#include "azure/keyvault/keys/recover_deleted_key_operation.hpp" +#include "azure/keyvault/keys/version.hpp" diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/deleted_key.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/deleted_key.hpp index 083e4c6f3..290fc6163 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/deleted_key.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/deleted_key.hpp @@ -53,12 +53,4 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { */ Azure::DateTime ScheduledPurgeDate; }; - - /*********************** Deserializer / Serializer ******************************/ - namespace _detail { - DeletedKey DeletedKeyDeserialize( - std::string const& name, - Azure::Core::Http::RawResponse const& rawResponse); - } // namespace _detail - }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_backup.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_backup.hpp new file mode 100644 index 000000000..f701ce6f9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_backup.hpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Internal wrapper layer on top of a uint_8 array. + * + */ + +#pragma once + +#include +#include + +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Keys { namespace _detail { + + struct KeyBackup : public Azure::Core::Json::_internal::JsonSerializable + { + + std::vector Value; + + std::string Serialize() const override; + + static KeyBackup Deserialize(Azure::Core::Http::RawResponse const& rawResponse); + }; +}}}}} // namespace Azure::Security::KeyVault::Keys::_detail diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_constants.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_constants.hpp index 5219b338a..a074ec2ce 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_constants.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_constants.hpp @@ -22,6 +22,13 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam constexpr static const char AttributesPropertyName[] = "attributes"; constexpr static const char TagsPropertyName[] = "tags"; constexpr static const char ReleasePolicyPropertyName[] = "release_policy"; + 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"; /***************** Key Request Parameters *****************/ constexpr static const char KeyTypePropertyName[] = "kty"; @@ -69,4 +76,21 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam constexpr static const char P384OidValue[] = "1.3.132.0.34"; constexpr static const char P521OidValue[] = "1.3.132.0.35"; + /***************** Import Key *****************/ + constexpr static const char HsmPropertyName[] = "hsm"; + + /***************** Encryption Algorithm *********/ + constexpr static const char Rsa15Value[] = "RSA1_5"; + constexpr static const char RsaOaepValue[] = "RSA-OAEP"; + constexpr static const char RsaOaep256Value[] = "RSA-OAEP-256"; + constexpr static const char A128GcmValue[] = "A128GCM"; + constexpr static const char A192GcmValue[] = "A192GCM"; + constexpr static const char A256GcmValue[] = "A256GCM"; + constexpr static const char A128CbcValue[] = "A128CBC"; + constexpr static const char A192CbcValue[] = "A192CBC"; + constexpr static const char A256CbcValue[] = "A256CBC"; + constexpr static const char A128CbcPadValue[] = "A128CBCPAD"; + constexpr static const char A192CbcPadValue[] = "A192CBCPAD"; + constexpr static const char A256CbcPadValue[] = "A256CBCPAD"; + }}}}} // namespace Azure::Security::KeyVault::Keys::_detail diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_request_parameters.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_request_parameters.hpp index 7b021d623..dbb19695c 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_request_parameters.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_request_parameters.hpp @@ -17,22 +17,51 @@ #include "azure/keyvault/keys/key_type.hpp" #include +#include #include #include +#include #include namespace Azure { namespace Security { namespace KeyVault { namespace Keys { namespace _detail { class KeyRequestParameters : public Azure::Core::Json::_internal::JsonSerializable { private: - JsonWebKeyType m_keyType; - CreateKeyOptions const& m_options; + Azure::Nullable m_keyType; + CreateKeyOptions m_options; public: Azure::Nullable Curve; Azure::Nullable KeySize; Azure::Nullable PublicExponent; + explicit KeyRequestParameters( + KeyProperties const& key, + Azure::Nullable> const& operations) + : m_options(CreateKeyOptions()) + { + if (key.Enabled) + { + m_options.Enabled = key.Enabled.GetValue(); + } + if (key.ExpiresOn) + { + m_options.ExpiresOn = key.ExpiresOn.GetValue(); + } + if (key.NotBefore) + { + m_options.NotBefore = key.NotBefore.GetValue(); + } + if (key.Tags.size() > 0) + { + m_options.Tags = std::unordered_map(key.Tags); + } + if (operations) + { + m_options.KeyOperations = std::list(operations.GetValue()); + } + } + explicit KeyRequestParameters(JsonWebKeyType keyType, CreateKeyOptions const& options) : m_keyType(keyType), m_options(options) { diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_serializers.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_serializers.hpp new file mode 100644 index 000000000..396b47cdf --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/details/key_serializers.hpp @@ -0,0 +1,93 @@ +// 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/keys/deleted_key.hpp" +#include "azure/keyvault/keys/import_key_options.hpp" +#include "azure/keyvault/keys/key_vault_key.hpp" +#include "azure/keyvault/keys/list_keys_single_page_result.hpp" + +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Keys { namespace _detail { + /***************** KeyVault Key *****************/ + struct KeyVaultKeySerializer + { + // Creates a new key based on a name and an http raw response. + static KeyVaultKey KeyVaultKeyDeserialize( + std::string const& name, + Azure::Core::Http::RawResponse const& rawResponse); + + // Create from http raw response only. + static KeyVaultKey KeyVaultKeyDeserialize(Azure::Core::Http::RawResponse const& rawResponse); + + // Updates a Key based on an Http raw response. + static void KeyVaultKeyDeserialize( + KeyVaultKey& key, + Azure::Core::Http::RawResponse const& rawResponse); + + // Create from json node directly. Used from listKeys + static void KeyVaultKeyDeserialize( + KeyVaultKey& key, + Azure::Core::Json::_internal::json const& json); + + void static inline ParseKeyUrl(KeyProperties& keyProperties, std::string const& url) + { + Azure::Core::Url kid(url); + keyProperties.Id = url; + keyProperties.VaultUrl = kid.GetUrlAuthorityWithScheme(); + 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) + { + keyProperties.Name = std::string(start, separator); + start = separator + 1; + keyProperties.Version = std::string(start, pathEnd); + } + else + { + // Nothing but the name+ + keyProperties.Name = std::string(start, pathEnd); + } + } + }; + + /**************** Deleted Key *******************/ + struct DeletedKeySerializer + { + static DeletedKey DeletedKeyDeserialize( + std::string const& name, + Azure::Core::Http::RawResponse const& rawResponse); + }; + + /**************** Import Key Options ***********/ + struct ImportKeyOptionsSerializer + { + static std::string ImportKeyOptionsSerialize(ImportKeyOptions const& importKeyOptions); + }; + + /**************** Key Properties Single Page ************/ + struct KeyPropertiesSinglePageSerializer + { + static KeyPropertiesSinglePage KeyPropertiesSinglePageDeserialize( + Azure::Core::Http::RawResponse const& rawResponse); + static DeletedKeySinglePage DeletedKeySinglePageDeserialize( + Azure::Core::Http::RawResponse const& rawResponse); + }; + +}}}}} // namespace Azure::Security::KeyVault::Keys::_detail diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/import_key_options.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/import_key_options.hpp new file mode 100644 index 000000000..61b748302 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/import_key_options.hpp @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines the properties to import a Key. + * + */ + +#pragma once + +#include + +#include "azure/keyvault/keys/json_web_key.hpp" +#include "azure/keyvault/keys/key_operation.hpp" +#include "azure/keyvault/keys/key_properties.hpp" + +#include + +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Keys { + + /** + * @brief A key resource and its properties. + * + */ + struct ImportKeyOptions + { + /** + * @brief The cryptographic key, the key type, and the operations you can perform using the key. + * + */ + JsonWebKey Key; + + /** + * @brief The additional properties. + * + */ + KeyProperties Properties; + + /** + * @brief Get or Set a value indicating whether to import the key into a hardware security + * module (HSM). + * + */ + Azure::Nullable HardwareProtected; + + /** + * @brief Construct a new Key Vault ImportKeyOptions object. + * + * @param name The name of the key. + */ + ImportKeyOptions(std::string name, JsonWebKey keyMaterial) + : Key(keyMaterial), Properties(std::move(name)) + { + } + + /** + * @brief Gets the name of the Key. + * + * @return The name of the key. + */ + std::string const& Name() const { return Properties.Name; } + }; +}}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/json_web_key.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/json_web_key.hpp index 4af1262f5..25a217fec 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/json_web_key.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/json_web_key.hpp @@ -9,6 +9,7 @@ #pragma once +#include #include #include "azure/keyvault/keys/key_curve_name.hpp" @@ -71,8 +72,31 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { */ Azure::Nullable CurveName; + /**** RSA fields ****/ + + /// The RSA modulus. + std::vector N; + /// The RSA public exponent. + std::vector E; + /// The RSA private key parameter. + std::vector DP; + /// The RSA private key parameter. + std::vector DQ; + /// The RSA private key parameter. + std::vector QI; + /// The RSA secret prime. + std::vector P; + /// The RSA secret prime. + std::vector Q; + + /// The RSA private exponent or EC private key. + std::vector D; + private: std::vector m_keyOps; }; + // Define the serialization of a JsonWebKey + void to_json(Azure::Core::Json::_internal::json& j, JsonWebKey const& p); + }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp index 76a59fff9..7e22aaff1 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp @@ -12,16 +12,44 @@ #include #include "azure/keyvault/keys/delete_key_operation.hpp" +#include "azure/keyvault/keys/import_key_options.hpp" #include "azure/keyvault/keys/key_client_options.hpp" #include "azure/keyvault/keys/key_create_options.hpp" #include "azure/keyvault/keys/key_type.hpp" #include "azure/keyvault/keys/key_vault_key.hpp" +#include "azure/keyvault/keys/list_keys_single_page_result.hpp" +#include "azure/keyvault/keys/recover_deleted_key_operation.hpp" #include +#include #include namespace Azure { namespace Security { namespace KeyVault { namespace Keys { + /** + * @brief Define a model for a purged key. + * + */ + struct PurgedKey + { + }; + + /** + * @brief Optional parameters for KeyVaultClient::GetKey + * + */ + struct GetKeyOptions + { + /** + * @brief Context for cancelling long running operations. + */ + Azure::Core::Context Context; + /** + * @brief Specify the key version to get. + */ + std::string Version; + }; + /** * @brief The KeyClient provides synchronous methods to manage a KeyVaultKe in the Azure Key * Vault. The client supports creating, retrieving, updating, deleting, purging, backing up, @@ -45,22 +73,6 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::shared_ptr credential, KeyClientOptions options = KeyClientOptions()); - /** - * @brief Optional parameters for KeyVaultClient::GetKey - * - */ - struct GetKeyOptions - { - /** - * @brief Context for cancelling long running operations. - */ - Azure::Core::Context Context; - /** - * @brief Specify the key version to get. - */ - std::string Version; - }; - /** * @brief Gets the public part of a stored key. * @@ -145,6 +157,55 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { CreateOctKeyOptions const& octKeyOptions, Azure::Core::Context const& context = Azure::Core::Context()) const; + /** + * @brief Get a single page with the properties of all keys in the specified vault. You can use + * the returned #KeyProperties.Name in subsequent calls to #GetKey. + * + * @remark Retrieves a list of the keys in the Key Vault that contains the public part of a + * stored key. The operation is applicable to all key types, however only the base key + * identifier, attributes, and tags are provided in the response. Individual versions of a key + * are not listed in the response. This operation requires the keys/list permission. + * + * @remark Use \p options to control which page to get. If + * #GetPropertiesOfKeysSinglePageOptions.NextLink is not set, the operation will get the first + * page and it will set the `ContinuationToken` from the #KeyPropertiesSinglePage as the next + * page of the response if there is a next page. + * + * @param options The #GetPropertiesOfKeysSinglePageOptions object to for setting the operation + * up. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response GetPropertiesOfKeysSinglePage( + GetPropertiesOfKeysSinglePageOptions const& options + = GetPropertiesOfKeysSinglePageOptions(), + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Get one page listing the properties of all the versions of the specified key. You can + * use the returned #KeyProperties.Name and #KeyProperties.Version in subsequent calls to + * #GetKey. + * + * @remark The full identifier, attributes, and tags are provided in the response. This + * operation requires the keys/list permission. + * + * @remark Use \p options to control which page to get. If + * #GetPropertiesOfKeyVersionsSinglePageOptions.NextLink is not set, the operation will get the + * first page and it will set the `ContinuationToken` from the #KeyPropertiesSinglePage as the + * next page of the response if there is a next page. + * + * @param name The name of the key. + * @param options The #GetPropertiesOfKeyVersionsSinglePageOptions object to for setting the + * operation up. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response GetPropertiesOfKeyVersionsSinglePage( + std::string const& name, + GetPropertiesOfKeyVersionsSinglePageOptions const& options + = GetPropertiesOfKeyVersionsSinglePageOptions(), + Azure::Core::Context const& context = Azure::Core::Context()) const; + /** * @brief Deletes a key of any type from storage in Azure Key Vault. * @@ -178,5 +239,156 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { Azure::Response GetDeletedKey( std::string const& name, Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Get on page listing the deleted keys in the specified vault. + * + * @remark Retrieves a list of the keys in the Key Vault that contains the public part od the + * deleted key. This operation includes deletion-specific information. This operation is + * applicable for vaults enabled fr soft-delete. While the operation can be invoked on any + * vault, it will return error if invoked on a non soft-delete enabled vault. This operation + * requires the keys/list permission. + * + * @remark Use \p options to control which page to get. If + * #GetPropertiesOfKeyVersionsSinglePageOptions.NextLink is not set, the operation will get the + * first page and it will set the `ContinuationToken` from the #KeyPropertiesSinglePage as the + * next page of the response if there is a next page. + * + * @param options The #GetDeletedKeysSinglePageOptions object to for setting the operation up. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response GetDeletedKeysSinglePage( + GetDeletedKeysSinglePageOptions const& options = GetDeletedKeysSinglePageOptions(), + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Permanently deletes the specified key. + * + * @remark The Purge Deleted Key operation is applicable for soft-delete enabled values. While + * the operation can be invoked on any vault, it will return an error if invoked on a non + * soft-delete enabled vault. This operation requires the keys/purge permission. + * + * @param name The name of the key. + * @param context A #Azure::Core::Context controlling the request lifetime. + */ + Azure::Response PurgeDeletedKey( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Recovers the deleted key to its latest version. + * + * @remark The Recover Deleted Key operation is applicable for deleted keys in soft-delete + * enabled vaults. It recovers the deleted key back to its latest version under /keys. An + * attempt to recover an non-deleted key will return an error. Consider this the inverse of the + * delete operation on soft-delete enabled vaults. This operation requires the keys/recover + * permission. + * + * @param name The name of the key. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation + */ + Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation StartRecoverDeletedKey( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief The update key operation changes specified attributes of a stored key and can be + * applied to any key type and key version stored in Azure Key Vault. + * + * @remark In order to perform this operation, the key must already exist in the Key Vault. + * Note: The cryptographic material of a key itself cannot be changed. This operation requires + * the keys/update permission. + * + * @param properties The #KeyProperties object with updated properties. + * @param keyOperations Optional list of supported #KeyOperation. If no operation list provided, + * no changes will be made to existing key operations. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response UpdateKeyProperties( + KeyProperties const& properties, + Azure::Nullable> const& keyOperations + = Azure::Nullable>(), + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Request that a backup of the specified be downloaded to the client. + * + * @remark The Key Backup operation exports a key from Azure Key Vault in a protected form. Note + * that this operation does NOT return the actual key in a form that can be used outside the + * Azure Key Vault system, the returned key is either protected to a Azure Key Vault HSM or to + * Azure Key Vault itself. The intent of this operation is to allow a client to GENERATE a key + * in one Azure Key Vault instance, BACKUP the key, and then RESTORE it into another Azure Key + * Vault instance. The BACKUP operation may be used to export, in protected form, any key type + * from Azure Key Vault. Individual versions of a key cannot be backed up. BACKUP / RESTORE can + * be performed within geographical boundaries only; meaning that a BACKUP from one geographical + * are cannot be restored to another geographical are. For example, a backup from the US + * geographical are cannot be restored in an EU geographical area. This operation requires the + * key/backup permission. + * + * @param name The name of the key. + * @param context A #Azure::Core::Context controlling the request lifetime. + */ + Azure::Response> BackupKey( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Restores a backed up key to keyvault. + * + * @remark Imports a previously backed up key into Azure Key Vault, restoring the key, its key + * identifier, attributes, and access control policies. The RESTORE operation may be used to + * import a previously backed up key. Individual versions of a key cannot be restored. The key + * is restored in its entirety with the same key name as it had when it was backed up. If the + * key name is not available in the target Key Vault, the RESTORE operation will be rejected. + * While the key name is retained during restore, the final key identifier will change if the + * key is restored to a different vault. Restore will restore all versions and preserve version + * identifiers. The RESTORE operation is subject to security constrains: The target Key Vault + * must be owned by the same Microsoft Azure Subscription as the source Key Vault. The user must + * have RESTORE permission in the target Key Vault. This operation requires the keys/restore + * permission. + * + * @param backup The backup blob associated with a key. + * @param context A #Azure::Core::Context controlling the request lifetime. + */ + Azure::Response RestoreKeyBackup( + std::vector const& backup, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Imports an externally created ket, stores it, and returns jey parameters and + * attributes to the client. + * + * @remark The import key operation may be used to import any key type into an Azure Key Vault. + * If the named key already exists, Azure Key Vault creates a new version of the key. This + * operation requires the keys/import permission. + * + * @param name The name of the key. + * @param keyMaterial The #JsonWebKey being imported. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response ImportKey( + std::string const& name, + JsonWebKey const& keyMaterial, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Imports an externally created key, stores it, and returns key parameters and + * attributes to the client. + * + * @remark The import operation may be used to import any key type into an Azure Key Vault. If + * the named key already exists, Azure Key Vault creates a new version of the key. This + * operation requires the keys/import permission. + * + * @param importKeyOptions The key import configuration object containing information about the + * #JsonWebKey being imported. + * @param context A #Azure::Core::Context controlling the request lifetime. + */ + Azure::Response ImportKey( + ImportKeyOptions const& importKeyOptions, + Azure::Core::Context const& context = Azure::Core::Context()) const; }; }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp index b5b42832c..5872cae25 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp @@ -9,61 +9,29 @@ #pragma once -#include -#include -#include +#include #include "azure/keyvault/keys/key_vault_key.hpp" namespace Azure { namespace Security { namespace KeyVault { namespace Keys { - /** - * @brief Available and supported service versions. - * - */ - enum class ServiceVersion - { - V7_0, - V7_1, - V7_2 - }; - /** * @brief Define the options to create an SDK Keys client. * */ - struct KeyClientOptions : public Azure::Core::_internal::ClientOptions + struct KeyClientOptions : public Azure::Security::KeyVault::Common::ClientOptions { - /** - * @brief The service version. All request are created with this version. - * - */ - ServiceVersion Version; - /** * @brief Construct a new Key Client Options object. * * @param version Optional version for the client. */ - KeyClientOptions(ServiceVersion version = ServiceVersion::V7_2) - : ClientOptions(), Version(version) + KeyClientOptions( + Azure::Security::KeyVault::Common::ServiceVersion version + = Azure::Security::KeyVault::Common::ServiceVersion::V7_2) + : ClientOptions(version) { } - - std::string GetVersionString() const - { - switch (Version) - { - case ServiceVersion::V7_0: - return "7.0"; - case ServiceVersion::V7_1: - return "7.1"; - case ServiceVersion::V7_2: - return "7.2"; - default: - throw std::runtime_error("Version not found"); - } - } }; }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_properties.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_properties.hpp index a98c0485a..d1ecc493d 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_properties.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_properties.hpp @@ -53,7 +53,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * Key Vault certificate, the value will be true. * */ - bool Managed; + bool Managed = false; /** * @brief Dictionary of tags with specific metadata about the key. diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_type.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_type.hpp index ef217f503..0c1f960fd 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_type.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_type.hpp @@ -52,9 +52,10 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { OctHsm, }; - namespace _detail { - JsonWebKeyType KeyTypeFromString(std::string const& name); - std::string KeyTypeToString(JsonWebKeyType kty); - } // namespace _detail + struct KeyType + { + static JsonWebKeyType KeyTypeFromString(std::string const& name); + static std::string KeyTypeToString(JsonWebKeyType kty); + }; }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_vault_key.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_vault_key.hpp index bc426f3fb..9f83453d4 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_vault_key.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_vault_key.hpp @@ -15,8 +15,8 @@ #include +#include #include - namespace Azure { namespace Security { namespace KeyVault { namespace Keys { /** @@ -78,18 +78,4 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { */ std::vector const& KeyOperations() const { return Key.KeyOperations(); } }; - - /*********************** Deserializer / Serializer ******************************/ - namespace _detail { - // Creates a new key based on a name and an http raw response. - KeyVaultKey KeyVaultKeyDeserialize( - std::string const& name, - Azure::Core::Http::RawResponse const& rawResponse); - - // Updates a Key based on an Http raw response. - void KeyVaultKeyDeserialize( - KeyVaultKey& key, - Azure::Core::Http::RawResponse const& rawResponse); - } // namespace _detail - }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/list_keys_single_page_result.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/list_keys_single_page_result.hpp new file mode 100644 index 000000000..9d6e41068 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/list_keys_single_page_result.hpp @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Defines a page of listing keys from a Key Vault. + * + */ + +#pragma once + +#include "azure/keyvault/keys/deleted_key.hpp" +#include "azure/keyvault/keys/json_web_key.hpp" +#include "azure/keyvault/keys/key_vault_key.hpp" + +#include + +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Keys { + + struct SinglePage + { + Azure::Nullable ContinuationToken; + }; + + struct KeyPropertiesSinglePage : public SinglePage + { + std::vector Items; + }; + + struct DeletedKeySinglePage : public SinglePage + { + std::vector Items; + }; + + struct GetSinglePageOptions + { + Azure::Nullable ContinuationToken; + Azure::Nullable MaxResults; + }; + struct GetPropertiesOfKeysSinglePageOptions : public GetSinglePageOptions + { + }; + + struct GetPropertiesOfKeyVersionsSinglePageOptions : public GetSinglePageOptions + { + }; + + struct GetDeletedKeysSinglePageOptions : public GetSinglePageOptions + { + }; +}}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp new file mode 100644 index 000000000..d4ee32e0e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief A long-running operation for recovering a Key. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "azure/keyvault/keys/key_vault_key.hpp" + +#include +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Keys { + + /** + * @brief A long running operation to recover a key. + * + */ + class RecoverDeletedKeyOperation : public Azure::Core::Operation { + private: + /* RecoverDeletedKeyOperation can be constructed only by friends classes (internal creation). + * The constructor is private and requires internal components.*/ + friend class KeyClient; + + std::shared_ptr m_pipeline; + Azure::Security::KeyVault::Keys::KeyVaultKey m_value; + std::string m_continuationToken; + + std::unique_ptr PollInternal( + Azure::Core::Context& context) override; + + Azure::Response PollUntilDoneInternal( + std::chrono::milliseconds period, + Azure::Core::Context& context) override + { + while (true) + { + // Poll will update the raw response. + Poll(context); + if (IsDone()) + { + break; + } + std::this_thread::sleep_for(period); + } + + return Azure::Response( + m_value, std::make_unique(*m_rawResponse)); + } + + /* + * Only friend classes are permitted to construct a RecoverDeletedKeyOperation. This is because + * a KeyVaultPipelne is required and it is not exposed to customers. + * + * Since C++ doesn't offer `internal` access, we use friends-only instead. + */ + RecoverDeletedKeyOperation( + std::shared_ptr + keyvaultPipeline, + Azure::Response response); + + /** + * @brief Get the #Azure::Core::Http::RawResponse of the operation request. + * @return A reference to an #Azure::Core::Http::RawResponse. + * @note Does not give up ownership of the RawResponse. + */ + Azure::Core::Http::RawResponse const& GetRawResponseInternal() const override + { + return *m_rawResponse; + } + + public: + /** + * @brief Get the #Azure::Security::KeyVault::Keys::KeyVaultKey object. + * + * @remark The deleted key contains the recovery id if the key can be recovered. + * + * @return A deleted key object. + */ + Azure::Security::KeyVault::Keys::KeyVaultKey Value() const override { return m_value; } + + /** + * @brief Get an Url as string which can be used to get the status of the delete key operation. + * + * @return std::string + */ + std::string GetResumeToken() const override { return m_continuationToken; } + }; + +}}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/CMakeLists.txt deleted file mode 100644 index 1fe426a82..000000000 --- a/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# SPDX-License-Identifier: MIT - -cmake_minimum_required (VERSION 3.13) - -project (azure-security-keyvault-keys-sample-get-key LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -add_executable ( - azure-security-keyvault-keys-sample-get-key - main.cpp -) - -target_link_libraries(azure-security-keyvault-keys-sample-get-key PRIVATE azure-security-keyvault-keys azure-identity) diff --git a/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/main.cpp b/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/main.cpp deleted file mode 100644 index d50e897cb..000000000 --- a/sdk/keyvault/azure-security-keyvault-keys/sample/get-key/main.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// SPDX-License-Identifier: MIT - -/** - * @brief The next sample provides the code implementation to use the Key Vault SDK client for C++ - * to create a key client and get a key from Key Vault service. - * - * @remark Make sure to set the next environment variables before running the sample. - * - AZURE_KEYVAULT_URL: To the KeyVault account url. - * - AZURE_KEYVAULT_TENANT_ID: Tenant id for the Azure account. - * - AZURE_KEYVAULT_CLIENT_ID: The client id to authenticate the request. - * - AZURE_KEYVAULT_CLIENT_SECRET: The secret id from the client id. - * - * Also, make sure the key is already created. Then set the key name as `KEY_VAULT_KEY_NAME` before - * the main() method below. - * - * @remark The sample has logging enabled and will log the HTTP response into the standard output. - * - */ - -#if defined(_MSC_VER) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include - -#include -#include - -using namespace Azure::Security::KeyVault::Keys; - -// Define the name of the key to get -#define KEY_VAULT_KEY_NAME "keyName" - -int main() -{ - auto tenantId = std::getenv("AZURE_KEYVAULT_TENANT_ID"); - auto clientId = std::getenv("AZURE_KEYVAULT_CLIENT_ID"); - auto clientSecret = std::getenv("AZURE_KEYVAULT_CLIENT_SECRET"); - auto credential - = std::make_shared(tenantId, clientId, clientSecret); - - KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); - - try - { - auto responseT = keyClient.GetKey(KEY_VAULT_KEY_NAME); - auto key = responseT.ExtractValue(); - std::cout << "KeyId: " << key.Key.Id << std::endl; - std::cout << "Operations:" << std::endl; - for (KeyOperation operation : key.KeyOperations()) - { - std::cout << " - " << operation.ToString() << std::endl; - } - } - catch (Azure::Core::Credentials::AuthenticationException const& e) - { - std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; - } - catch (Azure::Security::KeyVault::Common::KeyVaultException const& e) - { - std::cout << "KeyVault Client Exception happened:" << std::endl << e.Message << std::endl; - } - - return 0; -} diff --git a/sdk/keyvault/azure-security-keyvault-keys/samples/README.md b/sdk/keyvault/azure-security-keyvault-keys/samples/README.md new file mode 100644 index 000000000..4647aeef3 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/samples/README.md @@ -0,0 +1,19 @@ +--- +page_type: sample +languages: + - c++ 14 +products: + - azure + - azure-key-vault +name: Azure Security KeyVault Keys samples for C++ +description: Samples for the azure-security-keyVault-keys client library. +--- + +# Azure.Security.KeyVault.Keys Samples + +- Creating, getting, updating, and deleting keys +- Back up and restore a key +- Listing keys, key versions, and deleted keys +- Encrypting and decrypt keys +- Signing and verifying keys +- Wrapping and unwrap a key diff --git a/sdk/keyvault/azure-security-keyvault-keys/samples/sample1_hello_world.md b/sdk/keyvault/azure-security-keyvault-keys/samples/sample1_hello_world.md new file mode 100644 index 000000000..4dd878387 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/samples/sample1_hello_world.md @@ -0,0 +1,101 @@ +# Creating, getting, updating, and deleting keys + +This sample demonstrates how to create, get, update, and delete a key in Azure Key Vault. +To get started, you'll need a URI to an Azure Key Vault. See the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Keys/README.md) for links and instructions. + +## Creating a KeyClient + +To create a new `KeyClient` to create, get, update, or delete keys, you need the endpoint to an Azure Key Vault and credentials. + +Key Vault Keys client for C++ currently supports the `ClientSecretCredential` for authenticating. + +In the sample below, you can create a credential by setting the tenant id, client id and client secret as environment variables. + +```cpp Snippet:KeysSample1CreateCredential + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential = std::make_shared(tenantId, clientId, clientSecret); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp Snippet:KeysSample1KeyClient +KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); +``` + +## Creating a key + +Let's create an RSA key valid for 1 year. +If the key already exists in the Azure Key Vault, then a new version of the key is created. + +```cpp Snippet:KeysSample1CreateKey +auto rsaKey = CreateRsaKeyOptions(rsaKeyName); +rsaKey.KeySize = 2048; +rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +keyClient.CreateRsaKey(rsaKey); +``` + +## Getting a key + +Let's get the cloud RSA key from the Azure Key Vault. + +```cpp Snippet:KeysSample1GetKey +KeyVaultKey cloudRsaKey = keyClient.GetKey(rsaKeyName).ExtractValue(); +std::cout << "Key is returned with name " << cloudRsaKey.Name() << " and type " + << KeyType::KeyTypeToString(cloudRsaKey.GetKeyType()) << std::endl; + +``` + +## Updating key properties + +After one year, the cloud RSA key is still required, so we need to update the expiry time of the key. +The update method can be used to update the expiry attribute of the key. + +```cpp Snippet:KeysSample1UpdateKeyProperties +cloudRsaKey.Properties.ExpiresOn + = cloudRsaKey.Properties.ExpiresOn.GetValue() + std::chrono::hours(24 * 365); +KeyVaultKey updatedKey = keyClient.UpdateKeyProperties(cloudRsaKey.Properties).ExtractValue(); +std::cout << "Key's updated expiry time is " << updatedKey.Properties.ExpiresOn->ToString() + << std::endl; +``` + +## Updating a key size + +We need the cloud RSA key with bigger key size, so you want to update the key in Azure Key Vault to ensure it has the required size. +Calling `CreateRsaKey` on an existing key creates a new version of the key in the Azure Key Vault with the new specified size. + +```cpp Snippet:KeysSample1UpdateKey +CreateRsaKeyOptions newRsaKey(rsaKeyName); +newRsaKey.KeySize = 4096; +newRsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +keyClient.CreateRsaKey(newRsaKey); +``` + +## Deleting a key + +The cloud RSA key is no longer needed, so we need to delete it from the Key Vault. + +```cpp Snippet:KeysSample1DeleteKey +DeleteKeyOperation operation = keyClient.StartDeleteKey(rsaKeyName); +``` + +## Purging a deleted key + +If the Azure Key Vault is soft delete-enabled and you want to permanently delete the key before its `ScheduledPurgeDate`, +the deleted key needs to be purged. Before it can be purged, you need to wait until the key is fully deleted. + +```cpp Snippet:KeysSample1PurgeKey +// You only need to wait for completion if you want to purge or recover the key. +operation.PollUntilDone(std::chrono::milliseconds(2000)); + +keyClient.PurgeDeletedKey(rsaKeyName); +``` + +## Source + +- sample1_hello_world.cpp + +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md diff --git a/sdk/keyvault/azure-security-keyvault-keys/samples/sample2_backup_and_restore.md b/sdk/keyvault/azure-security-keyvault-keys/samples/sample2_backup_and_restore.md new file mode 100644 index 000000000..7ce822efb --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/samples/sample2_backup_and_restore.md @@ -0,0 +1,64 @@ +# Back up and restore a key + +This sample demonstrates how to back up and restore a Key from Azure Key Vault. +To get started, you'll need a URI to an Azure Key Vault. See the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Keys/README.md) for links and instructions. + +## Creating a KeyClient + +To create a new `KeyClient` to create, get, update, or delete keys, you need the endpoint to an Azure Key Vault and credentials. + +Key Vault Keys client for C++ currently supports the `ClientSecretCredential` for authenticating. + +In the sample below, you can create a credential by setting the tenant id, client id and client secret as environment variables. + +```cpp Snippet:KeysSample1CreateCredential + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential = std::make_shared(tenantId, clientId, clientSecret); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp Snippet:KeysSample1KeyClient +KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); +``` + +## Creating a key + +Let's create an RSA key valid for 1 year. +If the key already exists in the Azure Key Vault, then a new version of the key is created. + +```cpp Snippet:KeysSample1CreateKey +auto rsaKey = CreateRsaKeyOptions(rsaKeyName); +rsaKey.KeySize = 2048; +rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +keyClient.CreateRsaKey(rsaKey); +``` + +## Backing up a key + +You might make backups in case keys get accidentally deleted. +For long term storage, it is ideal to write the backup to a file, disk, database, etc. +For the purposes of this sample, we are storing the back up in a temporary memory area. + +```cpp Snippet:KeysSample2BackupKey +std::vector backupKey(keyClient.BackupKey(rsaKeyName).ExtractValue()); +``` + +## Restoring a key + +If the key is deleted for any reason, we can use the backup value to restore it in the Azure Key Vault. + +```cpp Snippet:KeysSample2RestoreKey +auto restoredKey = keyClient.RestoreKeyBackup(inMemoryBackup).ExtractValue(); +``` + +## Source + +To see the full example source, see: + +- sample2_backup_and_restore.cpp + +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md diff --git a/sdk/keyvault/azure-security-keyvault-keys/samples/sample3_get_keys.md b/sdk/keyvault/azure-security-keyvault-keys/samples/sample3_get_keys.md new file mode 100644 index 000000000..8a220e54f --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/samples/sample3_get_keys.md @@ -0,0 +1,171 @@ +# Listing keys, key versions, and deleted keys + +This sample demonstrates how to list keys and versions of a given key, and list deleted keys in a soft delete-enabled Key Vault. +To get started, you'll need a URI to an Azure Key Vault. See the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Keys/README.md) for links and instructions. + +## Creating a KeyClient + +To create a new `KeyClient` to create, get, update, or delete keys, you need the endpoint to an Azure Key Vault and credentials. + +Key Vault Keys client for C++ currently supports the `ClientSecretCredential` for authenticating. + +In the sample below, you can create a credential by setting the tenant id, client id and client secret as environment variables. + +```cpp Snippet:KeysSample1CreateCredential + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential = std::make_shared(tenantId, clientId, clientSecret); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp Snippet:KeysSample1KeyClient +KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); +``` + +## Creating a key + +Let's create an RSA key valid for 1 year. +If the key already exists in the Azure Key Vault, then a new version of the key is created. + +```cpp Snippet:KeysSample1CreateKey +std::string rsaKeyName("CloudRsaKey-" + Azure::Core::Uuid::CreateUuid().ToString()); +auto rsaKey = CreateRsaKeyOptions(rsaKeyName); +rsaKey.KeySize = 2048; +rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +std::string ecKeyName("CloudEcKey-" + Azure::Core::Uuid::CreateUuid().ToString()); +auto ecKey = CreateEcKeyOptions(ecKeyName); +ecKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +std::cout << "\t-Create Keys" << std::endl; +keyClient.CreateRsaKey(rsaKey); +keyClient.CreateEcKey(ecKey); +``` + +## Listing keys + +You need to check the type of keys that already exist in your Azure Key Vault. +Let's list the keys and print their types. List operations don't return the actual key, but only properties of the key. +So, for each returned key we call GetKey to get the actual key. + +```cpp Snippet:KeysSample3ListKeys +for (auto keysSinglePage = keyClient.GetPropertiesOfKeysSinglePage().ExtractValue();;) +{ + for (auto const& key : keysSinglePage.Items) + { + if (key.Managed) + { + continue; + } + auto keyWithType = keyClient.GetKey(key.Name).ExtractValue(); + std::cout << "Key is returned with name: " << keyWithType.Name() + << " and type: " << KeyType::KeyTypeToString(keyWithType.GetKeyType()) + << std::endl; + } + + if (!keysSinglePage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetPropertiesOfKeysSinglePageOptions options; + options.ContinuationToken = keysSinglePage.ContinuationToken.GetValue(); + keysSinglePage = keyClient.GetPropertiesOfKeysSinglePage(options).ExtractValue(); +} +``` + +## Updating RSA key size + +We need the cloud RSA key with bigger key size, so you want to update the key in Azure Key Vault to ensure it has the required size. +Calling `CreateRsaKey` on an existing key creates a new version of the key in the Azure Key Vault with the new specified size. + +```cpp Snippet:KeysSample3UpdateKey +CreateRsaKeyOptions newRsaKey(rsaKeyName); +newRsaKey.KeySize = 4096; +newRsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + +keyClient.CreateRsaKey(newRsaKey); +``` + +## Listing key versions + +You need to check all the different versions cloud RSA key had previously. +Lets print all the versions of this key. + +```cpp Snippet:KeysSample3ListKeyVersions +for (auto keyVersionsSinglePage + = keyClient.GetPropertiesOfKeyVersionsSinglePage(rsaKeyName).ExtractValue(); + ;) +{ + for (auto const& key : keyVersionsSinglePage.Items) + { + std::cout << "Key's version: " << key.Version << " with name: " << key.Name << std::endl; + } + + if (!keyVersionsSinglePage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetPropertiesOfKeyVersionsSinglePageOptions options; + options.ContinuationToken = keyVersionsSinglePage.ContinuationToken.GetValue(); + keyVersionsSinglePage + = keyClient.GetPropertiesOfKeyVersionsSinglePage(rsaKeyName, options).ExtractValue(); +} +``` + +## Deleting keys + +The cloud RSA Key and the cloud EC keys are no longer needed. +You need to delete them from the Azure Key Vault. + +```cpp Snippet:KeysSample3DeletedKeys +DeleteKeyOperation rsaOperation = keyClient.StartDeleteKey(rsaKeyName); +DeleteKeyOperation ecOperation = keyClient.StartDeleteKey(ecKeyName); + +// You only need to wait for completion if you want to purge or recover the key. +rsaOperation.PollUntilDone(std::chrono::milliseconds(2000)); +ecOperation.PollUntilDone(std::chrono::milliseconds(2000)); +``` + +## Listing deleted keys + +You can list all the deleted and non-purged keys, assuming Azure Key Vault is soft delete-enabled. + +```cpp Snippet:KeysSample3ListDeletedKeys +nextPage = true; +for (auto keysDeletedPage = keyClient.GetDeletedKeysSinglePage().ExtractValue();;) +{ + for (auto const& key : keysDeletedPage.Items) + { + std::cout << "Deleted key's name: " << key.Name() + << ", recovery level: " << key.Properties.RecoveryLevel + << " and recovery Id: " << key.RecoveryId << std::endl; + } + + if (!keysDeletedPage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetDeletedKeysSinglePageOptions options; + options.ContinuationToken = keysDeletedPage.ContinuationToken.GetValue(); + keysDeletedPage = keyClient.GetDeletedKeysSinglePage(options).ExtractValue(); +} +``` + +## Source + +To see the full example source, see: + +- sample3_get_keys.cpp + +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp index cdaf777c8..6ee12bc32 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp @@ -3,6 +3,7 @@ #include "azure/keyvault/keys/delete_key_operation.hpp" #include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" using namespace Azure::Security::KeyVault::Keys; @@ -38,7 +39,7 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(Azure::Core::C m_status = CheckCompleted(*rawResponse); if (m_status == Azure::Core::OperationStatus::Succeeded) { - m_value = _detail::DeletedKeyDeserialize(m_value.Name(), *rawResponse); + m_value = _detail::DeletedKeySerializer::DeletedKeyDeserialize(m_value.Name(), *rawResponse); } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/deleted_key.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/deleted_key.cpp index e3253ce7c..a721588d6 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/deleted_key.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/deleted_key.cpp @@ -1,18 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT -#include "azure/keyvault/keys/deleted_key.hpp" -#include "azure/keyvault/keys/details/key_constants.hpp" -#include "azure/keyvault/keys/key_vault_key.hpp" +#include +#include #include -#include +#include "azure/keyvault/keys/deleted_key.hpp" +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" +#include "azure/keyvault/keys/key_vault_key.hpp" using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; using Azure::Security::KeyVault::Common::_internal::UnixTimeConverter; -DeletedKey _detail::DeletedKeyDeserialize( +DeletedKey _detail::DeletedKeySerializer::DeletedKeyDeserialize( std::string const& name, Azure::Core::Http::RawResponse const& rawResponse) { @@ -21,16 +24,30 @@ DeletedKey _detail::DeletedKeyDeserialize( // "Key" DeletedKey deletedKey(name); - _detail::KeyVaultKeyDeserialize(deletedKey, rawResponse); + _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(deletedKey, rawResponse); // recoveryId // deletedDate // scheduledPurgeDate - deletedKey.RecoveryId = jsonParser[_detail::RecoveryIdPropertyName].get(); - deletedKey.DeletedDate = UnixTimeConverter::UnixTimeToDatetime( - jsonParser[_detail::DeletedOnPropertyName].get()); - deletedKey.ScheduledPurgeDate = UnixTimeConverter::UnixTimeToDatetime( - jsonParser[_detail::ScheduledPurgeDatePropertyName].get()); + if (!jsonParser[_detail::RecoveryIdPropertyName].is_null()) + { + deletedKey.RecoveryId = jsonParser[_detail::RecoveryIdPropertyName].get(); + } + if (!jsonParser[_detail::RecoveryLevelPropertyName].is_null()) + { + deletedKey.Properties.RecoveryLevel + = jsonParser[_detail::RecoveryLevelPropertyName].get(); + } + JsonOptional::SetIfExists( + deletedKey.DeletedDate, + jsonParser, + _detail::DeletedOnPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + JsonOptional::SetIfExists( + deletedKey.ScheduledPurgeDate, + jsonParser, + _detail::ScheduledPurgeDatePropertyName, + UnixTimeConverter::UnixTimeToDatetime); return deletedKey; } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/import_key_options.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/import_key_options.cpp new file mode 100644 index 000000000..a283a181f --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/import_key_options.cpp @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +#include + +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" +#include "azure/keyvault/keys/import_key_options.hpp" + +#include + +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Security::KeyVault::Keys::_detail; +using namespace Azure::Core::Json::_internal; +using namespace Azure::Security::KeyVault::Common::_internal; + +std::string +Azure::Security::KeyVault::Keys::_detail::ImportKeyOptionsSerializer::ImportKeyOptionsSerialize( + ImportKeyOptions const& importKeyOptions) +{ + + Azure::Core::Json::_internal::json payload; + // key + payload[_detail::KeyPropertyName] = importKeyOptions.Key; + + // hsm + SetFromNullable(importKeyOptions.HardwareProtected, payload, _detail::HsmPropertyName); + + // attributes + SetFromNullable( + importKeyOptions.Properties.CreatedOn, + payload[_detail::AttributesPropertyName], + _detail::CreatedPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + SetFromNullable( + importKeyOptions.Properties.Enabled, + payload[_detail::AttributesPropertyName], + _detail::EnabledPropertyName); + SetFromNullable( + importKeyOptions.Properties.ExpiresOn, + payload[_detail::AttributesPropertyName], + _detail::ExpPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + SetFromNullable( + importKeyOptions.Properties.NotBefore, + payload[_detail::AttributesPropertyName], + _detail::NbfPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + SetFromNullable( + importKeyOptions.Properties.RecoverableDays, + payload[_detail::AttributesPropertyName], + _detail::RecoverableDaysPropertyName); + + payload[_detail::RecoveryLevelPropertyName] = importKeyOptions.Properties.RecoveryLevel; + + SetFromNullable( + importKeyOptions.Properties.UpdatedOn, + payload[_detail::AttributesPropertyName], + _detail::UpdatedPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + + // tags + for (auto& tag : importKeyOptions.Properties.Tags) + { + payload[_detail::TagsPropertyName][tag.first] = tag.second; + } + + // release_policy + return payload.dump(); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/json_web_key.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/json_web_key.cpp new file mode 100644 index 000000000..ad26a6e63 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/json_web_key.cpp @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include + +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/json_web_key.hpp" + +#include + +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; +using namespace Azure::Security::KeyVault::Common::_internal; + +void Azure::Security::KeyVault::Keys::to_json( + Azure::Core::Json::_internal::json& j, + JsonWebKey const& p) +{ + j[_detail::KeyTypePropertyName] = KeyType::KeyTypeToString(p.KeyType); + j[_detail::NPropertyName] = Base64Url::Base64UrlEncode(p.N); + j[_detail::EPropertyName] = Base64Url::Base64UrlEncode(p.E); + j[_detail::DPropertyName] = Base64Url::Base64UrlEncode(p.D); + j[_detail::DPPropertyName] = Base64Url::Base64UrlEncode(p.DP); + j[_detail::DQPropertyName] = Base64Url::Base64UrlEncode(p.DQ); + j[_detail::QIPropertyName] = Base64Url::Base64UrlEncode(p.QI); + j[_detail::PPropertyName] = Base64Url::Base64UrlEncode(p.P); + j[_detail::QPropertyName] = Base64Url::Base64UrlEncode(p.Q); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_backup.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_backup.cpp new file mode 100644 index 000000000..faa6e361d --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_backup.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +#include + +#include "azure/keyvault/keys/details/key_backup.hpp" +#include "azure/keyvault/keys/details/key_constants.hpp" + +#include + +using namespace Azure::Security::KeyVault::Keys::_detail; +using namespace Azure::Core::Json::_internal; +using namespace Azure::Security::KeyVault::Common::_internal; + +std::string KeyBackup::Serialize() const +{ + Azure::Core::Json::_internal::json payload; + + payload["value"] = Base64Url::Base64UrlEncode(Value); + + // release_policy + return payload.dump(); +} + +KeyBackup KeyBackup::Deserialize(Azure::Core::Http::RawResponse const& rawResponse) +{ + auto const& body = rawResponse.GetBody(); + auto jsonParser = json::parse(body); + KeyBackup keyBackup; + JsonOptional::SetIfExists>( + keyBackup.Value, jsonParser, "value", [](std::string const& value) { + return Base64Url::Base64UrlDecode(value); + }); + return keyBackup; +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp index 32fd3703a..67ed16b43 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp @@ -5,8 +5,10 @@ #include #include +#include "azure/keyvault/keys/details/key_backup.hpp" #include "azure/keyvault/keys/details/key_constants.hpp" #include "azure/keyvault/keys/details/key_request_parameters.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" #include "azure/keyvault/keys/key_client.hpp" #include @@ -17,6 +19,41 @@ using namespace Azure::Security::KeyVault::Keys; using namespace Azure::Core::Http; using namespace Azure::Core::Http::Policies; +namespace { +struct RequestWithContinuationToken +{ + std::vector Path; + std::unique_ptr> Query; +}; + +static inline RequestWithContinuationToken BuildRequestFromContinuationToken( + GetSinglePageOptions const& options, + std::vector&& defaultPath) +{ + RequestWithContinuationToken request; + request.Path = defaultPath; + if (options.ContinuationToken) + { + // Using a continuation token requires to send the request to the continuation token url instead + // of the default url which is used only for the first page. + Azure::Core::Url nextPageUrl(options.ContinuationToken.GetValue()); + request.Query + = std::make_unique>(nextPageUrl.GetQueryParameters()); + request.Path.clear(); + request.Path.emplace_back(nextPageUrl.GetPath()); + } + if (options.MaxResults) + { + if (request.Query == nullptr) + { + request.Query = std::make_unique>(); + } + request.Query->emplace("maxResults", std::to_string(options.MaxResults.GetValue())); + } + return request; +} +} // namespace + KeyClient::KeyClient( std::string const& vaultUrl, std::shared_ptr credential, @@ -49,7 +86,7 @@ Azure::Response KeyClient::GetKey( context, Azure::Core::Http::HttpMethod::Get, [&name](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::KeyVaultKeyDeserialize(name, rawResponse); + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(name, rawResponse); }, {_detail::KeysPath, name, options.Version}); } @@ -65,7 +102,7 @@ Azure::Response KeyClient::CreateKey( Azure::Core::Http::HttpMethod::Post, _detail::KeyRequestParameters(keyType, options), [&name](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::KeyVaultKeyDeserialize(name, rawResponse); + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(name, rawResponse); }, {_detail::KeysPath, name, "create"}); } @@ -80,7 +117,7 @@ Azure::Response KeyClient::CreateEcKey( Azure::Core::Http::HttpMethod::Post, _detail::KeyRequestParameters(ecKeyOptions), [&keyName](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::KeyVaultKeyDeserialize(keyName, rawResponse); + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(keyName, rawResponse); }, {_detail::KeysPath, keyName, "create"}); } @@ -95,7 +132,7 @@ Azure::Response KeyClient::CreateRsaKey( Azure::Core::Http::HttpMethod::Post, _detail::KeyRequestParameters(rsaKeyOptions), [&keyName](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::KeyVaultKeyDeserialize(keyName, rawResponse); + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(keyName, rawResponse); }, {_detail::KeysPath, keyName, "create"}); } @@ -110,11 +147,45 @@ Azure::Response KeyClient::CreateOctKey( Azure::Core::Http::HttpMethod::Post, _detail::KeyRequestParameters(octKeyOptions), [&keyName](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::KeyVaultKeyDeserialize(keyName, rawResponse); + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(keyName, rawResponse); }, {_detail::KeysPath, keyName, "create"}); } +Azure::Response KeyClient::GetPropertiesOfKeysSinglePage( + GetPropertiesOfKeysSinglePageOptions const& options, + Azure::Core::Context const& context) const +{ + auto const request = BuildRequestFromContinuationToken(options, {_detail::KeysPath}); + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Get, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyPropertiesSinglePageSerializer::KeyPropertiesSinglePageDeserialize( + rawResponse); + }, + request.Path, + request.Query); +} + +Azure::Response KeyClient::GetPropertiesOfKeyVersionsSinglePage( + std::string const& name, + GetPropertiesOfKeyVersionsSinglePageOptions const& options, + Azure::Core::Context const& context) const +{ + auto const request + = BuildRequestFromContinuationToken(options, {_detail::KeysPath, name, "versions"}); + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Get, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyPropertiesSinglePageSerializer::KeyPropertiesSinglePageDeserialize( + rawResponse); + }, + request.Path, + request.Query); +} + Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::StartDeleteKey( std::string const& name, Azure::Core::Context const& context) const @@ -125,11 +196,26 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::StartDeleteKey( context, Azure::Core::Http::HttpMethod::Delete, [&name](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::DeletedKeyDeserialize(name, rawResponse); + return _detail::DeletedKeySerializer::DeletedKeyDeserialize(name, rawResponse); }, {_detail::KeysPath, name})); } +Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation KeyClient::StartRecoverDeletedKey( + std::string const& name, + Azure::Core::Context const& context) const +{ + return Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation( + m_pipeline, + m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Post, + [&name](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(name, rawResponse); + }, + {_detail::DeletedKeysPath, name, "recover"})); +} + Azure::Response KeyClient::GetDeletedKey( std::string const& name, Azure::Core::Context const& context) const @@ -138,7 +224,118 @@ Azure::Response KeyClient::GetDeletedKey( context, Azure::Core::Http::HttpMethod::Get, [&name](Azure::Core::Http::RawResponse const& rawResponse) { - return _detail::DeletedKeyDeserialize(name, rawResponse); + return _detail::DeletedKeySerializer::DeletedKeyDeserialize(name, rawResponse); }, {_detail::DeletedKeysPath, name}); } + +Azure::Response KeyClient::GetDeletedKeysSinglePage( + GetDeletedKeysSinglePageOptions const& options, + Azure::Core::Context const& context) const +{ + auto const request = BuildRequestFromContinuationToken(options, {_detail::DeletedKeysPath}); + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Get, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyPropertiesSinglePageSerializer::DeletedKeySinglePageDeserialize( + rawResponse); + }, + request.Path, + request.Query); +} + +Azure::Response KeyClient::PurgeDeletedKey( + std::string const& name, + Azure::Core::Context const& context) const +{ + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Delete, + [](Azure::Core::Http::RawResponse const&) { return PurgedKey(); }, + {_detail::DeletedKeysPath, name}); +} + +Azure::Response KeyClient::UpdateKeyProperties( + KeyProperties const& properties, + Azure::Nullable> const& keyOperations, + Azure::Core::Context const& context) const +{ + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Patch, + _detail::KeyRequestParameters(properties, keyOperations), + [&properties](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(properties.Name, rawResponse); + }, + {_detail::KeysPath, properties.Name, properties.Version}); +} + +Azure::Response> KeyClient::BackupKey( + std::string const& name, + Azure::Core::Context const& context) const +{ + // Use the internal model KeyBackup to parse from Json + auto response = m_pipeline->SendRequest<_detail::KeyBackup>( + context, + Azure::Core::Http::HttpMethod::Post, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyBackup::Deserialize(rawResponse); + }, + {_detail::KeysPath, name, "backup"}); + + // Convert the internal KeyBackup model to a raw vector. + return Azure::Response>( + response.ExtractValue().Value, response.ExtractRawResponse()); +} + +Azure::Response KeyClient::RestoreKeyBackup( + std::vector const& backup, + Azure::Core::Context const& context) const +{ + _detail::KeyBackup backupModel; + backupModel.Value = backup; + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Post, + backupModel, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(rawResponse); + }, + {_detail::KeysPath, "restore"}); +} + +Azure::Response KeyClient::ImportKey( + std::string const& name, + JsonWebKey const& keyMaterial, + Azure::Core::Context const& context) const +{ + ImportKeyOptions const importKeyOptions(name, keyMaterial); + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Put, + [&importKeyOptions]() { + return _detail::ImportKeyOptionsSerializer::ImportKeyOptionsSerialize(importKeyOptions); + }, + [&name](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(name, rawResponse); + }, + {_detail::KeysPath, name}); +} + +Azure::Response KeyClient::ImportKey( + ImportKeyOptions const& importKeyOptions, + Azure::Core::Context const& context) const +{ + return m_pipeline->SendRequest( + context, + Azure::Core::Http::HttpMethod::Put, + [&importKeyOptions]() { + return _detail::ImportKeyOptionsSerializer::ImportKeyOptionsSerialize(importKeyOptions); + }, + [&importKeyOptions](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize( + importKeyOptions.Name(), rawResponse); + }, + {_detail::KeysPath, importKeyOptions.Name()}); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_request_parameters.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_request_parameters.cpp index ff18c5565..601e3ac89 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_request_parameters.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_request_parameters.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: MIT #include +#include + +#include #include "azure/keyvault/keys/details/key_constants.hpp" #include "azure/keyvault/keys/details/key_request_parameters.hpp" @@ -9,14 +12,22 @@ #include using namespace Azure::Security::KeyVault::Keys::_detail; +using namespace Azure::Core::Json::_internal; +using namespace Azure::Security::KeyVault::Common::_internal; std::string KeyRequestParameters::Serialize() const { Azure::Core::Json::_internal::json payload; - /* Mandatory */ // kty - payload[_detail::KeyTypePropertyName] = KeyTypeToString(m_keyType); + SetFromNullable( + m_keyType, payload, _detail::KeyTypePropertyName, [](JsonWebKeyType type) { + return KeyType::KeyTypeToString(type); + }); + + // attributes + SetFromNullable( + m_options.Enabled, payload[_detail::AttributesPropertyName], _detail::EnabledPropertyName); /* Optional */ // key_size @@ -28,6 +39,18 @@ std::string KeyRequestParameters::Serialize() const } // attributes + SetFromNullable( + m_options.ExpiresOn, + payload[_detail::AttributesPropertyName], + _detail::ExpPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + + SetFromNullable( + m_options.NotBefore, + payload[_detail::AttributesPropertyName], + _detail::NbfPropertyName, + UnixTimeConverter::DatetimeToUnixTime); + // tags for (auto tag : m_options.Tags) { diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_type.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_type.cpp index e7c661dc0..fab821af2 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_type.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_type.cpp @@ -8,60 +8,60 @@ using namespace Azure::Security::KeyVault::Keys; -JsonWebKeyType _detail::KeyTypeFromString(std::string const& name) +JsonWebKeyType KeyType::KeyTypeFromString(std::string const& name) { - if (name == EcValue) + if (name == _detail::EcValue) { return JsonWebKeyType::Ec; } - if (name == EcHsmValue) + if (name == _detail::EcHsmValue) { return JsonWebKeyType::EcHsm; } - if (name == OctValue) + if (name == _detail::OctValue) { return JsonWebKeyType::Oct; } - if (name == OctHsmValue) + if (name == _detail::OctHsmValue) { return JsonWebKeyType::OctHsm; } - if (name == RsaValue) + if (name == _detail::RsaValue) { return JsonWebKeyType::Rsa; } - if (name == RsaHsmValue) + if (name == _detail::RsaHsmValue) { return JsonWebKeyType::RsaHsm; } throw std::runtime_error("cannot convert " + name + " to key type (kty)"); } -std::string _detail::KeyTypeToString(JsonWebKeyType kty) +std::string KeyType::KeyTypeToString(JsonWebKeyType kty) { if (kty == JsonWebKeyType::Ec) { - return EcValue; + return _detail::EcValue; } if (kty == JsonWebKeyType::EcHsm) { - return EcHsmValue; + return _detail::EcHsmValue; } if (kty == JsonWebKeyType::Oct) { - return OctValue; + return _detail::OctValue; } if (kty == JsonWebKeyType::OctHsm) { - return OctHsmValue; + return _detail::OctHsmValue; } if (kty == JsonWebKeyType::Rsa) { - return RsaValue; + return _detail::RsaValue; } if (kty == JsonWebKeyType::RsaHsm) { - return RsaHsmValue; + return _detail::RsaHsmValue; } return std::string(); } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp index 9ce46abc3..a52d0b590 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp @@ -1,15 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT -#include "azure/keyvault/keys/key_vault_key.hpp" -#include "azure/keyvault/keys/details/key_constants.hpp" -#include "azure/keyvault/keys/key_curve_name.hpp" - -#include - #include #include #include +#include + +#include + +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" +#include "azure/keyvault/keys/key_curve_name.hpp" +#include "azure/keyvault/keys/key_vault_key.hpp" using namespace Azure::Security::KeyVault::Keys; using namespace Azure::Core::Json::_internal; @@ -27,22 +29,36 @@ void ParseStringOperationsToKeyOperations( } } // namespace -KeyVaultKey _detail::KeyVaultKeyDeserialize( +KeyVaultKey _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize( std::string const& name, Azure::Core::Http::RawResponse const& rawResponse) { KeyVaultKey key(name); - _detail::KeyVaultKeyDeserialize(key, rawResponse); + _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(key, rawResponse); return key; } -void _detail::KeyVaultKeyDeserialize( +KeyVaultKey _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + KeyVaultKey key; + _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(key, rawResponse); + return key; +} + +void _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize( KeyVaultKey& key, Azure::Core::Http::RawResponse const& rawResponse) { - auto body = rawResponse.GetBody(); + auto const& body = rawResponse.GetBody(); auto jsonParser = json::parse(body); + _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(key, jsonParser); +} +void _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize( + KeyVaultKey& key, + Azure::Core::Json::_internal::json const& jsonParser) +{ // "Key" if (jsonParser.contains(_detail::KeyPropertyName)) { @@ -57,7 +73,7 @@ void _detail::KeyVaultKeyDeserialize( } key.Key.Id = jsonKey[_detail::KeyIdPropertyName].get(); key.Key.KeyType - = _detail::KeyTypeFromString(jsonKey[_detail::KeyTypePropertyName].get()); + = KeyType::KeyTypeFromString(jsonKey[_detail::KeyTypePropertyName].get()); JsonOptional::SetIfExists( key.Key.CurveName, jsonKey, _detail::CurveNamePropertyName, [](std::string const& keyName) { @@ -65,20 +81,36 @@ void _detail::KeyVaultKeyDeserialize( }); } + // Parse URL for the vaultUri, keyVersion + _detail::KeyVaultKeySerializer::ParseKeyUrl(key.Properties, key.Key.Id); + // "Attributes" if (jsonParser.contains(_detail::AttributesPropertyName)) { auto attributes = jsonParser[_detail::AttributesPropertyName]; - JsonOptional::SetIfExists(key.Properties.Enabled, attributes, "enabled"); + JsonOptional::SetIfExists(key.Properties.Enabled, attributes, _detail::EnabledPropertyName); + JsonOptional::SetIfExists( - key.Properties.NotBefore, attributes, "nbf", UnixTimeConverter::UnixTimeToDatetime); + key.Properties.NotBefore, + attributes, + _detail::NbfPropertyName, + UnixTimeConverter::UnixTimeToDatetime); JsonOptional::SetIfExists( - key.Properties.ExpiresOn, attributes, "exp", UnixTimeConverter::UnixTimeToDatetime); + key.Properties.ExpiresOn, + attributes, + _detail::ExpPropertyName, + UnixTimeConverter::UnixTimeToDatetime); JsonOptional::SetIfExists( - key.Properties.CreatedOn, attributes, "created", UnixTimeConverter::UnixTimeToDatetime); + key.Properties.CreatedOn, + attributes, + _detail::CreatedPropertyName, + UnixTimeConverter::UnixTimeToDatetime); JsonOptional::SetIfExists( - key.Properties.UpdatedOn, attributes, "updated", UnixTimeConverter::UnixTimeToDatetime); + key.Properties.UpdatedOn, + attributes, + _detail::UpdatedPropertyName, + UnixTimeConverter::UnixTimeToDatetime); } // "Tags" diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/list_keys_single_page_result.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/list_keys_single_page_result.cpp new file mode 100644 index 000000000..5f84ad141 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/list_keys_single_page_result.cpp @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure/keyvault/keys/list_keys_single_page_result.hpp" +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" + +#include + +#include +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; +using Azure::Security::KeyVault::Common::_internal::UnixTimeConverter; + +KeyPropertiesSinglePage +_detail::KeyPropertiesSinglePageSerializer::KeyPropertiesSinglePageDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + KeyPropertiesSinglePage result; + auto const& body = rawResponse.GetBody(); + auto jsonParser = json::parse(body); + + JsonOptional::SetIfExists(result.ContinuationToken, jsonParser, "nextLink"); + + // Key properties + auto keyPropertiesJson = jsonParser["value"]; + for (auto const& key : keyPropertiesJson) + { + KeyProperties keyProperties; + keyProperties.Id = key[_detail::KeyIdPropertyName].get(); + _detail::KeyVaultKeySerializer::ParseKeyUrl(keyProperties, keyProperties.Id); + // "Attributes" + if (key.contains(_detail::AttributesPropertyName)) + { + auto attributes = key[_detail::AttributesPropertyName]; + + JsonOptional::SetIfExists(keyProperties.Enabled, attributes, _detail::EnabledPropertyName); + JsonOptional::SetIfExists( + keyProperties.NotBefore, + attributes, + _detail::NbfPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + JsonOptional::SetIfExists( + keyProperties.ExpiresOn, + attributes, + _detail::ExpPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + JsonOptional::SetIfExists( + keyProperties.CreatedOn, + attributes, + _detail::CreatedPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + JsonOptional::SetIfExists( + keyProperties.UpdatedOn, + attributes, + _detail::UpdatedPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + } + + // "Tags" + if (key.contains(_detail::TagsPropertyName)) + { + auto const& tags = key[_detail::TagsPropertyName]; + for (auto tag = tags.begin(); tag != tags.end(); ++tag) + { + keyProperties.Tags.emplace(tag.key(), tag.value().get()); + } + } + + // managed + if (key.contains(_detail::ManagedPropertyName)) + { + keyProperties.Managed = key[_detail::ManagedPropertyName].get(); + } + + result.Items.emplace_back(keyProperties); + } + + return result; +} + +DeletedKeySinglePage _detail::KeyPropertiesSinglePageSerializer::DeletedKeySinglePageDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + auto const& body = rawResponse.GetBody(); + auto jsonParser = Azure::Core::Json::_internal::json::parse(body); + + DeletedKeySinglePage deletedKeySinglePage; + JsonOptional::SetIfExists(deletedKeySinglePage.ContinuationToken, jsonParser, "nextLink"); + + auto deletedKeys = jsonParser["value"]; + for (auto const& key : deletedKeys) + { + DeletedKey deletedKey; + deletedKey.Properties.Id = key[_detail::KeyIdPropertyName].get(); + _detail::KeyVaultKeySerializer::ParseKeyUrl(deletedKey.Properties, deletedKey.Properties.Id); + + if (!key[_detail::RecoveryIdPropertyName].is_null()) + { + deletedKey.RecoveryId = key[_detail::RecoveryIdPropertyName].get(); + } + if (!key[_detail::AttributesPropertyName][_detail::RecoveryLevelPropertyName].is_null()) + { + deletedKey.Properties.RecoveryLevel + = key[_detail::AttributesPropertyName][_detail::RecoveryLevelPropertyName] + .get(); + } + JsonOptional::SetIfExists( + deletedKey.DeletedDate, + key, + _detail::DeletedOnPropertyName, + UnixTimeConverter::UnixTimeToDatetime); + JsonOptional::SetIfExists( + deletedKey.ScheduledPurgeDate, + key, + _detail::ScheduledPurgeDatePropertyName, + UnixTimeConverter::UnixTimeToDatetime); + + deletedKeySinglePage.Items.emplace_back(deletedKey); + } + + return deletedKeySinglePage; +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp new file mode 100644 index 000000000..939cc3c3c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure/keyvault/keys/recover_deleted_key_operation.hpp" +#include "azure/keyvault/keys/details/key_constants.hpp" +#include "azure/keyvault/keys/details/key_serializers.hpp" + +using namespace Azure::Security::KeyVault::Keys; + +namespace { + +// For delete key, the LRO ends when we can retreive the Key from the deleted keys list from the +// server. +inline Azure::Core::OperationStatus CheckCompleted(Azure::Core::Http::RawResponse const& response) +{ + auto const code = response.GetStatusCode(); + switch (code) + { + case Azure::Core::Http::HttpStatusCode::Ok: + // Access denied but proof the key was deleted. + case Azure::Core::Http::HttpStatusCode::Forbidden: + return Azure::Core::OperationStatus::Succeeded; + case Azure::Core::Http::HttpStatusCode::NotFound: + return Azure::Core::OperationStatus::Running; + default: + throw Azure::Security::KeyVault::Common::KeyVaultException::CreateFromResponse(response); + } +} +} // namespace + +std::unique_ptr +Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::PollInternal( + Azure::Core::Context& context) +{ + std::unique_ptr rawResponse; + if (!IsDone()) + { + rawResponse = m_pipeline->Send( + context, + Azure::Core::Http::HttpMethod::Get, + {_detail::KeysPath, m_value.Name(), m_value.Properties.Version}); + m_status = CheckCompleted(*rawResponse); + if (m_status == Azure::Core::OperationStatus::Succeeded) + { + m_value + = _detail::KeyVaultKeySerializer::KeyVaultKeyDeserialize(m_value.Name(), *rawResponse); + } + } + + // To ensure the success of calling Poll multiple times, even after operation is completed, a + // copy of the raw http response is returned instead of transfering the ownership of the raw + // response inside the Operation. + return rawResponse; +} + +Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::RecoverDeletedKeyOperation( + std::shared_ptr + keyvaultPipeline, + Azure::Response response) + : m_pipeline(keyvaultPipeline) +{ + if (!response.HasValue()) + { + throw Azure::Security::KeyVault::Common::KeyVaultException( + "The response does not contain a value."); + } + // The response becomes useless and the value and rawResponse are now owned by the + // DeleteKeyOperation. This is fine because the DeleteKeyOperation is what the delete key api + // will return. + m_value = response.ExtractValue(); + m_rawResponse = response.ExtractRawResponse(); + + // Build the full url for continuation token. It is only used in case customers wants to use + // it on their own. The Operation uses the KeyVaultPipeline from the client which knows how to + // build this url. + m_continuationToken = m_pipeline->GetVaultUrl() + "/" + std::string(_detail::DeletedKeysPath) + + "/" + m_value.Name(); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/sample/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/samples/CMakeLists.txt similarity index 52% rename from sdk/keyvault/azure-security-keyvault-keys/sample/CMakeLists.txt rename to sdk/keyvault/azure-security-keyvault-keys/test/samples/CMakeLists.txt index 018539c7f..f8eb2c605 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/sample/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/CMakeLists.txt @@ -3,4 +3,6 @@ cmake_minimum_required (VERSION 3.13) -add_subdirectory(get-key) +add_subdirectory(sample1-hello-world) +add_subdirectory(sample2-backup-and-restore) +add_subdirectory(sample3-get-keys) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/CMakeLists.txt new file mode 100644 index 000000000..3403fbeaf --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +project (sample1-hello-world LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + sample1-hello-world + sample1_hello_world.cpp +) + +target_link_libraries(sample1-hello-world PRIVATE azure-security-keyvault-keys azure-identity) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/sample1_hello_world.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/sample1_hello_world.cpp new file mode 100644 index 000000000..a750dcaca --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample1-hello-world/sample1_hello_world.cpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief This sample provides the code implementation to use the Key Vault SDK client for C++ + * to create, get, update, delete and purge a key. + * + * @remark The following environment variables must be set before running the sample. + * - AZURE_KEYVAULT_URL: To the KeyVault account url. + * - AZURE_TENANT_ID: Tenant id for the Azure account. + * - AZURE_CLIENT_ID: The client id to authenticate the request. + * - AZURE_CLIENT_SECRET: The secret id from the client id. + * + */ + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys; + +int main() +{ + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential + = std::make_shared(tenantId, clientId, clientSecret); + + KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); + + std::string rsaKeyName("CloudRsaKey" + Azure::Core::Uuid::CreateUuid().ToString()); + try + { + auto rsaKey = CreateRsaKeyOptions(rsaKeyName); + rsaKey.KeySize = 2048; + rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + keyClient.CreateRsaKey(rsaKey); + + KeyVaultKey cloudRsaKey = keyClient.GetKey(rsaKeyName).ExtractValue(); + std::cout << "Key is returned with name " << cloudRsaKey.Name() << " and type " + << KeyType::KeyTypeToString(cloudRsaKey.GetKeyType()) << std::endl; + + cloudRsaKey.Properties.ExpiresOn + = cloudRsaKey.Properties.ExpiresOn.GetValue() + std::chrono::hours(24 * 365); + KeyVaultKey updatedKey = keyClient.UpdateKeyProperties(cloudRsaKey.Properties).ExtractValue(); + std::cout << "Key's updated expiry time is " << updatedKey.Properties.ExpiresOn->ToString() + << std::endl; + + CreateRsaKeyOptions newRsaKey(rsaKeyName); + newRsaKey.KeySize = 4096; + newRsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + keyClient.CreateRsaKey(newRsaKey); + + DeleteKeyOperation operation = keyClient.StartDeleteKey(rsaKeyName); + + // You only need to wait for completion if you want to purge or recover the key. + operation.PollUntilDone(std::chrono::milliseconds(2000)); + + keyClient.PurgeDeletedKey(rsaKeyName); + } + catch (Azure::Core::Credentials::AuthenticationException const& e) + { + std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; + return 1; + } + catch (Azure::Security::KeyVault::Common::KeyVaultException const& e) + { + std::cout << "KeyVault Client Exception happened:" << std::endl << e.Message << std::endl; + return 1; + } + + return 0; +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/CMakeLists.txt new file mode 100644 index 000000000..dc8a7701e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +project (sample2-backup-and-restore LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + sample2-backup-and-restore + sample2_backup_and_restore.cpp +) + +target_link_libraries(sample2-backup-and-restore PRIVATE azure-security-keyvault-keys azure-identity) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/sample2_backup_and_restore.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/sample2_backup_and_restore.cpp new file mode 100644 index 000000000..44cdccffc --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample2-backup-and-restore/sample2_backup_and_restore.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief This sample provides the code implementation to use the Key Vault SDK client for C++ + * to back up and restore a key. + * + * @remark The following environment variables must be set before running the sample. + * - AZURE_KEYVAULT_URL: To the KeyVault account url. + * - AZURE_TENANT_ID: Tenant id for the Azure account. + * - AZURE_CLIENT_ID: The client id to authenticate the request. + * - AZURE_CLIENT_SECRET: The secret id from the client id. + * + */ + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys; + +static void AssertKeysEqual(KeyProperties const& expected, KeyProperties const& actual); + +int main() +{ + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential + = std::make_shared(tenantId, clientId, clientSecret); + + KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); + + std::string rsaKeyName("CloudRsaKey" + Azure::Core::Uuid::CreateUuid().ToString()); + try + { + auto rsaKey = CreateRsaKeyOptions(rsaKeyName); + rsaKey.KeySize = 2048; + rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + std::cout << "\t-Create Key" << std::endl; + auto storedKey = keyClient.CreateRsaKey(rsaKey).ExtractValue(); + size_t backUpSize = 0; + { + std::cout << "\t-Backup Key" << std::endl; + std::vector backupKey(keyClient.BackupKey(rsaKeyName).ExtractValue()); + backUpSize = backupKey.size(); + + // save data to file + std::cout << "\t-Save to file" << std::endl; + std::ofstream savedFile; + savedFile.open("backup.dat"); + for (auto const& data : backupKey) + { + savedFile << data; + } + savedFile.close(); + } + // backup key is destroy at this point as it is out of the scope. + // The storage account key is no longer in use, so you delete it. + std::cout << "\t-Delete and purge key" << std::endl; + DeleteKeyOperation operation = keyClient.StartDeleteKey(rsaKeyName); + // You only need to wait for completion if you want to purge or recover the key. + operation.PollUntilDone(std::chrono::milliseconds(2000)); + keyClient.PurgeDeletedKey(rsaKeyName); + // let's wait for one minute so we know the key was purged. + std::this_thread::sleep_for(std::chrono::seconds(60)); + + // Restore the key from the file backup + std::cout << "\t-Read from file." << std::endl; + std::ifstream inFile; + inFile.open("backup.dat"); + std::vector inMemoryBackup(backUpSize); + inFile >> inMemoryBackup.data(); + inFile.close(); + + std::cout << "\t-Restore Key" << std::endl; + auto restoredKey = keyClient.RestoreKeyBackup(inMemoryBackup).ExtractValue(); + + AssertKeysEqual(storedKey.Properties, restoredKey.Properties); + + operation = keyClient.StartDeleteKey(rsaKeyName); + // You only need to wait for completion if you want to purge or recover the key. + operation.PollUntilDone(std::chrono::milliseconds(2000)); + keyClient.PurgeDeletedKey(rsaKeyName); + } + catch (Azure::Core::Credentials::AuthenticationException const& e) + { + std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; + return 1; + } + catch (Azure::Security::KeyVault::Common::KeyVaultException const& e) + { + std::cout << "KeyVault Client Exception happened:" << std::endl << e.Message << std::endl; + return 1; + } + + return 0; +} + +template +static inline bool CompareNullableT(Azure::Nullable const& left, Azure::Nullable const& right) +{ + if (!left.HasValue() && !right.HasValue()) + { + return true; + } + if (left.HasValue() && !right.HasValue()) + { + return false; + } + if (!left.HasValue() && right.HasValue()) + { + return false; + } + return left.GetValue() == right.GetValue(); +} + +void AssertKeysEqual(KeyProperties const& expected, KeyProperties const& actual) +{ +#if defined(NDEBUG) + // Use (void) to silent unused warnings. + (void)expected; + (void)actual; +#endif + assert(expected.Name == actual.Name); + assert(expected.Version == actual.Version); + assert(expected.Managed == actual.Managed); + assert(expected.RecoveryLevel == actual.RecoveryLevel); + assert(CompareNullableT(expected.ExpiresOn, actual.ExpiresOn)); + assert(CompareNullableT(expected.NotBefore, actual.NotBefore)); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/CMakeLists.txt new file mode 100644 index 000000000..d1fbe1cb2 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +project (sample3-get-keys LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + sample3-get-keys + sample3_get_keys.cpp +) + +target_link_libraries(sample3-get-keys PRIVATE azure-security-keyvault-keys azure-identity) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/sample3_get_keys.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/sample3_get_keys.cpp new file mode 100644 index 000000000..a8840585b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/samples/sample3-get-keys/sample3_get_keys.cpp @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief This sample provides the code implementation to use the Key Vault SDK client for C++ + * to list keys and versions of a given key, and list deleted keys in a soft-delete enabled Key + * Vault. + * + * @remark The following environment variables must be set before running the sample. + * - AZURE_KEYVAULT_URL: To the KeyVault account url. + * - AZURE_TENANT_ID: Tenant id for the Azure account. + * - AZURE_CLIENT_ID: The client id to authenticate the request. + * - AZURE_CLIENT_SECRET: The secret id from the client id. + * + */ + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys; + +int main() +{ + auto tenantId = std::getenv("AZURE_TENANT_ID"); + auto clientId = std::getenv("AZURE_CLIENT_ID"); + auto clientSecret = std::getenv("AZURE_CLIENT_SECRET"); + auto credential + = std::make_shared(tenantId, clientId, clientSecret); + + KeyClient keyClient(std::getenv("AZURE_KEYVAULT_URL"), credential); + + try + { + std::string rsaKeyName("CloudRsaKey-" + Azure::Core::Uuid::CreateUuid().ToString()); + auto rsaKey = CreateRsaKeyOptions(rsaKeyName); + rsaKey.KeySize = 2048; + rsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + std::string ecKeyName("CloudEcKey-" + Azure::Core::Uuid::CreateUuid().ToString()); + auto ecKey = CreateEcKeyOptions(ecKeyName); + ecKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + std::cout << "\t-Create Keys" << std::endl; + keyClient.CreateRsaKey(rsaKey); + keyClient.CreateEcKey(ecKey); + + std::cout << "\t-List Keys" << std::endl; + for (auto keysSinglePage = keyClient.GetPropertiesOfKeysSinglePage().ExtractValue();;) + { + for (auto const& key : keysSinglePage.Items) + { + if (key.Managed) + { + continue; + } + auto keyWithType = keyClient.GetKey(key.Name).ExtractValue(); + std::cout << "Key is returned with name: " << keyWithType.Name() + << " and type: " << KeyType::KeyTypeToString(keyWithType.GetKeyType()) + << std::endl; + } + + if (!keysSinglePage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetPropertiesOfKeysSinglePageOptions options; + options.ContinuationToken = keysSinglePage.ContinuationToken.GetValue(); + keysSinglePage = keyClient.GetPropertiesOfKeysSinglePage(options).ExtractValue(); + } + + // update key + CreateRsaKeyOptions newRsaKey(rsaKeyName); + newRsaKey.KeySize = 4096; + newRsaKey.ExpiresOn = std::chrono::system_clock::now() + std::chrono::hours(24 * 365); + + keyClient.CreateRsaKey(newRsaKey); + + // List key versions + std::cout << "\t-List Key versions" << std::endl; + for (auto keyVersionsSinglePage + = keyClient.GetPropertiesOfKeyVersionsSinglePage(rsaKeyName).ExtractValue(); + ;) + { + for (auto const& key : keyVersionsSinglePage.Items) + { + std::cout << "Key's version: " << key.Version << " with name: " << key.Name << std::endl; + } + + if (!keyVersionsSinglePage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetPropertiesOfKeyVersionsSinglePageOptions options; + options.ContinuationToken = keyVersionsSinglePage.ContinuationToken.GetValue(); + keyVersionsSinglePage + = keyClient.GetPropertiesOfKeyVersionsSinglePage(rsaKeyName, options).ExtractValue(); + } + + std::cout << "\t-Delete Keys" << std::endl; + DeleteKeyOperation rsaOperation = keyClient.StartDeleteKey(rsaKeyName); + DeleteKeyOperation ecOperation = keyClient.StartDeleteKey(ecKeyName); + + // You only need to wait for completion if you want to purge or recover the key. + rsaOperation.PollUntilDone(std::chrono::milliseconds(2000)); + ecOperation.PollUntilDone(std::chrono::milliseconds(2000)); + + std::cout << "\t-List Deleted Keys" << std::endl; + + // Start getting the first page. + for (auto keysDeletedPage = keyClient.GetDeletedKeysSinglePage().ExtractValue();;) + { + for (auto const& key : keysDeletedPage.Items) + { + std::cout << "Deleted key's name: " << key.Name() + << ", recovery level: " << key.Properties.RecoveryLevel + << " and recovery Id: " << key.RecoveryId << std::endl; + } + + if (!keysDeletedPage.ContinuationToken.HasValue()) + { + // No more pages for the response, break the loop + break; + } + + // Get the next page + GetDeletedKeysSinglePageOptions options; + options.ContinuationToken = keysDeletedPage.ContinuationToken.GetValue(); + keysDeletedPage = keyClient.GetDeletedKeysSinglePage(options).ExtractValue(); + } + + // If the keyvault is soft-delete enabled, then for permanent deletion, deleted keys needs to be + // purged. + keyClient.PurgeDeletedKey(rsaKeyName); + keyClient.PurgeDeletedKey(ecKeyName); + } + catch (Azure::Core::Credentials::AuthenticationException const& e) + { + std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; + return 1; + } + catch (Azure::Security::KeyVault::Common::KeyVaultException const& e) + { + std::cout << "KeyVault Client Exception happened:" << std::endl << e.Message << std::endl; + return 1; + } + + return 0; +} + +template +static inline bool CompareNullableT(Azure::Nullable const& left, Azure::Nullable const& right) +{ + if (!left.HasValue() && !right.HasValue()) + { + return true; + } + if (left.HasValue() && !right.HasValue()) + { + return false; + } + if (!left.HasValue() && right.HasValue()) + { + return false; + } + return left.GetValue() == right.GetValue(); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt index 923c4df7a..c4e9c2728 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt @@ -37,9 +37,12 @@ gtest_discover_tests(azure-security-keyvault-keys-test ################## Live Tests ########################## add_executable ( azure-security-keyvault-keys-test-live + key_client_backup_test_live.cpp key_client_create_test_live.cpp key_client_delete_test_live.cpp key_client_get_test_live.cpp + key_client_import_test_live.cpp + key_client_update_test_live.cpp main.cpp key_client_base_test.hpp ) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_backup_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_backup_test_live.cpp new file mode 100644 index 000000000..5c1e50b76 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_backup_test_live.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "gtest/gtest.h" + +#include "key_client_base_test.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys::Test; +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; + +TEST_F(KeyVaultClientTest, BackupKey) +{ + KeyClient keyClient(m_keyVaultUrl, m_credential); + std::string keyName = GetUniqueName(); + + std::cout + << std::endl + << "Backup key test takes more than 2 minutes since it needs to wait for purge to complete."; + + { + std::cout << std::endl << "- Create key"; + auto response = keyClient.CreateKey(keyName, JsonWebKeyType::Ec); + CheckValidResponse(response); + } + + // backup + std::cout << std::endl << "- Backup key"; + auto backUpResponse = keyClient.BackupKey(keyName); + CheckValidResponse(backUpResponse); + { + // Delete + std::cout << std::endl << "- Delete key"; + auto response = keyClient.StartDeleteKey(keyName); + response.PollUntilDone(std::chrono::milliseconds(1000)); + } + { + // Purge + std::cout << std::endl << "- Purge key"; + auto response = keyClient.PurgeDeletedKey(keyName); + CheckValidResponse(response, Azure::Core::Http::HttpStatusCode::NoContent); + // Purge can take up to 2 min + std::this_thread::sleep_for(std::chrono::minutes(2)); + } + { // Check key is gone + EXPECT_THROW(keyClient.GetKey(keyName), Azure::Security::KeyVault::Common::KeyVaultException); + } + { + // Restore + std::cout << std::endl << "- Restore key"; + auto respone = keyClient.RestoreKeyBackup(*backUpResponse); + CheckValidResponse(backUpResponse); + } + { + // Check key is restored + auto response = keyClient.GetKey(keyName); + CheckValidResponse(response); + EXPECT_EQ(keyName, response->Name()); + } + { + // Delete + std::cout << std::endl << "- Clean"; + auto response = keyClient.StartDeleteKey(keyName); + response.PollUntilDone(std::chrono::milliseconds(1000)); + } + { + // Purge + auto response = keyClient.PurgeDeletedKey(keyName); + CheckValidResponse(response, Azure::Core::Http::HttpStatusCode::NoContent); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp index 137eb1bf0..36f927e79 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp @@ -10,10 +10,12 @@ #include #include +#include #include #include #include +#include namespace Azure { namespace Security { namespace KeyVault { namespace Keys { namespace Test { @@ -43,13 +45,80 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam Azure::Response& response, Azure::Core::Http::HttpStatusCode expectedCode = Azure::Core::Http::HttpStatusCode::Ok) { - auto rawResponse = response.ExtractRawResponse(); + auto const& rawResponse = response.GetRawResponse(); EXPECT_EQ( static_cast::type>( - rawResponse->GetStatusCode()), + rawResponse.GetStatusCode()), static_cast::type>( expectedCode)); } + + static inline std::string GetUniqueName() { return Azure::Core::Uuid::CreateUuid().ToString(); } + + static inline void CleanUpKeyVault(KeyClient const& keyClient) + { + std::vector deletedKeys; + GetDeletedKeysSinglePageOptions options; + while (true) + { + auto keyResponse = keyClient.GetDeletedKeysSinglePage(options); + for (auto& key : keyResponse->Items) + { + deletedKeys.emplace_back(key); + } + if (!keyResponse->ContinuationToken) + { + break; + } + options.ContinuationToken = keyResponse->ContinuationToken; + } + if (deletedKeys.size() > 0) + { + for (auto& deletedKey : deletedKeys) + { + keyClient.PurgeDeletedKey(deletedKey.Name()); + } + // Wait for purge is completed + std::this_thread::sleep_for(std::chrono::minutes(1)); + } + } + + static inline void RemoveAllKeysFromVault(KeyClient const& keyClient, bool waitForPurge = true) + { + std::vector deletedKeys; + GetPropertiesOfKeysSinglePageOptions options; + while (true) + { + auto keyResponse = keyClient.GetPropertiesOfKeysSinglePage(options); + for (auto& key : keyResponse->Items) + { + deletedKeys.emplace_back(keyClient.StartDeleteKey(key.Name)); + } + if (!keyResponse->ContinuationToken) + { + break; + } + options.ContinuationToken = keyResponse->ContinuationToken; + } + if (deletedKeys.size() > 0) + { + std::cout << std::endl + << "Cleaning vault. " << deletedKeys.size() + << " Will be deleted and purged now..."; + for (auto& deletedKey : deletedKeys) + { + auto readyToPurgeKey = deletedKey.PollUntilDone(std::chrono::milliseconds(1000)); + keyClient.PurgeDeletedKey(readyToPurgeKey->Name()); + std::cout << std::endl << "Deleted and purged key: " + readyToPurgeKey->Name(); + } + std::cout << std::endl << "Complete purge operation."; + // Wait for purge is completed + if (waitForPurge) + { + std::this_thread::sleep_for(std::chrono::minutes(1)); + } + } + } }; }}}}} // namespace Azure::Security::KeyVault::Keys::Test diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_create_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_create_test_live.cpp index 9bda21df5..8be2f011e 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_create_test_live.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_create_test_live.cpp @@ -19,7 +19,7 @@ using namespace Azure::Security::KeyVault::Keys::Test; TEST_F(KeyVaultClientTest, CreateKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("createKey"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -40,7 +40,7 @@ TEST_F(KeyVaultClientTest, CreateKey) TEST_F(KeyVaultClientTest, CreateKeyWithOptions) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("createKeyWithOptions"); + auto keyName = GetUniqueName(); Azure::Security::KeyVault::Keys::CreateKeyOptions options; options.KeyOperations.push_back(Azure::Security::KeyVault::Keys::KeyOperation::Sign()); @@ -75,7 +75,7 @@ TEST_F(KeyVaultClientTest, CreateKeyWithOptions) TEST_F(KeyVaultClientTest, CreateKeyWithTags) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("myKeyWithOptionsTags"); + auto keyName = GetUniqueName(); Azure::Security::KeyVault::Keys::CreateKeyOptions options; options.Tags.emplace("one", "value=1"); @@ -102,7 +102,7 @@ TEST_F(KeyVaultClientTest, CreateKeyWithTags) TEST_F(KeyVaultClientTest, CreateEcKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("createEcKey"); + auto keyName = GetUniqueName(); { auto ecKey = Azure::Security::KeyVault::Keys::CreateEcKeyOptions(keyName); @@ -123,7 +123,7 @@ TEST_F(KeyVaultClientTest, CreateEcKey) TEST_F(KeyVaultClientTest, CreateEcKeyWithCurve) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("createEcKey"); + auto keyName = GetUniqueName(); { auto ecKey = Azure::Security::KeyVault::Keys::CreateEcKeyOptions(keyName); @@ -150,7 +150,7 @@ TEST_F(KeyVaultClientTest, CreateEcKeyWithCurve) TEST_F(KeyVaultClientTest, CreateRsaKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("createRsaKey"); + auto keyName = GetUniqueName(); { auto rsaKey = Azure::Security::KeyVault::Keys::CreateRsaKeyOptions(keyName, false); @@ -173,7 +173,7 @@ TEST_F(KeyVaultClientTest, CreateRsaKey) TEST_F(KeyVaultClientTest, CreateEcHsmKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultHsmUrl, m_credential); - std::string keyName("createEcHsmKey"); + auto keyName = GetUniqueName(); { auto ecHsmKey = Azure::Security::KeyVault::Keys::CreateEcKeyOptions(keyName, true); @@ -202,7 +202,7 @@ TEST_F(KeyVaultClientTest, CreateEcHsmKey) TEST_F(KeyVaultClientTest, CreateRsaHsmKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultHsmUrl, m_credential); - std::string keyName("createRsaHsmKey"); + auto keyName = GetUniqueName(); { auto rsaHsmKey = Azure::Security::KeyVault::Keys::CreateRsaKeyOptions(keyName, true); diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp index b1a26fe05..1bfbf734f 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp @@ -44,7 +44,7 @@ using namespace Azure::Security::KeyVault::Keys::Test; TEST_F(KeyVaultClientTest, DeleteKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("deleteThisKey"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -69,12 +69,26 @@ TEST_F(KeyVaultClientTest, DeleteKey) // Will throw and fail test if test takes more than 3 minutes (token cancelled) auto keyResponse = keyResponseLRO.PollUntilDone(std::chrono::milliseconds(1000), cancelToken); } + { + // recover + auto recoverOperation = keyClient.StartRecoverDeletedKey(keyName); + auto keyResponse = recoverOperation.PollUntilDone(std::chrono::milliseconds(500)); + auto key = keyResponse.ExtractValue(); + // Delete again for purging + auto deleteOp = keyClient.StartDeleteKey(key.Name()); + deleteOp.PollUntilDone(std::chrono::milliseconds(200)); + } + { + // Purge + auto response = keyClient.PurgeDeletedKey(keyName); + CheckValidResponse(response, Azure::Core::Http::HttpStatusCode::NoContent); + } } TEST_F(KeyVaultClientTest, DeleteKeyOperationPoll) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("deleteThisKeyPoll"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -98,7 +112,7 @@ TEST_F(KeyVaultClientTest, DeleteKeyOperationPoll) TEST_F(KeyVaultClientTest, DeleteInvalidKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("thisKeyDoesNotExists"); + auto keyName = GetUniqueName(); auto wasThrown = false; try @@ -125,7 +139,7 @@ TEST_F(KeyVaultClientTest, DeleteInvalidKey) TEST_F(KeyVaultClientTest, DoubleDelete) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("DeleteMeTwoTimes"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -163,7 +177,7 @@ TEST_F(KeyVaultClientTest, DoubleDelete) TEST_F(KeyVaultClientTest, DoubleDeleteBeforePollComplete) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("DeleteMeBeforePollComplete1"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -199,7 +213,7 @@ TEST_F(KeyVaultClientTest, DoubleDeleteBeforePollComplete) TEST_F(KeyVaultClientTest, CreateDeletedKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("YouCanCreateMeAfterYouDeletedMe"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -239,7 +253,7 @@ TEST_F(KeyVaultClientTest, CreateDeletedKey) TEST_F(KeyVaultClientTest, CreateDeletedKeyBeforePollComplete) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("YouCanCreateMeAfterYouDeletedMeEvenBeforePollComplete"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -276,7 +290,7 @@ TEST_F(KeyVaultClientTest, CreateDeletedKeyBeforePollComplete) TEST_F(KeyVaultClientTest, GetDeletedKey) { Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); - std::string keyName("deleteThisKeyAndGetItFromDeleteKeys1wwxx3x4"); + auto keyName = GetUniqueName(); { auto keyResponse @@ -302,7 +316,7 @@ TEST_F(KeyVaultClientTest, GetDeletedKey) EXPECT_EQ(deletedKey.Name(), keyName); auto expectedType = Azure::Security::KeyVault::Keys::JsonWebKeyType::Ec; EXPECT_EQ( - Azure::Security::KeyVault::Keys::_detail::KeyTypeToString(expectedType), - Azure::Security::KeyVault::Keys::_detail::KeyTypeToString(deletedKey.Key.KeyType)); + Azure::Security::KeyVault::Keys::KeyType::KeyTypeToString(expectedType), + Azure::Security::KeyVault::Keys::KeyType::KeyTypeToString(deletedKey.Key.KeyType)); } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_get_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_get_test_live.cpp index ed86d934f..1b1681b90 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_get_test_live.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_get_test_live.cpp @@ -15,17 +15,167 @@ #include using namespace Azure::Security::KeyVault::Keys::Test; +using namespace Azure::Security::KeyVault::Keys; -TEST_F(KeyVaultClientTest, GetKey) +TEST_F(KeyVaultClientTest, GetSingleKey) { - Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); + KeyClient keyClient(m_keyVaultUrl, m_credential); // Assuming and RS Key exists in the KeyVault Account. - std::string keyName("testKey"); + std::string keyName(GetUniqueName()); + auto createKeyResponse = keyClient.CreateEcKey(CreateEcKeyOptions(keyName)); + CheckValidResponse(createKeyResponse); auto keyResponse = keyClient.GetKey(keyName); CheckValidResponse(keyResponse); auto key = keyResponse.ExtractValue(); EXPECT_EQ(key.Name(), keyName); - EXPECT_EQ(key.GetKeyType(), Azure::Security::KeyVault::Keys::JsonWebKeyType::Rsa); + EXPECT_EQ(key.GetKeyType(), JsonWebKeyType::Ec); +} + +TEST_F(KeyVaultClientTest, GetPropertiesOfKeysOnePage) +{ + + KeyClient keyClient(m_keyVaultUrl, m_credential); + // Delete and purge anything before starting the test to ensure test will work + RemoveAllKeysFromVault(keyClient); + + // Create 5 keys + std::vector keyNames; + for (int counter = 0; counter < 5; counter++) + { + auto name = GetUniqueName(); + CreateEcKeyOptions options(name); + keyNames.emplace_back(name); + auto response = keyClient.CreateEcKey(options); + CheckValidResponse(response); + } + // Get Key properties + std::vector keyPropertiesList; + GetPropertiesOfKeysSinglePageOptions options; + while (true) + { + auto keyResponse = keyClient.GetPropertiesOfKeysSinglePage(options); + for (auto& key : keyResponse->Items) + { + keyPropertiesList.emplace_back(key); + } + if (!keyResponse->ContinuationToken) + { + break; + } + options.ContinuationToken = keyResponse->ContinuationToken; + } + + EXPECT_EQ(keyNames.size(), keyPropertiesList.size()); + for (auto const& keyProperties : keyPropertiesList) + { + // Check names are in the keyNames list + auto findKeyName = std::find(keyNames.begin(), keyNames.end(), keyProperties.Name); + EXPECT_NE(findKeyName, keyNames.end()); + } + + // Clean vault + RemoveAllKeysFromVault(keyClient, false); +} + +TEST_F(KeyVaultClientTest, GetKeysVersionsOnePage) +{ + KeyClient keyClient(m_keyVaultUrl, m_credential); + + // Create 5 key versions + std::string keyName(GetUniqueName()); + size_t expectedVersions = 5; + CreateEcKeyOptions createKeyOptions(keyName); + for (size_t counter = 0; counter < expectedVersions; counter++) + { + auto response = keyClient.CreateEcKey(createKeyOptions); + CheckValidResponse(response); + } + // Get Key versions + std::vector keyPropertiesList; + GetPropertiesOfKeyVersionsSinglePageOptions getKeyOptions; + while (true) + { + auto keyResponse = keyClient.GetPropertiesOfKeyVersionsSinglePage(keyName, getKeyOptions); + for (auto& key : keyResponse->Items) + { + keyPropertiesList.emplace_back(key); + } + if (!keyResponse->ContinuationToken) + { + break; + } + getKeyOptions.ContinuationToken = keyResponse->ContinuationToken; + } + + EXPECT_EQ(expectedVersions, keyPropertiesList.size()); + for (auto const& keyProperties : keyPropertiesList) + { + EXPECT_EQ(keyName, keyProperties.Name); + } + + // Clean vault + RemoveAllKeysFromVault(keyClient, false); +} + +TEST_F(KeyVaultClientTest, GetDeletedKeysOnePage) +{ + KeyClient keyClient(m_keyVaultUrl, m_credential); + + // Delete and purge anything before starting the test to ensure test will work + CleanUpKeyVault(keyClient); + + // Create 5 keys + std::vector keyNames; + for (int counter = 0; counter < 5; counter++) + { + auto name = GetUniqueName(); + CreateEcKeyOptions options(name); + keyNames.emplace_back(name); + auto response = keyClient.CreateEcKey(options); + CheckValidResponse(response); + } + // Delete keys + std::vector operations; + for (auto const& keyName : keyNames) + { + operations.emplace_back(keyClient.StartDeleteKey(keyName)); + } + // wait for all of the delete operations to complete + for (auto& operation : operations) + { + operation.PollUntilDone(std::chrono::milliseconds(1000)); + } + + // Get all deleted Keys + std::vector deletedKeys; + GetDeletedKeysSinglePageOptions options; + while (true) + { + auto keyResponse = keyClient.GetDeletedKeysSinglePage(options); + for (auto& key : keyResponse->Items) + { + deletedKeys.emplace_back(key); + } + if (!keyResponse->ContinuationToken) + { + break; + } + options.ContinuationToken = keyResponse->ContinuationToken; + } + + EXPECT_EQ(keyNames.size(), deletedKeys.size()); + for (auto const& deletedKey : deletedKeys) + { + // Check names are in the keyNames list + auto findKeyName = std::find(keyNames.begin(), keyNames.end(), deletedKey.Name()); + EXPECT_NE(findKeyName, keyNames.end()); + } + + // Purge + for (auto const& keyName : keyNames) + { + keyClient.PurgeDeletedKey(keyName); + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_import_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_import_test_live.cpp new file mode 100644 index 000000000..5c450f7ef --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_import_test_live.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "gtest/gtest.h" + +#include +#include + +#include "key_client_base_test.hpp" + +#include +#include + +using namespace Azure::Security::KeyVault::Keys::Test; +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Security::KeyVault::Common::_internal; + +TEST_F(KeyVaultClientTest, ImportKey) +{ + KeyClient keyClient(m_keyVaultUrl, m_credential); + JsonWebKey key; + key.KeyType = JsonWebKeyType::Rsa; + // Values from https://docs.microsoft.com/en-us/rest/api/keyvault/importkey/importkey + key.N = Base64Url::Base64UrlDecode( + "nKAwarTrOpzd1hhH4cQNdVTgRF-b0ubPD8ZNVf0UXjb62QuAk3Dn68ESThcF7SoDYRx2QVcfoMC9WCcuQUQDieJF-" + "lvJTSer1TwH72NBovwKlHvrXqEI0a6_uVYY5n-" + "soGt7qFZNbwQLdWWA6PrbqTLIkv6r01dcuhTiQQAn6OWEa0JbFvWfF1kILQIaSBBBaaQ4R7hZs7-" + "VQTHGD7J1xGteof4gw2VTiwNdcE8p5UG5b6S9KQwAeET4yB4KFPwQ3TDdzxJQ89mwYVi_" + "sgAIggN54hTq4oEKYJHBOMtFGIN0_HQ60ZSUnpOi87xNC-8VFqnv4rfTQ7nkK6XMvjMVfw"); + key.E = Base64Url::Base64UrlDecode("AQAB"); + key.D = Base64Url::Base64UrlDecode("GeT1_D5LAZa7qlC7WZ0DKJnOth8kcPrN0urTEFtWCbmHQWkAad_px_" + "VUpGp0BWDDzENbXbQcu4QCCdf4crve5eXt8dVI86OSah"); + key.DP = Base64Url::Base64UrlDecode( + "ZGnmWx-Nca71z9a9vvT4g02iv3S-" + "3kSgmhl8JST09YQwK8tfiK7nXnNMtXJi2K4dLKKnLicGtCzB6W3mXdLcP2SUOWDOeStoBt8HEBT" + "4MrI1psCKqnBum78WkHju90rBFj99amkP6UeQy5EASAzgmKQu2nUaUnRV0lYP8LHMCkE"); + key.DQ = Base64Url::Base64UrlDecode( + "dtpke0foFs04hPS6XYLA5lc7-1MAHfZKN4CkMAofwDqPmRQzCxpDJUk0gMWGJEdU_" + "Lqfbg22Py44cci0dczH36NW3UU5BL86T2_SPPDOuyX7kDscrIJCdowxQCGJHGRBEozM_" + "uTL46wu6UnUIv7m7cuGgodJyZBcdwpo6ziFink"); + key.QI = Base64Url::Base64UrlDecode( + "Y9KD5GaHkAYmAqpOfAQUMr71QuAAaBb0APzMuUvoEYw39PD3_vJeh9HZ15QmJ8zCX10-" + "nlzUB-bWwvK-rGcJXbK4pArilr5MiaYv7e8h5eW2zs2_itDJ6Oebi-" + "wVbMhg7DvUTBbkCvPhhIedE4UlDQmMYP7RhzVVs7SfmkGs_DQ"); + key.P = Base64Url::Base64UrlDecode( + "v1jeCPnuJQM2PW2690Q9KJk0Ulok8VFGjkcHUHVi3orKdy7y_" + "TCIWM6ZGvgFzI6abinzYbTEPKV4wFdMAwvOWmawXj5YrsoeB44_HXJ0ak_5_" + "iP6XXR8MLGXbd0ZqsxvAZyzMj9vyle7EN2cBod6aenI2QZoRDucPvjPwZsZotk"); + key.Q = Base64Url::Base64UrlDecode( + "0Yv-Dj6qnvx_LL70lUnKA6MgHE_bUC4drl5ZNDDsUdUUYfxIK4G1rGU45kHGtp-Qg-" + "Uyf9s52ywLylhcVE3jfbjOgEozlSwKyhqfXkLpMLWHqOKj9fcfYd4PWKPOgpzWsqjA6fJbBUM" + "Yo0CU2G9cWCtVodO7sBJVSIZunWrAlBc"); + std::string keyName(GetUniqueName()); + auto response = keyClient.ImportKey(keyName, key); + CheckValidResponse(response); + + { + // delete + purge + auto op = keyClient.StartDeleteKey(keyName); + op.PollUntilDone(std::chrono::milliseconds(1000)); + keyClient.PurgeDeletedKey(keyName); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_update_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_update_test_live.cpp new file mode 100644 index 000000000..4dca1e48b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_update_test_live.cpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "gtest/gtest.h" + +#include "key_client_base_test.hpp" + +#include +#include +#include + +#include + +using namespace Azure::Security::KeyVault::Keys::Test; +using namespace Azure; +using namespace Azure::Security::KeyVault::Keys; + +TEST_F(KeyVaultClientTest, UpdateProperties) +{ + KeyClient keyClient(m_keyVaultUrl, m_credential); + auto keyName = GetUniqueName(); + auto updateTo = DateTime::Parse("20301031T00:00:00Z", DateTime::DateFormat::Rfc3339); + { + auto keyResponse = keyClient.CreateKey(keyName, JsonWebKeyType::Ec); + CheckValidResponse(keyResponse); + auto keyVaultKey = keyResponse.ExtractValue(); + EXPECT_EQ(keyVaultKey.Name(), keyName); + EXPECT_TRUE(keyVaultKey.Properties.Enabled); + EXPECT_TRUE(keyVaultKey.Properties.Enabled.GetValue()); + + // Update Key + keyVaultKey.Properties.Enabled = false; + keyVaultKey.Properties.ExpiresOn = updateTo; + auto updatedResponse = keyClient.UpdateKeyProperties(keyVaultKey.Properties); + CheckValidResponse(updatedResponse); + } + { + // Get updated key to check values + auto updatedKey = keyClient.GetKey(keyName); + CheckValidResponse(updatedKey); + auto key = updatedKey.ExtractValue(); + EXPECT_TRUE(key.Properties.Enabled); + EXPECT_FALSE(key.Properties.Enabled.GetValue()); + } +}