Adding GetCryptographyClient to KeyClient (#2917)

* Adding GetCryptographyClient to KeyClient

* update arg name
This commit is contained in:
Victor Vazquez 2021-09-27 15:36:37 -07:00 committed by GitHub
parent 02be9fc757
commit 8a12a64629
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 354 additions and 192 deletions

View File

@ -4,6 +4,8 @@
### Features Added
- [2833](https://github.com/Azure/azure-sdk-for-cpp/issues/2833) Added `GetCryptographyClient()` to `KeyClient` to return a `CryptographyClient` that uses the same options, policies, and pipeline as the `KeyClient` that created it.
### Breaking Changes
### Bugs Fixed

View File

@ -54,6 +54,7 @@ set(
src/cryptography/wrap_result.cpp
src/cryptography/unwrap_result.cpp
src/cryptography/verify_result.cpp
src/private/cryptography_internal_access.hpp
src/private/cryptography_serializers.hpp
src/private/key_backup.hpp
src/private/key_constants.hpp

View File

@ -23,210 +23,238 @@
#include <string>
#include <vector>
namespace Azure { namespace Security { namespace KeyVault {
namespace Azure {
namespace Security {
namespace KeyVault {
namespace Keys {
namespace Cryptography {
namespace _detail {
class KeyVaultProtocolClient;
} // namespace _detail
namespace Keys { namespace Cryptography {
/**
* @brief A client used to perform cryptographic operations with Azure Key Vault keys.
* @brief The internal access to the cryptography client.
*
*/
class CryptographyClient final {
protected:
Azure::Core::Url m_keyId;
std::string m_apiVersion;
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_pipeline;
class CryptoClientInternalAccess;
} // namespace _detail
private:
Azure::Core::Http::Request CreateRequest(
Azure::Core::Http::HttpMethod method,
std::vector<std::string> const& path = {},
Azure::Core::IO::BodyStream* content = nullptr) const;
/**
* @brief A client used to perform cryptographic operations with Azure Key Vault keys.
*
*/
class CryptographyClient final {
protected:
Azure::Core::Url m_keyId;
std::string m_apiVersion;
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_pipeline;
std::unique_ptr<Azure::Core::Http::RawResponse> SendCryptoRequest(
std::vector<std::string> const& path,
std::string const& payload,
Azure::Core::Context const& context) const;
private:
// Provide private-access to the internal layer
friend class Azure::Security::KeyVault::Keys::Cryptography::_detail::CryptoClientInternalAccess;
public:
/**
* @brief Initializes a new instance of the #CryptographyClient class.
*
* @param keyId The key identifier of the #KeyVaultKey which will be used for cryptographic
* operations.
* @param credential A #TokenCredential used to authenticate requests to the vault, like
* DefaultAzureCredential.
* @param options #CryptographyClientOptions for the #CryptographyClient for local or remote
* operations on Key Vault.
*/
explicit CryptographyClient(
std::string const& keyId,
std::shared_ptr<Core::Credentials::TokenCredential const> credential,
CryptographyClientOptions const& options = CryptographyClientOptions());
Azure::Core::Http::Request CreateRequest(
Azure::Core::Http::HttpMethod method,
std::vector<std::string> const& path = {},
Azure::Core::IO::BodyStream* content = nullptr) const;
/**
* @brief Destructs `%CryptographyClient`.
*
*/
~CryptographyClient();
std::unique_ptr<Azure::Core::Http::RawResponse> SendCryptoRequest(
std::vector<std::string> const& path,
std::string const& payload,
Azure::Core::Context const& context) const;
/**
* @brief Encrypts plaintext.
*
* @param parameters An #EncryptParameters containing the data to encrypt and other parameters
* for algorithm-dependent encryption.
* @param context A #Azure::Core::Context to cancel the operation.
* @return An #EncryptResult containing the encrypted data along with all other information
* needed to decrypt it. This information should be stored with the encrypted data.
*/
Azure::Response<EncryptResult> Encrypt(
EncryptParameters const& parameters,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Construct a new Cryptography client that re-uses a pre-existing pipeline.
*
* @details This constructor is internal and accessed by a key client to create a cryptography
* client from it.
*
* @remark This is an internal only API.
*
* @param keyId The url for the key.
* @param apiVersion The service version to send requests to.
* @param pipeline The already created pipeline to be use for requests.
*/
explicit CryptographyClient(
Azure::Core::Url keyId,
std::string const& apiVersion,
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> pipeline)
: m_keyId(keyId), m_apiVersion(apiVersion), m_pipeline(pipeline)
{
}
/**
* @brief Decrypts ciphertext.
*
* @param parameters A #DecryptParameters containing the data to decrypt and other parameters
* for algorithm-dependent Decryption.
* @param context A #Azure::Core::Context to cancel the operation.
* @return An #DecryptResult containing the decrypted data along with all other information
* needed to decrypt it. This information should be stored with the Decrypted data.
*/
Azure::Response<DecryptResult> Decrypt(
DecryptParameters const& parameters,
Azure::Core::Context const& context = Azure::Core::Context());
public:
/**
* @brief Initializes a new instance of the #CryptographyClient class.
*
* @param keyId The key identifier of the #KeyVaultKey which will be used for cryptographic
* operations.
* @param credential A #TokenCredential used to authenticate requests to the vault, like
* DefaultAzureCredential.
* @param options #CryptographyClientOptions for the #CryptographyClient for local or remote
* operations on Key Vault.
*/
explicit CryptographyClient(
std::string const& keyId,
std::shared_ptr<Core::Credentials::TokenCredential const> credential,
CryptographyClientOptions const& options = CryptographyClientOptions());
/**
* @brief Encrypts the specified key.
*
* @param algorithm The #KeyWrapAlgorithm to use.
* @param key The key to encrypt.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the wrap operation. The returned #WrapResult contains the wrapped key
* along with all other information needed to unwrap it. This information should be stored
* with the wrapped key.
*/
Azure::Response<WrapResult> WrapKey(
KeyWrapAlgorithm algorithm,
std::vector<uint8_t> const& key,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Destructs `%CryptographyClient`.
*
*/
~CryptographyClient();
/**
* @brief Decrypts the specified encrypted key.
*
* @param algorithm The #KeyWrapAlgorithm to use.
* @param encryptedKey The encrypted key.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the unwrap operation. The returned #UnwrapResult contains the key
* along with information regarding the algorithm and key used to unwrap it.
*/
Azure::Response<UnwrapResult> UnwrapKey(
KeyWrapAlgorithm algorithm,
std::vector<uint8_t> const& encryptedKey,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Encrypts plaintext.
*
* @param parameters An #EncryptParameters containing the data to encrypt and other parameters
* for algorithm-dependent encryption.
* @param context A #Azure::Core::Context to cancel the operation.
* @return An #EncryptResult containing the encrypted data along with all other information
* needed to decrypt it. This information should be stored with the encrypted data.
*/
Azure::Response<EncryptResult> Encrypt(
EncryptParameters const& parameters,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Signs the specified digest.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param digest The pre-hashed digest to sign. The hash algorithm used to compute the digest
* must be compatable with the specified algorithm.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> Sign(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& digest,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Decrypts ciphertext.
*
* @param parameters A #DecryptParameters containing the data to decrypt and other parameters
* for algorithm-dependent Decryption.
* @param context A #Azure::Core::Context to cancel the operation.
* @return An #DecryptResult containing the decrypted data along with all other information
* needed to decrypt it. This information should be stored with the Decrypted data.
*/
Azure::Response<DecryptResult> Decrypt(
DecryptParameters const& parameters,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Signs the specified data.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param data The data to sign.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> SignData(
SignatureAlgorithm algorithm,
Azure::Core::IO::BodyStream& data,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Encrypts the specified key.
*
* @param algorithm The #KeyWrapAlgorithm to use.
* @param key The key to encrypt.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the wrap operation. The returned #WrapResult contains the wrapped key
* along with all other information needed to unwrap it. This information should be stored
* with the wrapped key.
*/
Azure::Response<WrapResult> WrapKey(
KeyWrapAlgorithm algorithm,
std::vector<uint8_t> const& key,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Signs the specified data.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param data The data to sign.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> SignData(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& data,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Decrypts the specified encrypted key.
*
* @param algorithm The #KeyWrapAlgorithm to use.
* @param encryptedKey The encrypted key.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the unwrap operation. The returned #UnwrapResult contains the key
* along with information regarding the algorithm and key used to unwrap it.
*/
Azure::Response<UnwrapResult> UnwrapKey(
KeyWrapAlgorithm algorithm,
std::vector<uint8_t> const& encryptedKey,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the digest.
* @param digest The pre-hashed digest corresponding to the signature. The hash algorithm used
* to compute the digest must be compatable with the specified algorithm.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> Verify(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& digest,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Signs the specified digest.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param digest The pre-hashed digest to sign. The hash algorithm used to compute the digest
* must be compatable with the specified algorithm.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> Sign(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& digest,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the data.
* @param data The data corresponding to the signature.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> VerifyData(
SignatureAlgorithm algorithm,
Azure::Core::IO::BodyStream& data,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Signs the specified data.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param data The data to sign.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> SignData(
SignatureAlgorithm algorithm,
Azure::Core::IO::BodyStream& data,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the data.
* @param data The data corresponding to the signature.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> VerifyData(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& data,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
};
/**
* @brief Signs the specified data.
*
* @param algorithm The #SignatureAlgorithm to use.
* @param data The data to sign.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the sign operation. The returned #SignResult contains the signature
* along with all other information needed to verify it. This information should be stored
* with the signature.
*/
Azure::Response<SignResult> SignData(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& data,
Azure::Core::Context const& context = Azure::Core::Context());
}} // namespace Keys::Cryptography
}}} // namespace Azure::Security::KeyVault
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the digest.
* @param digest The pre-hashed digest corresponding to the signature. The hash algorithm used
* to compute the digest must be compatable with the specified algorithm.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> Verify(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& digest,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the data.
* @param data The data corresponding to the signature.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> VerifyData(
SignatureAlgorithm algorithm,
Azure::Core::IO::BodyStream& data,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
/**
* @brief Verifies the specified signature.
*
* @param algorithm The #SignatureAlgorithm to use. This must be the same algorithm used to
* sign the data.
* @param data The data corresponding to the signature.
* @param signature The signature to verify.
* @param context A #Azure::Core::Context to cancel the operation.
* @return The result of the verify operation. If the signature is valid the
* #VerifyResult.IsValid property of the returned #VerifyResult will be set to true.
*/
Azure::Response<VerifyResult> VerifyData(
SignatureAlgorithm algorithm,
std::vector<uint8_t> const& data,
std::vector<uint8_t> const& signature,
Azure::Core::Context const& context = Azure::Core::Context());
};
}}}}} // namespace Azure::Security::KeyVault::Keys::Cryptography

View File

@ -9,6 +9,7 @@
#pragma once
#include "azure/keyvault/keys/cryptography/cryptography_client.hpp"
#include "azure/keyvault/keys/key_client_models.hpp"
#include "azure/keyvault/keys/key_client_options.hpp"
@ -70,6 +71,21 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
*/
explicit KeyClient(KeyClient const& keyClient) = default;
/**
* @brief Get a CryptographyClient for the given key.
*
* @details The returned client uses the same options and pipeline as the key client which
* creates it.
*
* @param name The name of the key used to perform cryptographic operations.
* @param version Optional version of the key used to perform cryptographic operations.
* @return Cryptography::CryptographyClient with the same options and re-using the same
* pipeline.
*/
Cryptography::CryptographyClient GetCryptographyClient(
std::string const& name,
std::string const& version = std::string());
/**
* @brief Gets the public part of a stored key.
*

View File

@ -16,6 +16,7 @@
#include "../private/key_verify_parameters.hpp"
#include "../private/key_wrap_parameters.hpp"
#include "../private/keyvault_protocol.hpp"
#include "../private/package_version.hpp"
#include <algorithm>
#include <memory>
@ -114,9 +115,8 @@ CryptographyClient::CryptographyClient(
std::string const& keyId,
std::shared_ptr<Core::Credentials::TokenCredential const> credential,
CryptographyClientOptions const& options)
: m_keyId(Azure::Core::Url(keyId)), m_apiVersion(options.Version.ToString())
{
m_keyId = Azure::Core::Url(keyId);
m_apiVersion = options.Version.ToString();
std::vector<std::unique_ptr<HttpPolicy>> perRetrypolicies;
{
Azure::Core::Credentials::TokenRequestContext const tokenContext
@ -130,7 +130,7 @@ CryptographyClient::CryptographyClient(
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
options,
"KeyVault",
options.Version.ToString(),
PackageVersion::ToString(),
std::move(perRetrypolicies),
std::move(perCallpolicies));
}

View File

@ -7,6 +7,7 @@
#include <azure/core/internal/http/pipeline.hpp>
#include "azure/keyvault/keys/key_client.hpp"
#include "private/cryptography_internal_access.hpp"
#include "private/key_backup.hpp"
#include "private/key_constants.hpp"
#include "private/key_request_parameters.hpp"
@ -89,8 +90,6 @@ KeyClient::KeyClient(
KeyClientOptions options)
: m_vaultUrl(vaultUrl), m_apiVersion(options.Version.ToString())
{
auto apiVersion = options.Version.ToString();
std::vector<std::unique_ptr<HttpPolicy>> perRetrypolicies;
{
Azure::Core::Credentials::TokenRequestContext const tokenContext
@ -400,3 +399,19 @@ Azure::Response<KeyVaultKey> KeyClient::ImportKey(
importKeyOptions.Name(), *rawResponse);
return Azure::Response<KeyVaultKey>(std::move(value), std::move(rawResponse));
}
Cryptography::CryptographyClient KeyClient::GetCryptographyClient(
std::string const& name,
std::string const& version)
{
Azure::Core::Url vaultUrl(m_vaultUrl);
vaultUrl.AppendPath(_detail::KeysPath);
vaultUrl.AppendPath(name);
if (!version.empty())
{
vaultUrl.AppendPath(version);
}
return Cryptography::_detail::CryptoClientInternalAccess::CreateCryptographyClient(
vaultUrl, m_apiVersion, m_pipeline);
}

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Provides access to the private content from a cryptographic client.
*
*/
#pragma once
#include <azure/core/http/http.hpp>
#include "azure/keyvault/keys/cryptography/cryptography_client.hpp"
#include <string>
namespace Azure {
namespace Security {
namespace KeyVault {
namespace Keys {
namespace Cryptography {
namespace _detail {
class CryptoClientInternalAccess {
CryptoClientInternalAccess() = delete;
public:
static CryptographyClient CreateCryptographyClient(
Azure::Core::Url keyId,
std::string const& apiVersion,
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> pipeline)
{
return CryptographyClient(keyId, apiVersion, pipeline);
}
};
}}}}}} // namespace Azure::Security::KeyVault::Keys::Cryptography::_detail

View File

@ -288,6 +288,68 @@ TEST_P(KeyVaultClientTest, RemoteSignVerifyDataRSA256)
}
}
TEST_P(KeyVaultClientTest, GetCryptoFromKeyRemoteEncrypt)
{
KeyClient keyClient(m_keyVaultUrl, m_credential);
std::string keyName(GetUniqueName());
CreateRsaKeyOptions rsaKeyOptions(keyName);
rsaKeyOptions.KeySize = GetParam();
auto rsaKey = keyClient.CreateRsaKey(rsaKeyOptions).Value;
// Get Crypto client from the key client
auto cryptoClient = keyClient.GetCryptographyClient(keyName);
{
uint8_t plaintextSource[] = "A single block of plaintext";
std::vector<uint8_t> plaintext(std::begin(plaintextSource), std::end(plaintextSource));
auto encryptResult
= cryptoClient.Encrypt(EncryptParameters::RsaOaepParameters(plaintext)).Value;
EXPECT_EQ(encryptResult.Algorithm.ToString(), EncryptionAlgorithm::RsaOaep.ToString());
EXPECT_EQ(encryptResult.KeyId, rsaKey.Id());
EXPECT_TRUE(encryptResult.Ciphertext.size() > 0);
auto decryptResult
= cryptoClient.Decrypt(DecryptParameters::RsaOaepParameters(encryptResult.Ciphertext))
.Value;
EXPECT_EQ(decryptResult.Algorithm.ToString(), encryptResult.Algorithm.ToString());
EXPECT_EQ(decryptResult.Plaintext, plaintext);
EXPECT_EQ(decryptResult.KeyId, encryptResult.KeyId);
}
}
TEST_P(KeyVaultClientTest, GetCryptoFromKeyVersionRemoteEncrypt)
{
KeyClient keyClient(m_keyVaultUrl, m_credential);
std::string keyName(GetUniqueName());
CreateRsaKeyOptions rsaKeyOptions(keyName);
rsaKeyOptions.KeySize = GetParam();
auto rsaKey = keyClient.CreateRsaKey(rsaKeyOptions).Value;
// Get Crypto client from the key client
auto cryptoClient = keyClient.GetCryptographyClient(rsaKey.Name(), rsaKey.Properties.Version);
{
uint8_t plaintextSource[] = "A single block of plaintext";
std::vector<uint8_t> plaintext(std::begin(plaintextSource), std::end(plaintextSource));
auto encryptResult
= cryptoClient.Encrypt(EncryptParameters::RsaOaepParameters(plaintext)).Value;
EXPECT_EQ(encryptResult.Algorithm.ToString(), EncryptionAlgorithm::RsaOaep.ToString());
EXPECT_EQ(encryptResult.KeyId, rsaKey.Id());
EXPECT_TRUE(encryptResult.Ciphertext.size() > 0);
auto decryptResult
= cryptoClient.Decrypt(DecryptParameters::RsaOaepParameters(encryptResult.Ciphertext))
.Value;
EXPECT_EQ(decryptResult.Algorithm.ToString(), encryptResult.Algorithm.ToString());
EXPECT_EQ(decryptResult.Plaintext, plaintext);
EXPECT_EQ(decryptResult.KeyId, encryptResult.KeyId);
}
}
namespace {
static std::string GetSuffix(const testing::TestParamInfo<int>& info)
{