From 07a68fb1a5e06ddc445d2061f172d0972aa7e2b8 Mon Sep 17 00:00:00 2001 From: Nicola Camillucci Date: Thu, 7 May 2026 10:56:57 +0100 Subject: [PATCH 1/4] Added secure wrap/unwrap operations --- sdk/keyvault/azure-keyvault-keys/CHANGELOG.md | 4 ++ .../azure/keyvault/keys/crypto/__init__.py | 7 +- .../azure/keyvault/keys/crypto/_client.py | 68 +++++++++++++++++++ .../azure/keyvault/keys/crypto/_enums.py | 18 +++++ .../azure/keyvault/keys/crypto/_models.py | 35 +++++++++- .../keyvault/keys/crypto/aio/__init__.py | 12 ++++ .../azure/keyvault/keys/crypto/aio/_client.py | 68 +++++++++++++++++++ 7 files changed, 210 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 513e58f6659b..a34c00afda85 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -4,6 +4,10 @@ ### Features Added +- Added `secure_wrap_key` and `secure_unwrap_key` methods for wrap/unwrap operations on keys. +- Added the `KeySecureWrapAlgorithm` enum, listing the algorithms supported by the secure wrap/unwrap operations. +- Added the `SecureWrapResult` and `SecureUnwrapResult` model classes wrapping the results of `secure_wrap_key` and `secure_unwrap_key`, respectively. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/__init__.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/__init__.py index 77047d425a7f..ffd733bf374b 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/__init__.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/__init__.py @@ -7,12 +7,14 @@ EncryptResult, KeyVaultRSAPrivateKey, KeyVaultRSAPublicKey, + SecureUnwrapResult, + SecureWrapResult, SignResult, WrapResult, VerifyResult, UnwrapResult, ) -from ._enums import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm +from ._enums import EncryptionAlgorithm, KeySecureWrapAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from ._client import CryptographyClient __all__ = [ @@ -20,9 +22,12 @@ "DecryptResult", "EncryptionAlgorithm", "EncryptResult", + "KeySecureWrapAlgorithm", "KeyVaultRSAPrivateKey", "KeyVaultRSAPublicKey", "KeyWrapAlgorithm", + "SecureUnwrapResult", + "SecureWrapResult", "SignatureAlgorithm", "SignResult", "WrapResult", diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py index 51f273d8f858..115378f0fc50 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py @@ -14,7 +14,10 @@ DecryptResult, EncryptionAlgorithm, EncryptResult, + KeySecureWrapAlgorithm, KeyWrapAlgorithm, + SecureUnwrapResult, + SecureWrapResult, SignatureAlgorithm, SignResult, VerifyResult, @@ -485,6 +488,71 @@ def unwrap_key(self, algorithm: KeyWrapAlgorithm, encrypted_key: bytes, **kwargs ) return UnwrapResult(key_id=self.key_id, algorithm=algorithm, key=operation_result.result) + @distributed_trace + def secure_wrap_key(self, algorithm: KeySecureWrapAlgorithm, **kwargs: Any) -> SecureWrapResult: + """Generate and securely wrap a new 256-bit AES key inside the trusted execution environment (TEE). + + The SECURE WRAP operation creates a fresh 256-bit AES key inside a TEE and wraps it with this + client's key as the wrapping key. Requires the keys/wrapKey permission. Only available with + API version ``2026-01-01-preview`` and newer. + + :param algorithm: The secure wrap algorithm to use. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + + :returns: The result of the secure wrap operation. + :rtype: ~azure.keyvault.keys.crypto.SecureWrapResult + """ + if self._jwk: + raise NotImplementedError( + "Secure wrap is not supported on a CryptographyClient created from a JsonWebKey" + ) + operation_result = self._client.secure_wrap_key( + key_name=self._key_id.name if self._key_id else None, + key_version=self._key_id.version if self._key_id else None, + parameters=self._models.SecureKeyWrapOperationParameters(algorithm=algorithm), + **kwargs, + ) + return SecureWrapResult(key_id=self.key_id, algorithm=algorithm, encrypted_key=operation_result.value) + + @distributed_trace + def secure_unwrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + encrypted_key: bytes, + target_attestation_token: str, + **kwargs: Any, + ) -> SecureUnwrapResult: + """Securely unwrap a key into a trusted execution environment (TEE). + + The SECURE UNWRAP operation unwraps a key previously wrapped by SECURE WRAP and releases it + only into a target TEE attested by the supplied Microsoft Azure Attestation (MAA) token. + Requires the keys/unwrapKey permission. Only available with API version + ``2026-01-01-preview`` and newer. + + :param algorithm: The secure wrap algorithm originally used to wrap the key. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + :param bytes encrypted_key: The wrapped key bytes to unwrap. + :param str target_attestation_token: The MAA attestation token of the target TEE. + + :returns: The result of the secure unwrap operation. + :rtype: ~azure.keyvault.keys.crypto.SecureUnwrapResult + """ + if self._jwk: + raise NotImplementedError( + "Secure unwrap is not supported on a CryptographyClient created from a JsonWebKey" + ) + operation_result = self._client.secure_unwrap_key( + key_name=self._key_id.name if self._key_id else None, + key_version=self._key_id.version if self._key_id else None, + parameters=self._models.SecureKeyUnWrapOperationParameters( + algorithm=algorithm, + value=encrypted_key, + target_attestation_token=target_attestation_token, + ), + **kwargs, + ) + return SecureUnwrapResult(key_id=self.key_id, algorithm=algorithm, key=operation_result.value) + @distributed_trace def sign(self, algorithm: SignatureAlgorithm, digest: bytes, **kwargs: Any) -> SignResult: """Create a signature from a digest using the client's key. diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_enums.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_enums.py index 356b72b5edc5..c880a6b39066 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_enums.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_enums.py @@ -26,6 +26,24 @@ class KeyWrapAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): ckm_aes_key_wrap_pad = "CKM_AES_KEY_WRAP_PAD" +class KeySecureWrapAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Key wrapping algorithms supported by the SECURE WRAP / SECURE UNWRAP operations. + + The secure wrap/unwrap surface is only available with API version ``2026-01-01-preview`` + and newer. + """ + + rsa_oaep_256 = "RSA-OAEP-256" + aes_128 = "A128KW" + aes_192 = "A192KW" + aes_256 = "A256KW" + aes_128_pad = "A128KWPAD" + aes_192_pad = "A192KWPAD" + aes_256_pad = "A256KWPAD" + ckm_aes_key_wrap = "CKM_AES_KEY_WRAP" + ckm_aes_key_wrap_pad = "CKM_AES_KEY_WRAP_PAD" + + class EncryptionAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Encryption algorithms""" diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_models.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_models.py index d65d152f6240..a61761944dca 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_models.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_models.py @@ -27,7 +27,7 @@ PublicFormat, ) -from ._enums import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm +from ._enums import EncryptionAlgorithm, KeySecureWrapAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from .._models import JsonWebKey if TYPE_CHECKING: @@ -619,3 +619,36 @@ def __init__(self, key_id: Optional[str], algorithm: KeyWrapAlgorithm, encrypted self.key_id = key_id self.algorithm = algorithm self.encrypted_key = encrypted_key + + +class SecureUnwrapResult: + """The result of a secure unwrap key operation. + + :param str key_id: Key encryption key's Key Vault identifier. + :param algorithm: The secure key wrap algorithm used. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + :param bytes key: The unwrapped key. + """ + + def __init__(self, key_id: Optional[str], algorithm: KeySecureWrapAlgorithm, key: bytes) -> None: + self.key_id = key_id + self.algorithm = algorithm + self.key = key + + +class SecureWrapResult: + """The result of a secure wrap key operation. + + The secure wrap operation creates a new 256-bit AES key inside the trusted execution environment + (TEE) and returns the wrapped key bytes encrypted with the key wrapping key. + + :param str key_id: The wrapping key's Key Vault identifier. + :param algorithm: The secure key wrap algorithm used. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + :param bytes encrypted_key: The wrapped key bytes generated inside the TEE. + """ + + def __init__(self, key_id: Optional[str], algorithm: KeySecureWrapAlgorithm, encrypted_key: bytes) -> None: + self.key_id = key_id + self.algorithm = algorithm + self.encrypted_key = encrypted_key diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/__init__.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/__init__.py index ec7a1b484a2b..e5ba9e3febfd 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/__init__.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/__init__.py @@ -24,6 +24,10 @@ def __getattr__(name: str): from .. import EncryptionAlgorithm requested = EncryptionAlgorithm + if name == "KeySecureWrapAlgorithm": + from .. import KeySecureWrapAlgorithm + + requested = KeySecureWrapAlgorithm if name == "KeyWrapAlgorithm": from .. import KeyWrapAlgorithm @@ -36,6 +40,14 @@ def __getattr__(name: str): from .. import EncryptResult requested = EncryptResult + if name == "SecureUnwrapResult": + from .. import SecureUnwrapResult + + requested = SecureUnwrapResult + if name == "SecureWrapResult": + from .. import SecureWrapResult + + requested = SecureWrapResult if name == "SignResult": from .. import SignResult diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py index 13111932ebbe..2006e12d276b 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py @@ -14,7 +14,10 @@ DecryptResult, EncryptionAlgorithm, EncryptResult, + KeySecureWrapAlgorithm, KeyWrapAlgorithm, + SecureUnwrapResult, + SecureWrapResult, SignatureAlgorithm, SignResult, VerifyResult, @@ -409,6 +412,71 @@ async def unwrap_key(self, algorithm: KeyWrapAlgorithm, encrypted_key: bytes, ** return UnwrapResult(key_id=self.key_id, algorithm=algorithm, key=operation_result.result) + @distributed_trace_async + async def secure_wrap_key(self, algorithm: KeySecureWrapAlgorithm, **kwargs: Any) -> SecureWrapResult: + """Generate and securely wrap a new 256-bit AES key inside the trusted execution environment (TEE). + + The SECURE WRAP operation creates a fresh 256-bit AES key inside a TEE and wraps it with this + client's key as the wrapping key. Requires the keys/wrapKey permission. Only available with + API version ``2026-01-01-preview`` and newer. + + :param algorithm: The secure wrap algorithm to use. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + + :returns: The result of the secure wrap operation. + :rtype: ~azure.keyvault.keys.crypto.SecureWrapResult + """ + if self._jwk: + raise NotImplementedError( + "Secure wrap is not supported on a CryptographyClient created from a JsonWebKey" + ) + operation_result = await self._client.secure_wrap_key( + key_name=self._key_id.name if self._key_id else None, + key_version=self._key_id.version if self._key_id else None, + parameters=self._models.SecureKeyWrapOperationParameters(algorithm=algorithm), + **kwargs, + ) + return SecureWrapResult(key_id=self.key_id, algorithm=algorithm, encrypted_key=operation_result.value) + + @distributed_trace_async + async def secure_unwrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + encrypted_key: bytes, + target_attestation_token: str, + **kwargs: Any, + ) -> SecureUnwrapResult: + """Securely unwrap a key into a trusted execution environment (TEE). + + The SECURE UNWRAP operation unwraps a key previously wrapped by SECURE WRAP and releases it + only into a target TEE attested by the supplied Microsoft Azure Attestation (MAA) token. + Requires the keys/unwrapKey permission. Only available with API version + ``2026-01-01-preview`` and newer. + + :param algorithm: The secure wrap algorithm originally used to wrap the key. + :type algorithm: ~azure.keyvault.keys.crypto.KeySecureWrapAlgorithm + :param bytes encrypted_key: The wrapped key bytes to unwrap. + :param str target_attestation_token: The MAA attestation token of the target TEE. + + :returns: The result of the secure unwrap operation. + :rtype: ~azure.keyvault.keys.crypto.SecureUnwrapResult + """ + if self._jwk: + raise NotImplementedError( + "Secure unwrap is not supported on a CryptographyClient created from a JsonWebKey" + ) + operation_result = await self._client.secure_unwrap_key( + key_name=self._key_id.name if self._key_id else None, + key_version=self._key_id.version if self._key_id else None, + parameters=self._models.SecureKeyUnWrapOperationParameters( + algorithm=algorithm, + value=encrypted_key, + target_attestation_token=target_attestation_token, + ), + **kwargs, + ) + return SecureUnwrapResult(key_id=self.key_id, algorithm=algorithm, key=operation_result.value) + @distributed_trace_async async def sign(self, algorithm: SignatureAlgorithm, digest: bytes, **kwargs: Any) -> SignResult: """Create a signature from a digest using the client's key. From dd1c07b9ba6a7c0c5c8640850a248e81cfe0a4b7 Mon Sep 17 00:00:00 2001 From: Nicola Camillucci Date: Thu, 7 May 2026 12:07:30 +0100 Subject: [PATCH 2/4] Added samples and tests --- .../samples/secure_key_operations.py | 101 +++++++++++++++ .../samples/secure_key_operations_async.py | 118 ++++++++++++++++++ .../tests/test_crypto_client.py | 40 +++++- .../tests/test_crypto_client_async.py | 48 ++++++- .../tests/test_examples_crypto.py | 48 ++++++- .../tests/test_examples_crypto_async.py | 54 +++++++- 6 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py create mode 100644 sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py diff --git a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py new file mode 100644 index 000000000000..40846ad01dea --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py @@ -0,0 +1,101 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import json +import os + +from azure.identity import DefaultAzureCredential +from azure.keyvault.keys import KeyClient, KeyReleasePolicy +from azure.keyvault.keys.crypto import CryptographyClient, KeySecureWrapAlgorithm + +# ---------------------------------------------------------------------------------------------------------- +# Prerequisites: +# 1. An Azure Key Vault Managed HSM (https://learn.microsoft.com/azure/key-vault/managed-hsm/quick-create-cli). +# Secure Wrap/Unwrap operations are only supported on Managed HSM, not on standard Key Vaults. +# +# 2. azure-keyvault-keys and azure-identity libraries (pip install these) +# +# 3. Set environment variable MANAGED_HSM_URL with the URL of your Managed HSM +# +# 4. Set environment variable AZURE_KEYVAULT_ATTESTATION_URL with the URL of the Microsoft Azure Attestation (MAA) +# authority that the wrapping key's release policy will trust +# +# 5. Set up your environment to use azure-identity's DefaultAzureCredential. For more information about how to configure +# the DefaultAzureCredential, refer to https://aka.ms/azsdk/python/identity/docs#azure.identity.DefaultAzureCredential +# +# 6. Key create, get, and crypto wrap/unwrap permissions for your service principal in your Managed HSM +# +# 7. Set environment variable AZURE_KEYVAULT_TARGET_ATTESTATION_TOKEN with a valid attestation token signed by +# Microsoft Azure Attestation (MAA) for the target trusted execution environment (TEE). This is required to +# run the secure_unwrap_key step. +# +# ---------------------------------------------------------------------------------------------------------- +# Sample - demonstrates Secure Wrap and Secure Unwrap operations against a Managed HSM. These operations let +# the HSM generate a fresh key inside its trusted execution environment, wrap it under an HSM key, and (later) +# release it back into a target TEE that can prove its identity via an attestation token. +# +# Note: Secure Wrap/Unwrap operations require API version 2026-01-01-preview or later. +# +# 1. Create an HSM-backed RSA wrapping key (create_rsa_key) +# +# 2. Securely wrap a freshly generated TEE key (secure_wrap_key) +# +# 3. Securely unwrap the key into a target TEE (secure_unwrap_key) +# ---------------------------------------------------------------------------------------------------------- + +# Instantiate clients that will be used to call the service. +# Here we use the DefaultAzureCredential, but any azure-identity credential can be used. +MANAGED_HSM_URL = os.environ["MANAGED_HSM_URL"] +ATTESTATION_URL = os.environ["AZURE_KEYVAULT_ATTESTATION_URL"] +credential = DefaultAzureCredential() +key_client = KeyClient(vault_url=MANAGED_HSM_URL, credential=credential) + +# Secure wrap/unwrap keys must be created with the secureWrapKey and secureUnwrapKey operations and a release +# policy. The release policy governs which target environments (TEEs) the wrapping key may be released into. +release_policy_json = { + "anyOf": [ + { + "anyOf": [{"claim": "sdk-test", "equals": True}], + "authority": ATTESTATION_URL.rstrip("/") + "/", + } + ], + "version": "1.0.0", +} +release_policy = KeyReleasePolicy(json.dumps(release_policy_json).encode()) + +# Create an HSM-backed RSA key to act as the wrapping key. +print("\n.. Create a wrapping key") +key_name = "secureWrapKeyName" +wrapping_key = key_client.create_rsa_key( + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, +) +print(f"Wrapping key '{wrapping_key.name}' created of type '{wrapping_key.key_type}'.") + +# Build a CryptographyClient bound to the wrapping key. +crypto_client = CryptographyClient(wrapping_key, credential=credential) + +# Securely wrap a key generated inside the HSM trusted execution environment. The wrapped key bytes +# (`encrypted_key`) can later be exchanged with a target TEE via secure_unwrap_key. +print("\n.. Securely wrap a TEE-generated key") +wrap_result = crypto_client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) +print( + f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'." +) +encrypted_key = wrap_result.encrypted_key + +# Unwrap the key into a target TEE. This requires a valid attestation token signed by Microsoft Azure +# Attestation (MAA) for the target environment. The token is opaque to the SDK; obtain it from your TEE +# / attestation flow and pass it as `target_attestation_token`. +target_attestation_token = os.environ["AZURE_KEYVAULT_TARGET_ATTESTATION_TOKEN"] +print("\n.. Securely unwrap the key into a target TEE") +unwrap_result = crypto_client.secure_unwrap_key( + KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token +) +print( + f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'." +) diff --git a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py new file mode 100644 index 000000000000..6e41951036fe --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py @@ -0,0 +1,118 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import asyncio +import json +import os + +from azure.identity.aio import DefaultAzureCredential +from azure.keyvault.keys import KeyReleasePolicy +from azure.keyvault.keys.aio import KeyClient +from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm +from azure.keyvault.keys.crypto.aio import CryptographyClient + +# ---------------------------------------------------------------------------------------------------------- +# Prerequisites: +# 1. An Azure Key Vault Managed HSM (https://learn.microsoft.com/azure/key-vault/managed-hsm/quick-create-cli). +# Secure Wrap/Unwrap operations are only supported on Managed HSM, not on standard Key Vaults. +# +# 2. azure-keyvault-keys and azure-identity libraries (pip install these) +# +# 3. Set environment variable MANAGED_HSM_URL with the URL of your Managed HSM +# +# 4. Set environment variable AZURE_KEYVAULT_ATTESTATION_URL with the URL of the Microsoft Azure Attestation (MAA) +# authority that the wrapping key's release policy will trust +# +# 5. Set up your environment to use azure-identity's DefaultAzureCredential. For more information about how to configure +# the DefaultAzureCredential, refer to https://aka.ms/azsdk/python/identity/docs#azure.identity.DefaultAzureCredential +# +# 6. Key create, get, and crypto wrap/unwrap permissions for your service principal in your Managed HSM +# +# 7. Set environment variable AZURE_KEYVAULT_TARGET_ATTESTATION_TOKEN with a valid attestation token signed by +# Microsoft Azure Attestation (MAA) for the target trusted execution environment (TEE). This is required to +# run the secure_unwrap_key step. +# +# ---------------------------------------------------------------------------------------------------------- +# Sample - demonstrates Secure Wrap and Secure Unwrap operations against a Managed HSM. These operations let +# the HSM generate a fresh key inside its trusted execution environment, wrap it under an HSM key, and (later) +# release it back into a target TEE that can prove its identity via an attestation token. +# +# Note: Secure Wrap/Unwrap operations require API version 2026-01-01-preview or later. +# +# 1. Create an HSM-backed RSA wrapping key (create_rsa_key) +# +# 2. Securely wrap a freshly generated TEE key (secure_wrap_key) +# +# 3. Securely unwrap the key into a target TEE (secure_unwrap_key) +# ---------------------------------------------------------------------------------------------------------- + + +async def run_sample(): + # Instantiate clients that will be used to call the service. + # Here we use the DefaultAzureCredential, but any azure-identity credential can be used. + MANAGED_HSM_URL = os.environ["MANAGED_HSM_URL"] + ATTESTATION_URL = os.environ["AZURE_KEYVAULT_ATTESTATION_URL"] + credential = DefaultAzureCredential() + key_client = KeyClient(vault_url=MANAGED_HSM_URL, credential=credential) + + # Secure wrap/unwrap keys must be created with the secureWrapKey and secureUnwrapKey operations and a release + # policy. The release policy governs which target environments (TEEs) the wrapping key may be released into. + release_policy_json = { + "anyOf": [ + { + "anyOf": [{"claim": "sdk-test", "equals": True}], + "authority": ATTESTATION_URL.rstrip("/") + "/", + } + ], + "version": "1.0.0", + } + release_policy = KeyReleasePolicy(json.dumps(release_policy_json).encode()) + + # Create an HSM-backed RSA key to act as the wrapping key. + print("\n.. Create a wrapping key") + key_name = "secureWrapKeyNameAsync" + wrapping_key = await key_client.create_rsa_key( + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, + ) + print( + f"Wrapping key '{wrapping_key.name}' created of type '{wrapping_key.key_type}'." + ) + + # Build a CryptographyClient bound to the wrapping key. + crypto_client = CryptographyClient(wrapping_key, credential=credential) + + # Securely wrap a key generated inside the HSM trusted execution environment. + print("\n.. Securely wrap a TEE-generated key") + wrap_result = await crypto_client.secure_wrap_key( + KeySecureWrapAlgorithm.rsa_oaep_256 + ) + print( + f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'." + ) + encrypted_key = wrap_result.encrypted_key + + # Unwrap the key into a target TEE. This requires a valid attestation token signed by Microsoft Azure + # Attestation (MAA) for the target environment. The token is opaque to the SDK; obtain it from your TEE + # / attestation flow and pass it as `target_attestation_token`. + target_attestation_token = os.environ["AZURE_KEYVAULT_TARGET_ATTESTATION_TOKEN"] + print("\n.. Securely unwrap the key into a target TEE") + unwrap_result = await crypto_client.secure_unwrap_key( + KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token + ) + print( + f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'." + ) + + print("\nrun_sample done") + await crypto_client.close() + await key_client.close() + await credential.close() + + +if __name__ == "__main__": + asyncio.run(run_sample()) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py index 86f900f9f61a..06778949a6dc 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py @@ -33,6 +33,7 @@ from azure.keyvault.keys.crypto import ( CryptographyClient, EncryptionAlgorithm, + KeySecureWrapAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm, ) @@ -43,11 +44,12 @@ from devtools_testutils import recorded_by_proxy, set_bodiless_matcher from _shared.test_case import KeyVaultTestCase -from _test_case import KeysClientPreparer, get_decorator +from _test_case import KeysClientPreparer, get_attestation_token, get_decorator, get_release_policy from _keys_test_case import KeysTestCase all_api_versions = get_decorator() only_hsm = get_decorator(only_hsm=True) +only_hsm_2026_default = get_decorator(only_hsm=True, api_versions=[DEFAULT_VERSION]) only_vault_default = get_decorator(only_vault=True, api_versions=[DEFAULT_VERSION]) only_vault_7_4_plus = get_decorator(only_vault=True, api_versions=[ApiVersion.V7_4, ApiVersion.V7_5]) @@ -441,6 +443,42 @@ def test_symmetric_wrap_and_unwrap(self, key_client, **kwargs): result = crypto_client.unwrap_key(result.algorithm, result.encrypted_key) assert result.key == self.plaintext + @pytest.mark.parametrize("api_version,is_hsm", only_hsm_2026_default) + @KeysClientPreparer() + @recorded_by_proxy + def test_secure_wrap_and_unwrap(self, key_client, **kwargs): + """Securely wrap a TEE-generated key and unwrap it back into a target TEE.""" + attestation_uri = self._get_attestation_uri() + release_policy = get_release_policy(attestation_uri) + key_name = self.get_resource_name("secure-wrap-key") + wrapping_key = self._create_rsa_key( + key_client, + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, + ) + crypto_client = self.create_crypto_client(wrapping_key, api_version=key_client.api_version) + + wrap_result = crypto_client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) + assert wrap_result.key_id == wrapping_key.id + assert wrap_result.algorithm == KeySecureWrapAlgorithm.rsa_oaep_256 + assert wrap_result.encrypted_key + + target_attestation_token = get_attestation_token(attestation_uri) + try: + unwrap_result = crypto_client.secure_unwrap_key( + wrap_result.algorithm, wrap_result.encrypted_key, target_attestation_token + ) + assert unwrap_result.key_id == wrapping_key.id + assert unwrap_result.algorithm == KeySecureWrapAlgorithm.rsa_oaep_256 + assert unwrap_result.key + except HttpResponseError as ex: + # The service may transiently fail to verify a test attestation token in live runs. + if self.is_live and "attestation" in ex.message.lower(): + pytest.skip("Target environment attestation statement could not be verified. Likely transient.") + raise + @pytest.mark.parametrize("api_version,is_hsm", all_api_versions) @KeysClientPreparer() @recorded_by_proxy diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py index c2b665882c82..fb0ab19f9721 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py @@ -19,21 +19,26 @@ from azure.keyvault.keys.crypto.aio import ( CryptographyClient, EncryptionAlgorithm, # Shouldn't be imported from aio namespace, but do so to test backwards compatibility + KeySecureWrapAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm, ) +from azure.keyvault.keys._shared.client_base import DEFAULT_VERSION from azure.keyvault.keys._generated._serialization import Deserializer from azure.keyvault.keys._generated.models import KeySignParameters from devtools_testutils.aio import recorded_by_proxy_async -from _async_test_case import AsyncKeysClientPreparer -from _test_case import get_decorator +from _async_test_case import AsyncKeysClientPreparer, get_attestation_token +from _test_case import get_decorator, get_release_policy from _shared.helpers_async import get_completed_future from _shared.test_case_async import KeyVaultTestCase from _keys_test_case import KeysTestCase all_api_versions = get_decorator(is_async=True) only_hsm = get_decorator(only_hsm=True, is_async=True) +only_hsm_2026_default = get_decorator( + only_hsm=True, is_async=True, api_versions=[DEFAULT_VERSION] +) only_vault_7_4_plus = get_decorator(only_vault=True, is_async=True, api_versions=[ApiVersion.V7_4, ApiVersion.V7_5]) @@ -43,7 +48,7 @@ class TestCryptoClient(KeyVaultTestCase, KeysTestCase): aad = b"test" async def _create_rsa_key(self, client, key_name, **kwargs): - key_ops = ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey"] + key_ops = kwargs.get("key_operations") or ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey"] hsm = kwargs.get("hardware_protected") or False if self.is_live: await asyncio.sleep(2) # to avoid throttling by the service @@ -298,6 +303,43 @@ async def test_symmetric_wrap_and_unwrap(self, key_client, **kwargs): result = await crypto_client.unwrap_key(result.algorithm, result.encrypted_key) assert result.key == self.plaintext + @pytest.mark.asyncio + @pytest.mark.parametrize("api_version,is_hsm", only_hsm_2026_default) + @AsyncKeysClientPreparer() + @recorded_by_proxy_async + async def test_secure_wrap_and_unwrap(self, key_client, **kwargs): + """Securely wrap a TEE-generated key and unwrap it back into a target TEE.""" + attestation_uri = self._get_attestation_uri() + release_policy = get_release_policy(attestation_uri) + key_name = self.get_resource_name("secure-wrap-key") + wrapping_key = await self._create_rsa_key( + key_client, + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, + ) + crypto_client = self.create_crypto_client(wrapping_key, is_async=True, api_version=key_client.api_version) + + wrap_result = await crypto_client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) + assert wrap_result.key_id == wrapping_key.id + assert wrap_result.algorithm == KeySecureWrapAlgorithm.rsa_oaep_256 + assert wrap_result.encrypted_key + + target_attestation_token = await get_attestation_token(attestation_uri) + try: + unwrap_result = await crypto_client.secure_unwrap_key( + wrap_result.algorithm, wrap_result.encrypted_key, target_attestation_token + ) + assert unwrap_result.key_id == wrapping_key.id + assert unwrap_result.algorithm == KeySecureWrapAlgorithm.rsa_oaep_256 + assert unwrap_result.key + except HttpResponseError as ex: + # The service may transiently fail to verify a test attestation token in live runs. + if self.is_live and "attestation" in ex.message.lower(): + pytest.skip("Target environment attestation statement could not be verified. Likely transient.") + raise + @pytest.mark.asyncio @pytest.mark.parametrize("api_version,is_hsm", all_api_versions) @AsyncKeysClientPreparer() diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py index 82f3aef073b2..39c211732a96 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py @@ -3,14 +3,17 @@ # Licensed under the MIT License. # ------------------------------------ import pytest +from azure.core.exceptions import HttpResponseError from azure.keyvault.keys.crypto import CryptographyClient +from azure.keyvault.keys._shared.client_base import DEFAULT_VERSION from devtools_testutils import recorded_by_proxy, set_bodiless_matcher from _shared.test_case import KeyVaultTestCase -from _test_case import KeysClientPreparer, get_decorator +from _test_case import KeysClientPreparer, get_attestation_token, get_decorator, get_release_policy from _keys_test_case import KeysTestCase all_api_versions = get_decorator(only_vault=True) +only_hsm_2026_default = get_decorator(only_hsm=True, api_versions=[DEFAULT_VERSION]) class TestCryptoExamples(KeyVaultTestCase, KeysTestCase): @@ -110,3 +113,46 @@ def test_sign_verify(self, key_client, **kwargs): result = client.verify(SignatureAlgorithm.rs256, digest, signature) assert result.is_valid # [END verify] + + @pytest.mark.parametrize("api_version,is_hsm", only_hsm_2026_default) + @KeysClientPreparer() + @recorded_by_proxy + def test_secure_wrap_unwrap(self, key_client, **kwargs): + credential = self.get_credential(CryptographyClient) + key_name = self.get_resource_name("crypto-test-secure-wrap-key") + attestation_uri = self._get_attestation_uri() + release_policy = get_release_policy(attestation_uri) + key = key_client.create_rsa_key( + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, + ) + client = CryptographyClient(key, credential, api_version=key_client.api_version) + + # [START secure_wrap_key] + from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm + + # the result holds the wrapped key generated inside the trusted execution environment + result = client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) + encrypted_key = result.encrypted_key + print(result.key_id) + print(result.algorithm) + # [END secure_wrap_key] + + target_attestation_token = get_attestation_token(attestation_uri) + try: + # [START secure_unwrap_key] + from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm + + # secure_unwrap_key requires a target environment attestation token to release the key into + result = client.secure_unwrap_key( + KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token + ) + unwrapped_key = result.key + print(result.key_id) + # [END secure_unwrap_key] + except HttpResponseError as ex: + if self.is_live and "attestation" in ex.message.lower(): + pytest.skip("Target environment attestation statement could not be verified. Likely transient.") + raise diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py index a25ad3d18181..10072742aae0 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py @@ -3,18 +3,22 @@ # Licensed under the MIT License. # ------------------------------------ import pytest +from azure.core.exceptions import HttpResponseError from azure.keyvault.keys.crypto.aio import CryptographyClient +from azure.keyvault.keys._shared.client_base import DEFAULT_VERSION from devtools_testutils import set_bodiless_matcher from devtools_testutils.aio import recorded_by_proxy_async -from _async_test_case import AsyncKeysClientPreparer -from _test_case import get_decorator +from _async_test_case import AsyncKeysClientPreparer, get_attestation_token +from _test_case import get_decorator, get_release_policy from _shared.test_case_async import KeyVaultTestCase +from _keys_test_case import KeysTestCase all_api_versions = get_decorator(is_async=True, only_vault=True) +only_hsm_2026_default = get_decorator(only_hsm=True, is_async=True, api_versions=[DEFAULT_VERSION]) -class TestCryptoExamples(KeyVaultTestCase): +class TestCryptoExamples(KeyVaultTestCase, KeysTestCase): @pytest.mark.asyncio @pytest.mark.parametrize("api_version,is_hsm", all_api_versions) @AsyncKeysClientPreparer() @@ -118,3 +122,47 @@ async def test_sign_verify_async(self, key_client, **kwargs): verified = await client.verify(SignatureAlgorithm.rs256, digest, signature) assert verified.is_valid # [END verify] + + @pytest.mark.asyncio + @pytest.mark.parametrize("api_version,is_hsm", only_hsm_2026_default) + @AsyncKeysClientPreparer() + @recorded_by_proxy_async + async def test_secure_wrap_unwrap_async(self, key_client, **kwargs): + credential = self.get_credential(CryptographyClient, is_async=True) + key_name = self.get_resource_name("crypto-test-secure-wrap-key") + attestation_uri = self._get_attestation_uri() + release_policy = get_release_policy(attestation_uri) + key = await key_client.create_rsa_key( + key_name, + hardware_protected=True, + key_operations=["secureWrapKey", "secureUnwrapKey"], + release_policy=release_policy, + ) + client = CryptographyClient(key, credential, api_version=key_client.api_version) + + # [START secure_wrap_key] + from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm + + # the result holds the wrapped key generated inside the trusted execution environment + result = await client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) + print(result.key_id) + print(result.algorithm) + encrypted_key = result.encrypted_key + # [END secure_wrap_key] + + target_attestation_token = await get_attestation_token(attestation_uri) + try: + # [START secure_unwrap_key] + from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm + + # secure_unwrap_key requires a target environment attestation token to release the key into + result = await client.secure_unwrap_key( + KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token + ) + print(result.key_id) + unwrapped_key = result.key + # [END secure_unwrap_key] + except HttpResponseError as ex: + if self.is_live and "attestation" in ex.message.lower(): + pytest.skip("Target environment attestation statement could not be verified. Likely transient.") + raise From 94176605b63d6484b2376a6a598be690b051054b Mon Sep 17 00:00:00 2001 From: Nicola Camillucci Date: Mon, 22 Jun 2026 14:05:55 +0100 Subject: [PATCH 3/4] Recorded tests --- sdk/keyvault/azure-keyvault-keys/CHANGELOG.md | 2 +- sdk/keyvault/azure-keyvault-keys/assets.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index a34c00afda85..90885aa3359e 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features Added -- Added `secure_wrap_key` and `secure_unwrap_key` methods for wrap/unwrap operations on keys. +- Added `secure_wrap_key` and `secure_unwrap_key` methods for wrap/unwrap operations on keys [#47200](https://github.com/Azure/azure-sdk-for-python/pull/47591). - Added the `KeySecureWrapAlgorithm` enum, listing the algorithms supported by the secure wrap/unwrap operations. - Added the `SecureWrapResult` and `SecureUnwrapResult` model classes wrapping the results of `secure_wrap_key` and `secure_unwrap_key`, respectively. diff --git a/sdk/keyvault/azure-keyvault-keys/assets.json b/sdk/keyvault/azure-keyvault-keys/assets.json index be0788c9d3ab..a48eb13f1ca3 100644 --- a/sdk/keyvault/azure-keyvault-keys/assets.json +++ b/sdk/keyvault/azure-keyvault-keys/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/keyvault/azure-keyvault-keys", - "Tag": "python/keyvault/azure-keyvault-keys_652b9e1d39" + "Tag": "python/keyvault/azure-keyvault-keys_0031f0c596" } From cb5ad5ac064010664a2ce81177311432c6a8a505 Mon Sep 17 00:00:00 2001 From: Nicola Camillucci Date: Mon, 22 Jun 2026 15:18:27 +0100 Subject: [PATCH 4/4] PyLint, CSpell, AzPySdk --- sdk/keyvault/azure-keyvault-keys/CHANGELOG.md | 2 +- sdk/keyvault/azure-keyvault-keys/api.md | 1211 +++++++++++++++++ .../azure-keyvault-keys/api.metadata.yml | 3 + .../azure/keyvault/keys/_enums.py | 2 + .../azure/keyvault/keys/crypto/_client.py | 4 +- .../azure/keyvault/keys/crypto/aio/_client.py | 4 +- .../azure-keyvault-keys/pyproject.toml | 1 + .../samples/secure_key_operations.py | 12 +- .../samples/secure_key_operations_async.py | 20 +- .../tests/test_crypto_client.py | 2 +- .../tests/test_crypto_client_async.py | 8 +- .../tests/test_examples_crypto.py | 3 +- .../tests/test_examples_crypto_async.py | 3 +- sdk/keyvault/cspell.json | 1 + 14 files changed, 1239 insertions(+), 37 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-keys/api.md create mode 100644 sdk/keyvault/azure-keyvault-keys/api.metadata.yml diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 90885aa3359e..b30ee56687ea 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features Added -- Added `secure_wrap_key` and `secure_unwrap_key` methods for wrap/unwrap operations on keys [#47200](https://github.com/Azure/azure-sdk-for-python/pull/47591). +- Added `secure_wrap_key` and `secure_unwrap_key` methods for wrap/unwrap operations on keys [#47591](https://github.com/Azure/azure-sdk-for-python/pull/47591). - Added the `KeySecureWrapAlgorithm` enum, listing the algorithms supported by the secure wrap/unwrap operations. - Added the `SecureWrapResult` and `SecureUnwrapResult` model classes wrapping the results of `secure_wrap_key` and `secure_unwrap_key`, respectively. diff --git a/sdk/keyvault/azure-keyvault-keys/api.md b/sdk/keyvault/azure-keyvault-keys/api.md new file mode 100644 index 000000000000..e5190332f21e --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/api.md @@ -0,0 +1,1211 @@ +```py +namespace azure.keyvault.keys + + class azure.keyvault.keys.ApiVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta): + V2016_10_01 = "2016-10-01" + V2025_07_01 = "2025-07-01" + V2026_01_01_PREVIEW = "2026-01-01-preview" + V7_0 = "7.0" + V7_1 = "7.1" + V7_2 = "7.2" + V7_3 = "7.3" + V7_4 = "7.4" + V7_5 = "7.5" + V7_6 = "7.6" + + + class azure.keyvault.keys.DeletedKey(KeyVaultKey): + property deleted_date: Optional[datetime] # Read-only + property id: str # Read-only + property key: JsonWebKey # Read-only + property key_operations: List[Union[str, KeyOperation]] # Read-only + property key_type: Union[str, KeyType] # Read-only + property name: str # Read-only + property properties: KeyProperties # Read-only + property recovery_id: Optional[str] # Read-only + property scheduled_purge_date: Optional[datetime] # Read-only + + def __init__( + self, + properties: KeyProperties, + deleted_date: Optional[datetime] = None, + recovery_id: Optional[str] = None, + scheduled_purge_date: Optional[datetime] = None, + **kwargs: Any + ) -> None: ... + + def __repr__(self) -> str: ... + + + class azure.keyvault.keys.ExternalKey: + + def __init__( + self, + *, + id: str + ) -> None: ... + + def __repr__(self) -> str: ... + + + class azure.keyvault.keys.JsonWebKey: + + def __init__( + self, + *, + crv: Union[KeyCurveName, str] = ..., + d: Optional[bytes] = ..., + dp: Optional[bytes] = ..., + dq: Optional[bytes] = ..., + e: Optional[bytes] = ..., + k: Optional[bytes] = ..., + key_ops: Union[list[str, KeyOperation]] = ..., + kid: Optional[str] = ..., + kty: Union[KeyType, str] = ..., + n: Optional[bytes] = ..., + p: Optional[bytes] = ..., + q: Optional[bytes] = ..., + qi: Optional[bytes] = ..., + t: Optional[bytes] = ..., + x: Optional[bytes] = ..., + y: Optional[bytes] = ..., + **kwargs: Any + ) -> None: ... + + + class azure.keyvault.keys.KeyAttestation: + certificate_pem_file: Union[bytes, None] + private_key_attestation: Union[bytes, None] + public_key_attestation: Union[bytes, None] + version: Union[str, None] + + def __init__( + self, + *, + certificate_pem_file: Optional[bytes] = ..., + private_key_attestation: Optional[bytes] = ..., + public_key_attestation: Optional[bytes] = ..., + version: Optional[str] = ... + ) -> None: ... + + def __repr__(self) -> str: ... + + + class azure.keyvault.keys.KeyClient(KeyVaultClientBase): implements ContextManager + property vault_url: str # Read-only + + def __init__( + self, + vault_url: str, + credential: TokenCredential, + *, + api_version: Union[ApiVersion, str] = ..., + verify_challenge_resource: Optional[bool] = ..., + **kwargs: Any + ) -> None: ... + + @distributed_trace + def backup_key( + self, + name: str, + **kwargs: Any + ) -> bytes: ... + + @distributed_trace + def begin_delete_key( + self, + name: str, + **kwargs: Any + ) -> LROPoller[DeletedKey]: ... + + @distributed_trace + def begin_recover_deleted_key( + self, + name: str, + **kwargs: Any + ) -> LROPoller[KeyVaultKey]: ... + + def close(self) -> None: ... + + @distributed_trace + def create_ec_key( + self, + name: str, + *, + curve: Optional[Union[str, KeyCurveName]] = ..., + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def create_external_key( + self, + name: str, + external_key: ExternalKey, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def create_key( + self, + name: str, + key_type: Union[str, KeyType], + *, + curve: Optional[Union[str, KeyCurveName]] = ..., + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + public_exponent: Optional[int] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def create_oct_key( + self, + name: str, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def create_rsa_key( + self, + name: str, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + public_exponent: Optional[int] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + def get_cryptography_client( + self, + key_name: str, + *, + key_version: Optional[str] = ..., + **kwargs + ) -> CryptographyClient: ... + + @distributed_trace + def get_deleted_key( + self, + name: str, + **kwargs: Any + ) -> DeletedKey: ... + + @distributed_trace + def get_key( + self, + name: str, + version: Optional[str] = None, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def get_key_attestation( + self, + name: str, + version: Optional[str] = None, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def get_key_rotation_policy( + self, + key_name: str, + **kwargs: Any + ) -> KeyRotationPolicy: ... + + @distributed_trace + def get_random_bytes( + self, + count: int, + **kwargs: Any + ) -> bytes: ... + + @distributed_trace + def import_key( + self, + name: str, + key: JsonWebKey, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def list_deleted_keys(self, **kwargs: Any) -> ItemPaged[DeletedKey]: ... + + @distributed_trace + def list_properties_of_key_versions( + self, + name: str, + **kwargs: Any + ) -> ItemPaged[KeyProperties]: ... + + @distributed_trace + def list_properties_of_keys(self, **kwargs: Any) -> ItemPaged[KeyProperties]: ... + + @distributed_trace + def purge_deleted_key( + self, + name: str, + **kwargs: Any + ) -> None: ... + + @distributed_trace + def release_key( + self, + name: str, + target_attestation_token: str, + *, + algorithm: Optional[Union[str, KeyExportEncryptionAlgorithm]] = ..., + nonce: Optional[str] = ..., + version: Optional[str] = ..., + **kwargs: Any + ) -> ReleaseKeyResult: ... + + @distributed_trace + def restore_key_backup( + self, + backup: bytes, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def rotate_key( + self, + name: str, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> HttpResponse: ... + + @distributed_trace + def update_key_properties( + self, + name: str, + version: Optional[str] = None, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def update_key_rotation_policy( + self, + key_name: str, + policy: KeyRotationPolicy, + *, + expires_in: Optional[str] = ..., + lifetime_actions: Optional[List[KeyRotationLifetimeAction]] = ..., + **kwargs: Any + ) -> KeyRotationPolicy: ... + + + class azure.keyvault.keys.KeyCurveName(str, Enum, metaclass=CaseInsensitiveEnumMeta): + p_256 = "P-256" + p_256_k = "P-256K" + p_384 = "P-384" + p_521 = "P-521" + + + class azure.keyvault.keys.KeyExportEncryptionAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ckm_rsa_aes_key_wrap = "CKM_RSA_AES_KEY_WRAP" + rsa_aes_key_wrap_256 = "RSA_AES_KEY_WRAP_256" + rsa_aes_key_wrap_384 = "RSA_AES_KEY_WRAP_384" + + + class azure.keyvault.keys.KeyOperation(str, Enum, metaclass=CaseInsensitiveEnumMeta): + decrypt = "decrypt" + encrypt = "encrypt" + export = "export" + import_key = "import" + sign = "sign" + unwrap_key = "unwrapKey" + verify = "verify" + wrap_key = "wrapKey" + + + class azure.keyvault.keys.KeyProperties: + property attestation: Optional[KeyAttestation] # Read-only + property created_on: Optional[datetime] # Read-only + property enabled: Optional[bool] # Read-only + property expires_on: Optional[datetime] # Read-only + property exportable: Optional[bool] # Read-only + property external_key: Optional[ExternalKey] # Read-only + property hsm_platform: Optional[str] # Read-only + property id: str # Read-only + property key_size: Optional[int] # Read-only + property managed: Optional[bool] # Read-only + property name: str # Read-only + property not_before: Optional[datetime] # Read-only + property recoverable_days: Optional[int] # Read-only + property recovery_level: Optional[str] # Read-only + property release_policy: Optional[KeyReleasePolicy] # Read-only + property tags: Optional[Dict[str, str]] # Read-only + property updated_on: Optional[datetime] # Read-only + property vault_url: str # Read-only + property version: Optional[str] # Read-only + + def __init__( + self, + key_id: str, + attributes: Optional[KeyAttributes] = None, + *, + managed: Optional[bool] = ..., + release_policy: Union[KeyReleasePolicy, None] = ..., + tags: Union[dict[str, str], None] = ..., + **kwargs: Any + ) -> None: ... + + def __repr__(self) -> str: ... + + + class azure.keyvault.keys.KeyReleasePolicy: + + def __init__( + self, + encoded_policy: bytes, + *, + content_type: Optional[str] = ..., + immutable: Optional[bool] = ..., + **kwargs: Any + ) -> None: ... + + + class azure.keyvault.keys.KeyRotationLifetimeAction: + + def __init__( + self, + action: Union[KeyRotationPolicyAction, str], + *, + time_after_create: Union[str, None] = ..., + time_before_expiry: Union[str, None] = ..., + **kwargs: Any + ) -> None: ... + + + class azure.keyvault.keys.KeyRotationPolicy: + created_on: Union[datetime, None] + expires_in: Union[str, None] + id: Union[str, None] + lifetime_actions: list[KeyRotationLifetimeAction] + updated_on: Union[datetime, None] + + def __init__(self, **kwargs: Any) -> None: ... + + + class azure.keyvault.keys.KeyRotationPolicyAction(str, Enum, metaclass=CaseInsensitiveEnumMeta): + notify = "Notify" + rotate = "Rotate" + + + class azure.keyvault.keys.KeyType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ec = "EC" + ec_hsm = "EC-HSM" + oct = "oct" + oct_hsm = "oct-HSM" + rsa = "RSA" + rsa_hsm = "RSA-HSM" + + + class azure.keyvault.keys.KeyVaultKey: + property id: str # Read-only + property key: JsonWebKey # Read-only + property key_operations: List[Union[str, KeyOperation]] # Read-only + property key_type: Union[str, KeyType] # Read-only + property name: str # Read-only + property properties: KeyProperties # Read-only + + def __init__( + self, + key_id: str, + jwk: Optional[Dict[str, Any]] = None, + **kwargs + ) -> None: ... + + def __repr__(self) -> str: ... + + + class azure.keyvault.keys.KeyVaultKeyIdentifier: + property name: str # Read-only + property source_id: str # Read-only + property vault_url: str # Read-only + property version: Optional[str] # Read-only + + def __init__(self, source_id: str) -> None: ... + + + class azure.keyvault.keys.ReleaseKeyResult: + value: str + + def __init__(self, value: str) -> None: ... + + +namespace azure.keyvault.keys.aio + + class azure.keyvault.keys.aio.KeyClient(AsyncKeyVaultClientBase): implements AsyncContextManager + property vault_url: str # Read-only + + def __init__( + self, + vault_url: str, + credential: AsyncTokenCredential, + *, + api_version: Union[ApiVersion, str] = ..., + verify_challenge_resource: Optional[bool] = ..., + **kwargs: Any + ) -> None: ... + + @distributed_trace_async + async def backup_key( + self, + name: str, + **kwargs: Any + ) -> bytes: ... + + async def close(self) -> None: ... + + @distributed_trace_async + async def create_ec_key( + self, + name: str, + *, + curve: Optional[Union[str, KeyCurveName]] = ..., + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def create_external_key( + self, + name: str, + external_key: ExternalKey, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def create_key( + self, + name: str, + key_type: Union[str, KeyType], + *, + curve: Optional[Union[str, KeyCurveName]] = ..., + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + public_exponent: Optional[int] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def create_oct_key( + self, + name: str, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def create_rsa_key( + self, + name: str, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = False, + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + public_exponent: Optional[int] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + size: Optional[int] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def delete_key( + self, + name: str, + **kwargs: Any + ) -> DeletedKey: ... + + def get_cryptography_client( + self, + key_name: str, + *, + key_version: Optional[str] = ..., + **kwargs + ) -> CryptographyClient: ... + + @distributed_trace_async + async def get_deleted_key( + self, + name: str, + **kwargs: Any + ) -> DeletedKey: ... + + @distributed_trace_async + async def get_key( + self, + name: str, + version: Optional[str] = None, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def get_key_attestation( + self, + name: str, + version: Optional[str] = None, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def get_key_rotation_policy( + self, + key_name: str, + **kwargs: Any + ) -> KeyRotationPolicy: ... + + @distributed_trace_async + async def get_random_bytes( + self, + count: int, + **kwargs: Any + ) -> bytes: ... + + @distributed_trace_async + async def import_key( + self, + name: str, + key: JsonWebKey, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + exportable: Optional[bool] = ..., + hardware_protected: Optional[bool] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace + def list_deleted_keys(self, **kwargs: Any) -> AsyncItemPaged[DeletedKey]: ... + + @distributed_trace + def list_properties_of_key_versions( + self, + name: str, + **kwargs: Any + ) -> AsyncItemPaged[KeyProperties]: ... + + @distributed_trace + def list_properties_of_keys(self, **kwargs: Any) -> AsyncItemPaged[KeyProperties]: ... + + @distributed_trace_async + async def purge_deleted_key( + self, + name: str, + **kwargs: Any + ) -> None: ... + + @distributed_trace_async + async def recover_deleted_key( + self, + name: str, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def release_key( + self, + name: str, + target_attestation_token: str, + *, + algorithm: Optional[Union[str, KeyExportEncryptionAlgorithm]] = ..., + nonce: Optional[str] = ..., + version: Optional[str] = ..., + **kwargs: Any + ) -> ReleaseKeyResult: ... + + @distributed_trace_async + async def restore_key_backup( + self, + backup: bytes, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def rotate_key( + self, + name: str, + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: ... + + @distributed_trace_async + async def update_key_properties( + self, + name: str, + version: Optional[str] = None, + *, + enabled: Optional[bool] = ..., + expires_on: Optional[datetime] = ..., + key_operations: Optional[List[Union[str, KeyOperation]]] = ..., + not_before: Optional[datetime] = ..., + release_policy: Optional[KeyReleasePolicy] = ..., + tags: Optional[Dict[str, str]] = ..., + **kwargs: Any + ) -> KeyVaultKey: ... + + @distributed_trace_async + async def update_key_rotation_policy( + self, + key_name: str, + policy: KeyRotationPolicy, + *, + expires_in: Optional[str] = ..., + lifetime_actions: Optional[List[KeyRotationLifetimeAction]] = ..., + **kwargs: Any + ) -> KeyRotationPolicy: ... + + +namespace azure.keyvault.keys.crypto + + class azure.keyvault.keys.crypto.CryptographyClient(KeyVaultClientBase): implements ContextManager + property key_id: Optional[str] # Read-only + property vault_url: Optional[str] # Read-only + + def __init__( + self, + key: Union[KeyVaultKey, str], + credential: TokenCredential, + *, + api_version: Union[ApiVersion, str] = ..., + verify_challenge_resource: Optional[bool] = ..., + **kwargs: Any + ) -> None: ... + + @classmethod + def from_jwk(cls, jwk: Union[JsonWebKey, Dict[str, Any]]) -> CryptographyClient: ... + + def close(self) -> None: ... + + @distributed_trace + def create_rsa_private_key(self) -> KeyVaultRSAPrivateKey: ... + + @distributed_trace + def create_rsa_public_key(self) -> KeyVaultRSAPublicKey: ... + + @distributed_trace + def decrypt( + self, + algorithm: EncryptionAlgorithm, + ciphertext: bytes, + *, + additional_authenticated_data: Optional[bytes] = ..., + authentication_tag: Optional[bytes] = ..., + iv: Optional[bytes] = ..., + **kwargs: Any + ) -> DecryptResult: ... + + @distributed_trace + def encrypt( + self, + algorithm: EncryptionAlgorithm, + plaintext: bytes, + *, + additional_authenticated_data: Optional[bytes] = ..., + iv: Optional[bytes] = ..., + **kwargs: Any + ) -> EncryptResult: ... + + @distributed_trace + def secure_unwrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + encrypted_key: bytes, + target_attestation_token: str, + **kwargs: Any + ) -> SecureUnwrapResult: ... + + @distributed_trace + def secure_wrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + **kwargs: Any + ) -> SecureWrapResult: ... + + @distributed_trace + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> HttpResponse: ... + + @distributed_trace + def sign( + self, + algorithm: SignatureAlgorithm, + digest: bytes, + **kwargs: Any + ) -> SignResult: ... + + @distributed_trace + def unwrap_key( + self, + algorithm: KeyWrapAlgorithm, + encrypted_key: bytes, + **kwargs: Any + ) -> UnwrapResult: ... + + @distributed_trace + def verify( + self, + algorithm: SignatureAlgorithm, + digest: bytes, + signature: bytes, + **kwargs: Any + ) -> VerifyResult: ... + + @distributed_trace + def wrap_key( + self, + algorithm: KeyWrapAlgorithm, + key: bytes, + **kwargs: Any + ) -> WrapResult: ... + + + class azure.keyvault.keys.crypto.DecryptResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: EncryptionAlgorithm, + plaintext: bytes + ) -> None: ... + + + class azure.keyvault.keys.crypto.EncryptResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: EncryptionAlgorithm, + ciphertext: bytes, + *, + additional_authenticated_data: Optional[bytes] = ..., + authentication_tag: Optional[bytes] = ..., + iv: Optional[bytes] = ..., + **kwargs: Any + ) -> None: ... + + + class azure.keyvault.keys.crypto.EncryptionAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + a128_cbc = "A128CBC" + a128_cbcpad = "A128CBCPAD" + a128_gcm = "A128GCM" + a192_cbc = "A192CBC" + a192_cbcpad = "A192CBCPAD" + a192_gcm = "A192GCM" + a256_cbc = "A256CBC" + a256_cbcpad = "A256CBCPAD" + a256_gcm = "A256GCM" + rsa1_5 = "RSA1_5" + rsa_oaep = "RSA-OAEP" + rsa_oaep_256 = "RSA-OAEP-256" + + + class azure.keyvault.keys.crypto.KeySecureWrapAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + aes_128 = "A128KW" + aes_128_pad = "A128KWPAD" + aes_192 = "A192KW" + aes_192_pad = "A192KWPAD" + aes_256 = "A256KW" + aes_256_pad = "A256KWPAD" + ckm_aes_key_wrap = "CKM_AES_KEY_WRAP" + ckm_aes_key_wrap_pad = "CKM_AES_KEY_WRAP_PAD" + rsa_oaep_256 = "RSA-OAEP-256" + + + class azure.keyvault.keys.crypto.KeyVaultRSAPrivateKey(RSAPrivateKey): + property key_size: int # Read-only + + def __copy__(self) -> KeyVaultRSAPrivateKey: ... + + def __deepcopy__(self, memo: dict) -> KeyVaultRSAPrivateKey: ... + + def __init__( + self, + client: CryptographyClient, + key_material: Optional[JsonWebKey] + ) -> None: ... + + def decrypt( + self, + ciphertext: bytes, + padding: AsymmetricPadding + ) -> bytes: ... + + def private_bytes( + self, + encoding: Encoding, + format: PrivateFormat, + encryption_algorithm: KeySerializationEncryption + ) -> bytes: ... + + def private_numbers(self) -> RSAPrivateNumbers: ... + + def public_key(self) -> KeyVaultRSAPublicKey: ... + + def sign( + self, + data: bytes, + padding: AsymmetricPadding, + algorithm: Union[Prehashed, HashAlgorithm] + ) -> bytes: ... + + def signer( + self, + padding: AsymmetricPadding, + algorithm: HashAlgorithm + ) -> NoReturn: ... + + + class azure.keyvault.keys.crypto.KeyVaultRSAPublicKey(RSAPublicKey): + property key_size: int # Read-only + + def __copy__(self) -> KeyVaultRSAPublicKey: ... + + def __deepcopy__(self, memo: dict) -> KeyVaultRSAPublicKey: ... + + def __eq__(self, other: object) -> bool: ... + + def __init__( + self, + client: CryptographyClient, + key_material: Optional[JsonWebKey] = None + ) -> None: ... + + def encrypt( + self, + plaintext: bytes, + padding: AsymmetricPadding + ) -> bytes: ... + + def public_bytes( + self, + encoding: Encoding, + format: PublicFormat + ) -> bytes: ... + + def public_numbers(self) -> RSAPublicNumbers: ... + + def recover_data_from_signature( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: Optional[HashAlgorithm] + ) -> bytes: ... + + def verifier( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: HashAlgorithm + ) -> NoReturn: ... + + def verify( + self, + signature: bytes, + data: bytes, + padding: AsymmetricPadding, + algorithm: Union[Prehashed, HashAlgorithm] + ) -> None: ... + + + class azure.keyvault.keys.crypto.KeyWrapAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + aes_128 = "A128KW" + aes_192 = "A192KW" + aes_256 = "A256KW" + ckm_aes_key_wrap = "CKM_AES_KEY_WRAP" + ckm_aes_key_wrap_pad = "CKM_AES_KEY_WRAP_PAD" + rsa1_5 = "RSA1_5" + rsa_oaep = "RSA-OAEP" + rsa_oaep_256 = "RSA-OAEP-256" + + + class azure.keyvault.keys.crypto.SecureUnwrapResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: KeySecureWrapAlgorithm, + key: bytes + ) -> None: ... + + + class azure.keyvault.keys.crypto.SecureWrapResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: KeySecureWrapAlgorithm, + encrypted_key: bytes + ) -> None: ... + + + class azure.keyvault.keys.crypto.SignResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: SignatureAlgorithm, + signature: bytes + ) -> None: ... + + + class azure.keyvault.keys.crypto.SignatureAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta): + es256 = "ES256" + es256_k = "ES256K" + es384 = "ES384" + es512 = "ES512" + hs256 = "HS256" + hs384 = "HS384" + hs512 = "HS512" + ps256 = "PS256" + ps384 = "PS384" + ps512 = "PS512" + rs256 = "RS256" + rs384 = "RS384" + rs512 = "RS512" + + + class azure.keyvault.keys.crypto.UnwrapResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: KeyWrapAlgorithm, + key: bytes + ) -> None: ... + + + class azure.keyvault.keys.crypto.VerifyResult: + + def __init__( + self, + key_id: Optional[str], + is_valid: bool = False, + algorithm: SignatureAlgorithm + ) -> None: ... + + + class azure.keyvault.keys.crypto.WrapResult: + + def __init__( + self, + key_id: Optional[str], + algorithm: KeyWrapAlgorithm, + encrypted_key: bytes + ) -> None: ... + + +namespace azure.keyvault.keys.crypto.aio + + class azure.keyvault.keys.crypto.aio.CryptographyClient(AsyncKeyVaultClientBase): implements AsyncContextManager + property key_id: Optional[str] # Read-only + property vault_url: Optional[str] # Read-only + + def __init__( + self, + key: Union[KeyVaultKey, str], + credential: AsyncTokenCredential, + *, + api_version: Union[ApiVersion, str] = ..., + verify_challenge_resource: Optional[bool] = ..., + **kwargs: Any + ) -> None: ... + + @classmethod + def from_jwk(cls, jwk: Union[JsonWebKey, Dict[str, Any]]) -> CryptographyClient: ... + + async def close(self) -> None: ... + + @distributed_trace_async + async def decrypt( + self, + algorithm: EncryptionAlgorithm, + ciphertext: bytes, + *, + additional_authenticated_data: Optional[bytes] = ..., + authentication_tag: Optional[bytes] = ..., + iv: Optional[bytes] = ..., + **kwargs: Any + ) -> DecryptResult: ... + + @distributed_trace_async + async def encrypt( + self, + algorithm: EncryptionAlgorithm, + plaintext: bytes, + *, + additional_authenticated_data: Optional[bytes] = ..., + iv: Optional[bytes] = ..., + **kwargs: Any + ) -> EncryptResult: ... + + @distributed_trace_async + async def secure_unwrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + encrypted_key: bytes, + target_attestation_token: str, + **kwargs: Any + ) -> SecureUnwrapResult: ... + + @distributed_trace_async + async def secure_wrap_key( + self, + algorithm: KeySecureWrapAlgorithm, + **kwargs: Any + ) -> SecureWrapResult: ... + + @distributed_trace_async + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: ... + + @distributed_trace_async + async def sign( + self, + algorithm: SignatureAlgorithm, + digest: bytes, + **kwargs: Any + ) -> SignResult: ... + + @distributed_trace_async + async def unwrap_key( + self, + algorithm: KeyWrapAlgorithm, + encrypted_key: bytes, + **kwargs: Any + ) -> UnwrapResult: ... + + @distributed_trace_async + async def verify( + self, + algorithm: SignatureAlgorithm, + digest: bytes, + signature: bytes, + **kwargs: Any + ) -> VerifyResult: ... + + @distributed_trace_async + async def wrap_key( + self, + algorithm: KeyWrapAlgorithm, + key: bytes, + **kwargs: Any + ) -> WrapResult: ... + + +``` \ No newline at end of file diff --git a/sdk/keyvault/azure-keyvault-keys/api.metadata.yml b/sdk/keyvault/azure-keyvault-keys/api.metadata.yml new file mode 100644 index 000000000000..2212d8480523 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/api.metadata.yml @@ -0,0 +1,3 @@ +apiMdSha256: 66369ee9edffb0a52e64a5da097275d309c0809f04fe69c1566fc1c8adc531e6 +parserVersion: 0.3.28 +pythonVersion: 3.13.14 diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py index 24dab8ff5ad7..20220a0ad990 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py @@ -37,6 +37,8 @@ class KeyOperation(str, Enum, metaclass=CaseInsensitiveEnumMeta): verify = "verify" wrap_key = "wrapKey" unwrap_key = "unwrapKey" + secure_wrap_key = "secureWrapKey" + secure_unwrap_key = "secureUnwrapKey" export = "export" diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py index 115378f0fc50..8632e1a40a11 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py @@ -503,9 +503,7 @@ def secure_wrap_key(self, algorithm: KeySecureWrapAlgorithm, **kwargs: Any) -> S :rtype: ~azure.keyvault.keys.crypto.SecureWrapResult """ if self._jwk: - raise NotImplementedError( - "Secure wrap is not supported on a CryptographyClient created from a JsonWebKey" - ) + raise NotImplementedError("Secure wrap is not supported on a CryptographyClient created from a JsonWebKey") operation_result = self._client.secure_wrap_key( key_name=self._key_id.name if self._key_id else None, key_version=self._key_id.version if self._key_id else None, diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py index 2006e12d276b..36e7568a3dba 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py @@ -427,9 +427,7 @@ async def secure_wrap_key(self, algorithm: KeySecureWrapAlgorithm, **kwargs: Any :rtype: ~azure.keyvault.keys.crypto.SecureWrapResult """ if self._jwk: - raise NotImplementedError( - "Secure wrap is not supported on a CryptographyClient created from a JsonWebKey" - ) + raise NotImplementedError("Secure wrap is not supported on a CryptographyClient created from a JsonWebKey") operation_result = await self._client.secure_wrap_key( key_name=self._key_id.name if self._key_id else None, key_version=self._key_id.version if self._key_id else None, diff --git a/sdk/keyvault/azure-keyvault-keys/pyproject.toml b/sdk/keyvault/azure-keyvault-keys/pyproject.toml index 36d5b7306ef9..2ab9d5f8b531 100644 --- a/sdk/keyvault/azure-keyvault-keys/pyproject.toml +++ b/sdk/keyvault/azure-keyvault-keys/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] requires-python = ">=3.10" keywords = ["azure", "azure sdk"] diff --git a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py index 40846ad01dea..6b21013d6f93 100644 --- a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py +++ b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations.py @@ -7,7 +7,7 @@ import os from azure.identity import DefaultAzureCredential -from azure.keyvault.keys import KeyClient, KeyReleasePolicy +from azure.keyvault.keys import KeyClient, KeyOperation, KeyReleasePolicy from azure.keyvault.keys.crypto import CryptographyClient, KeySecureWrapAlgorithm # ---------------------------------------------------------------------------------------------------------- @@ -71,7 +71,7 @@ wrapping_key = key_client.create_rsa_key( key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) print(f"Wrapping key '{wrapping_key.name}' created of type '{wrapping_key.key_type}'.") @@ -83,9 +83,7 @@ # (`encrypted_key`) can later be exchanged with a target TEE via secure_unwrap_key. print("\n.. Securely wrap a TEE-generated key") wrap_result = crypto_client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) -print( - f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'." -) +print(f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'.") encrypted_key = wrap_result.encrypted_key # Unwrap the key into a target TEE. This requires a valid attestation token signed by Microsoft Azure @@ -96,6 +94,4 @@ unwrap_result = crypto_client.secure_unwrap_key( KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token ) -print( - f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'." -) +print(f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'.") diff --git a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py index 6e41951036fe..5f106e3aabf2 100644 --- a/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py +++ b/sdk/keyvault/azure-keyvault-keys/samples/secure_key_operations_async.py @@ -8,7 +8,7 @@ import os from azure.identity.aio import DefaultAzureCredential -from azure.keyvault.keys import KeyReleasePolicy +from azure.keyvault.keys import KeyOperation, KeyReleasePolicy from azure.keyvault.keys.aio import KeyClient from azure.keyvault.keys.crypto import KeySecureWrapAlgorithm from azure.keyvault.keys.crypto.aio import CryptographyClient @@ -76,24 +76,18 @@ async def run_sample(): wrapping_key = await key_client.create_rsa_key( key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) - print( - f"Wrapping key '{wrapping_key.name}' created of type '{wrapping_key.key_type}'." - ) + print(f"Wrapping key '{wrapping_key.name}' created of type '{wrapping_key.key_type}'.") # Build a CryptographyClient bound to the wrapping key. crypto_client = CryptographyClient(wrapping_key, credential=credential) # Securely wrap a key generated inside the HSM trusted execution environment. print("\n.. Securely wrap a TEE-generated key") - wrap_result = await crypto_client.secure_wrap_key( - KeySecureWrapAlgorithm.rsa_oaep_256 - ) - print( - f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'." - ) + wrap_result = await crypto_client.secure_wrap_key(KeySecureWrapAlgorithm.rsa_oaep_256) + print(f"Wrapped key produced for '{wrap_result.key_id}' using '{wrap_result.algorithm}'.") encrypted_key = wrap_result.encrypted_key # Unwrap the key into a target TEE. This requires a valid attestation token signed by Microsoft Azure @@ -104,9 +98,7 @@ async def run_sample(): unwrap_result = await crypto_client.secure_unwrap_key( KeySecureWrapAlgorithm.rsa_oaep_256, encrypted_key, target_attestation_token ) - print( - f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'." - ) + print(f"Unwrapped key returned for '{unwrap_result.key_id}' using '{unwrap_result.algorithm}'.") print("\nrun_sample done") await crypto_client.close() diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py index 06778949a6dc..1f561fefcec1 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py @@ -455,7 +455,7 @@ def test_secure_wrap_and_unwrap(self, key_client, **kwargs): key_client, key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) crypto_client = self.create_crypto_client(wrapping_key, api_version=key_client.api_version) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py index fb0ab19f9721..36b61d6bd43d 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py @@ -1,4 +1,4 @@ -# pylint: disable=line-too-long,useless-suppression +# pylint: disable=line-too-long,useless-suppression,too-many-lines # ------------------------------------ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. @@ -36,9 +36,7 @@ all_api_versions = get_decorator(is_async=True) only_hsm = get_decorator(only_hsm=True, is_async=True) -only_hsm_2026_default = get_decorator( - only_hsm=True, is_async=True, api_versions=[DEFAULT_VERSION] -) +only_hsm_2026_default = get_decorator(only_hsm=True, is_async=True, api_versions=[DEFAULT_VERSION]) only_vault_7_4_plus = get_decorator(only_vault=True, is_async=True, api_versions=[ApiVersion.V7_4, ApiVersion.V7_5]) @@ -316,7 +314,7 @@ async def test_secure_wrap_and_unwrap(self, key_client, **kwargs): key_client, key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) crypto_client = self.create_crypto_client(wrapping_key, is_async=True, api_version=key_client.api_version) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py index 39c211732a96..8579041709f4 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto.py @@ -4,6 +4,7 @@ # ------------------------------------ import pytest from azure.core.exceptions import HttpResponseError +from azure.keyvault.keys import KeyOperation from azure.keyvault.keys.crypto import CryptographyClient from azure.keyvault.keys._shared.client_base import DEFAULT_VERSION from devtools_testutils import recorded_by_proxy, set_bodiless_matcher @@ -125,7 +126,7 @@ def test_secure_wrap_unwrap(self, key_client, **kwargs): key = key_client.create_rsa_key( key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) client = CryptographyClient(key, credential, api_version=key_client.api_version) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py index 10072742aae0..7fd84d40a233 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_examples_crypto_async.py @@ -4,6 +4,7 @@ # ------------------------------------ import pytest from azure.core.exceptions import HttpResponseError +from azure.keyvault.keys import KeyOperation from azure.keyvault.keys.crypto.aio import CryptographyClient from azure.keyvault.keys._shared.client_base import DEFAULT_VERSION from devtools_testutils import set_bodiless_matcher @@ -135,7 +136,7 @@ async def test_secure_wrap_unwrap_async(self, key_client, **kwargs): key = await key_client.create_rsa_key( key_name, hardware_protected=True, - key_operations=["secureWrapKey", "secureUnwrapKey"], + key_operations=[KeyOperation.secure_wrap_key, KeyOperation.secure_unwrap_key], release_policy=release_policy, ) client = CryptographyClient(key, credential, api_version=key_client.api_version) diff --git a/sdk/keyvault/cspell.json b/sdk/keyvault/cspell.json index ef10cfe00d14..1c0c7db29ba8 100644 --- a/sdk/keyvault/cspell.json +++ b/sdk/keyvault/cspell.json @@ -1,6 +1,7 @@ { "ignoreWords": [ "keyvaultekmclient", + "kwpad", "spiffe" ] }