Skip to content

Commit

Permalink
Implement export_key and release_key
Browse files Browse the repository at this point in the history
  • Loading branch information
mccoyp committed Apr 14, 2021
1 parent f701f3c commit 32199ca
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 20 deletions.
15 changes: 14 additions & 1 deletion sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,32 @@
from ._parse_id import parse_key_vault_key_id
from ._shared.client_base import ApiVersion
from ._shared import KeyVaultResourceId
from ._models import DeletedKey, JsonWebKey, KeyProperties, KeyVaultKey
from ._models import (
DeletedKey,
JsonWebKey,
KeyExportParameters,
KeyProperties,
KeyReleaseParameters,
KeyReleasePolicy,
KeyReleaseResult,
KeyVaultKey,
)
from ._client import KeyClient

__all__ = [
"ApiVersion",
"KeyClient",
"JsonWebKey",
"KeyExportParameters",
"KeyVaultKey",
"KeyCurveName",
"KeyOperation",
"KeyType",
"DeletedKey",
"KeyProperties",
"KeyReleaseParameters",
"KeyReleasePolicy",
"KeyReleaseResult",
"parse_key_vault_key_id",
"KeyVaultResourceId"
]
Expand Down
96 changes: 87 additions & 9 deletions sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ._shared import KeyVaultClientBase
from ._shared.exceptions import error_map as _error_map
from ._shared._polling import DeleteRecoverPollingMethod, KeyVaultOperationPoller
from ._models import KeyVaultKey, KeyProperties, DeletedKey
from ._models import KeyVaultKey, KeyProperties, KeyReleaseResult, DeletedKey

try:
from typing import TYPE_CHECKING
Expand All @@ -20,7 +20,7 @@
from typing import Any, List, Optional, Union
from datetime import datetime
from azure.core.paging import ItemPaged
from ._models import JsonWebKey
from ._models import JsonWebKey, KeyExportParameters, KeyReleaseParameters


class KeyClient(KeyVaultClientBase):
Expand Down Expand Up @@ -67,6 +67,9 @@ def create_key(self, name, key_type, **kwargs):
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the private key can be exported.
:param release_policy: The policy rules under which the key can be exported.
:type release_policy: ~azure.keyvault.keys.KeyReleasePolicy
:returns: The created key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Expand All @@ -82,18 +85,27 @@ def create_key(self, name, key_type, **kwargs):
enabled = kwargs.pop("enabled", None)
not_before = kwargs.pop("not_before", None)
expires_on = kwargs.pop("expires_on", None)
if enabled is not None or not_before is not None or expires_on is not None:
attributes = self._models.KeyAttributes(enabled=enabled, not_before=not_before, expires=expires_on)
exportable = kwargs.pop("exportable", None)

if enabled is not None or not_before is not None or expires_on is not None or exportable is not None:
attributes = self._models.KeyAttributes(
enabled=enabled, not_before=not_before, expires=expires_on, exportable=exportable
)
else:
attributes = None

policy = kwargs.pop("release_policy", None)
release_policy = None if policy is None else self._models.KeyReleasePolicy(
data=policy.data, content_type=policy.content_type
)
parameters = self._models.KeyCreateParameters(
kty=key_type,
key_size=kwargs.pop("size", None),
key_attributes=attributes,
key_ops=kwargs.pop("key_operations", None),
tags=kwargs.pop("tags", None),
curve=kwargs.pop("curve", None)
curve=kwargs.pop("curve", None),
release_policy=release_policy
)

bundle = self._client.create_key(
Expand Down Expand Up @@ -123,6 +135,9 @@ def create_rsa_key(self, name, **kwargs):
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the private key can be exported.
:param release_policy: The policy rules under which the key can be exported.
:type release_policy: ~azure.keyvault.keys.KeyReleasePolicy
:returns: The created key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Expand Down Expand Up @@ -157,6 +172,9 @@ def create_ec_key(self, name, **kwargs):
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the private key can be exported.
:param release_policy: The policy rules under which the key can be exported.
:type release_policy: ~azure.keyvault.keys.KeyReleasePolicy
:returns: The created key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Expand Down Expand Up @@ -409,7 +427,6 @@ def begin_recover_deleted_key(self, name, **kwargs):

return KeyVaultOperationPoller(polling_method)


@distributed_trace
def update_key_properties(self, name, version=None, **kwargs):
# type: (str, Optional[str], **Any) -> KeyVaultKey
Expand Down Expand Up @@ -536,23 +553,35 @@ def import_key(self, name, key, **kwargs):
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the private key can be exported.
:param release_policy: The policy rules under which the key can be exported.
:type release_policy: ~azure.keyvault.keys.KeyReleasePolicy
:returns: The imported key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`
"""
enabled = kwargs.pop("enabled", None)
not_before = kwargs.pop("not_before", None)
expires_on = kwargs.pop("expires_on", None)
if enabled is not None or not_before is not None or expires_on is not None:
attributes = self._models.KeyAttributes(enabled=enabled, not_before=not_before, expires=expires_on)
exportable = kwargs.pop("exportable", None)

if enabled is not None or not_before is not None or expires_on is not None or exportable is not None:
attributes = self._models.KeyAttributes(
enabled=enabled, not_before=not_before, expires=expires_on, exportable=exportable
)
else:
attributes = None

policy = kwargs.pop("release_policy", None)
release_policy = None if policy is None else self._models.KeyReleasePolicy(
data=policy.data, content_type=policy.content_type
)
parameters = self._models.KeyImportParameters(
key=key._to_generated_model(),
key_attributes=attributes,
hsm=kwargs.pop("hardware_protected", None),
tags=kwargs.pop("tags", None)
tags=kwargs.pop("tags", None),
release_policy=release_policy
)

bundle = self._client.import_key(
Expand All @@ -563,3 +592,52 @@ def import_key(self, name, key, **kwargs):
**kwargs
)
return KeyVaultKey._from_key_bundle(bundle)

@distributed_trace
def export_key(self, name, version, parameters, **kwargs):
# type: (str, str, KeyExportParameters, **Any) -> KeyVaultKey
"""Exports a key.
The export key operation is applicable to all key types. The target key must be marked
exportable. This operation requires the keys/export permission.
:param str name: The name of the key to export.
:param str version: A specific version of the key to export.
:param parameters: The parameters for the export operation.
:type parameters: ~azure.keyvault.keys.KeyExportParameters
:return: The exported key.
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`
"""
key_parameters = self._models.KeyExportParameters(
kek=parameters.key._to_generated_model(), enc=parameters.algorithm
)
bundle = self._client.export(self.vault_url, name, version, key_parameters, **kwargs)
return KeyVaultKey._from_key_bundle(bundle)

@distributed_trace
def release_key(self, name, version, parameters, **kwargs):
# type: (str, str, KeyReleaseParameters, **Any) -> KeyReleaseResult
"""Releases a key.
The release key operation is applicable to all key types. The target key must be marked
exportable. This operation requires the keys/release permission.
:param str name: The name of the key to get.
:param str version: A specific version of the key to release.
:param parameters: The parameters for the key release operation.
:type parameters: ~azure.keyvault.keys.KeyReleaseParameters
:return: The result of the key release.
:rtype: ~azure.keyvault.keys.KeyReleaseResult
:raises: :class:`~azure.core.exceptions.HttpResponseError`
"""
result = self._client.release(
vault_base_url=self._vault_url,
key_name=name,
key_version=version,
parameters=self._models.KeyReleaseParameters(
environment=parameters.environment, nonce=parameters.nonce, enc=parameters.algorithm
),
**kwargs
)
return KeyReleaseResult(result.value)
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class KeyOperation(str, Enum):
verify = "verify"
wrap_key = "wrapKey"
unwrap_key = "unwrapKey"
export = "export"


class KeyType(str, Enum):
Expand Down
90 changes: 90 additions & 0 deletions sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ def _to_generated_model(self):
return jwk


class KeyExportParameters(object):
"""The parameters for a key export.
This includes a key and algorithm that are used for encrypting the exported key material.
:param key: The export encryption key. This must be an RSA key that supports encryption.
:type key: ~azure.keyvault.keys.JsonWebKey
:keyword str algorithm: The encryption algorithm to use to protected the exported key material. Possible values
include: "CKM_RSA_AES_KEY_WRAP", "RSA_AES_KEY_WRAP_256", "RSA_AES_KEY_WRAP_384".
"""

def __init__(self, key, **kwargs):
# type: (JsonWebKey, **Any) -> None
self.key = key
self.algorithm = kwargs.get("algorithm", None)


class KeyProperties(object):
"""A key's id and attributes."""

Expand Down Expand Up @@ -143,6 +160,15 @@ def expires_on(self):
:rtype: ~datetime.datetime
"""
return self._attributes.expires

@property
def exportable(self):
# type: () -> bool
"""Whether the private key can be exported
:rtype: bool
"""
return self._attributes.exportable

@property
def created_on(self):
Expand Down Expand Up @@ -211,6 +237,59 @@ def managed(self):
return self._managed


class KeyReleaseParameters(object):
"""The parameters for key release.
:param str environment: The target environment assertion.
:keyword str algorithm: The encryption algorithm to use to protect the released key material. Possible values
include: "CKM_RSA_AES_KEY_WRAP", "RSA_AES_KEY_WRAP_256", "RSA_AES_KEY_WRAP_384".
:keyword str nonce: A client-provided nonce for freshness.
"""

def __init__(self, environment, **kwargs):
# type: (str, **Any) -> None
self.environment = environment
self.algorithm = kwargs.get("algorithm", None)
self.nonce = kwargs.get("nonce", None)


class KeyReleasePolicy(object):
"""A key release policy.
:param data: Blob encoding the policy rules under which the key can be released.
:type data: bytes
:keyword content_type: Content type and version of key release policy. Defaults to
'application/json; charset=utf-8; version=1.0'.
:type content_type: str
"""

def __init__(self, data, **kwargs):
# type: (bytes, **Any) -> None
self.data = data
self.content_type = kwargs.get("content_type", None) or "application/json; charset=utf-8; version=1.0"

@classmethod
def _from_key_bundle(cls, key_bundle):
# type: (_models.KeyBundle) -> Optional[KeyReleasePolicy]
"""Construct a KeyReleasePolicy from an autorest-generated KeyBundle"""
release_policy = None
if hasattr(key_bundle, "release_policy"):
policy = key_bundle.release_policy
release_policy = None if policy is None else cls(data=policy.data, content_type=policy.content_type)
return release_policy


class KeyReleaseResult(object):
"""The release result, containing the released key.
:ivar str value: A signed object containing the released key.
"""

def __init__(self, value):
# type: (str) -> None
self.value = value


class KeyVaultKey(object):
"""A key's attributes and cryptographic material.
Expand Down Expand Up @@ -246,6 +325,7 @@ class KeyVaultKey(object):
def __init__(self, key_id, jwk=None, **kwargs):
# type: (str, Optional[dict], **Any) -> None
self._properties = kwargs.pop("properties", None) or KeyProperties(key_id, **kwargs)
self._release_policy = kwargs.pop("release_policy", None)
if isinstance(jwk, dict):
if any(field in kwargs for field in JsonWebKey._FIELDS): # pylint:disable=protected-access
raise ValueError(
Expand All @@ -268,6 +348,7 @@ def _from_key_bundle(cls, key_bundle):
key_id=key_bundle.key.kid,
jwk={field: getattr(key_bundle.key, field, None) for field in JsonWebKey._FIELDS},
properties=KeyProperties._from_key_bundle(key_bundle),
release_policy=KeyReleasePolicy._from_key_bundle(key_bundle)
)

@property
Expand Down Expand Up @@ -297,6 +378,15 @@ def properties(self):
"""
return self._properties

@property
def release_policy(self):
# type: () -> KeyReleasePolicy
"""The key's release policy
:rtype: ~azure.keyvault.keys.KeyReleasePolicy
"""
return self._release_policy

@property
def key(self):
# type: () -> JsonWebKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# AsyncTokenCredential is a typing_extensions.Protocol; we don't depend on that package
pass

DEFAULT_VERSION = ApiVersion.V7_2_preview
DEFAULT_VERSION = ApiVersion.V7_3_preview

class AsyncKeyVaultClientBase(object):
def __init__(self, vault_url: str, credential: "AsyncTokenCredential", **kwargs: "Any") -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ class ApiVersion(str, Enum):
"""Key Vault API versions supported by this package"""

#: this is the default version
V7_3_preview = "7.3-preview"
V7_2_preview = "7.2-preview"
V7_1 = "7.1"
V7_0 = "7.0"
V2016_10_01 = "2016-10-01"

DEFAULT_VERSION = ApiVersion.V7_2_preview
DEFAULT_VERSION = ApiVersion.V7_3_preview


class KeyVaultClientBase(object):
Expand Down
Loading

0 comments on commit 32199ca

Please sign in to comment.