Skip to content

Commit c91b480

Browse files
authored
[Keyvault] az keyvault key get-attestation: Support getting a MHSM key's attestation (#31427)
1 parent e5f87ec commit c91b480

File tree

7 files changed

+211
-3
lines changed

7 files changed

+211
-3
lines changed

src/azure-cli/azure/cli/command_modules/keyvault/_help.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,19 @@
487487
material of a key itself cannot be changed. This operation requires the keys/update permission.
488488
"""
489489

490+
helps['keyvault key get-attestation'] = """
491+
type: command
492+
short-summary: Get a key's attestation blob.
493+
long-summary: This command is applicable to any key stored in Azure Key Vault Managed HSM. This operation requires the keys/get permission.
494+
examples:
495+
- name: Get a key's attestation.
496+
text: |
497+
az keyvault key get-attestation --hsm-name myhsm -n mykey
498+
- name: Save the key's attestation to local file.
499+
text: |
500+
az keyvault key get-attestation --hsm-name myhsm -n mykey -f mykeyattestation.json
501+
"""
502+
490503
helps['keyvault key show-deleted'] = """
491504
type: command
492505
short-summary: Get the public part of a deleted key.

src/azure-cli/azure/cli/command_modules/keyvault/_params.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ class CLISecurityDomainOperation(str, Enum):
288288
# keys track2
289289
for scope in ['create', 'import', 'set-attributes', 'show', 'show-deleted', 'delete', 'list', 'list-deleted',
290290
'list-versions', 'encrypt', 'decrypt', 'sign', 'verify', 'recover', 'purge', 'download',
291-
'backup', 'restore', 'rotate', 'rotation-policy show', 'rotation-policy update']:
291+
'backup', 'restore', 'rotate', 'get-attestation', 'rotation-policy show', 'rotation-policy update']:
292292
with self.argument_context('keyvault key {}'.format(scope), arg_group='Id') as c:
293293
c.argument('name', options_list=['--name', '-n'], id_part='child_name_1',
294294
required=False, completer=get_keyvault_name_completion_list('key'),
@@ -315,6 +315,13 @@ class CLISecurityDomainOperation(str, Enum):
315315
help='The recovery id of the key. If specified all other \'Id\' arguments should be omitted.',
316316
validator=validate_keyvault_resource_id('key'))
317317

318+
with self.argument_context('keyvault key get-attestation') as c:
319+
c.argument('file_path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(),
320+
help="File to receive the key's attestation if you want to save it.")
321+
c.extra('hsm_name', data_plane_hsm_name_type, required=False, arg_group='Id',
322+
help='Name of the HSM. Required if --id is not specified.')
323+
c.ignore('vault_base_url')
324+
318325
with self.argument_context('keyvault key list') as c:
319326
c.extra('include_managed', arg_type=get_three_state_flag(), default=False,
320327
help='Include managed keys.')

src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ def transform_key_output(result, **command_args):
122122
if value and isinstance(value, bytes):
123123
setattr(result.key, attr, base64.b64encode(value))
124124

125+
# Avoid returning attestation info together with key properties
126+
# Customer should use specific `az keyvault key get-attestation` command
127+
if result.properties._attributes:
128+
result.properties._attributes.attestation = None
129+
125130
output = {
126131
'attributes': result.properties._attributes,
127132
'key': result.key,

src/azure-cli/azure/cli/command_modules/keyvault/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def load_command_table(self, _):
153153
g.keyvault_custom('create', 'create_key', transform=transform_key_output, validator=validate_key_create)
154154
g.keyvault_command('set-attributes', 'update_key_properties', transform=transform_key_output)
155155
g.keyvault_command('show', 'get_key', transform=transform_key_output)
156+
g.keyvault_custom('get-attestation', 'get_key_attestation')
156157
g.keyvault_custom('import', 'import_key', transform=transform_key_output)
157158
g.keyvault_custom('get-policy-template', 'get_policy_template', is_preview=True)
158159
g.keyvault_custom('encrypt', 'encrypt_key', is_preview=True, transform=transform_key_encryption_output)

src/azure-cli/azure/cli/command_modules/keyvault/custom.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from cryptography.x509 import load_pem_x509_certificate
3636

3737
from knack.log import get_logger
38-
from knack.util import CLIError
38+
from knack.util import CLIError, todict
3939

4040

4141
logger = get_logger(__name__)
@@ -1102,6 +1102,25 @@ def list_keys(client, maxresults=None, include_managed=False):
11021102
return result
11031103

11041104

1105+
def get_key_attestation(client, name, version=None, file_path=None):
1106+
key = client.get_key_attestation(name=name, version=version)
1107+
key_attestation = key.properties.attestation
1108+
if not file_path:
1109+
return key_attestation
1110+
1111+
if os.path.isfile(file_path) or os.path.isdir(file_path):
1112+
raise CLIError("File or directory named '{}' already exists.".format(file_path))
1113+
1114+
try:
1115+
from ._command_type import _encode_hex
1116+
with open(file_path, 'w') as outfile:
1117+
json.dump(todict(_encode_hex(key_attestation)), outfile)
1118+
except Exception as ex: # pylint: disable=broad-except
1119+
if os.path.isfile(file_path):
1120+
os.remove(file_path)
1121+
raise ex
1122+
1123+
11051124
def delete_key(client, name):
11061125
return client.begin_delete_key(name).result()
11071126

@@ -2116,7 +2135,6 @@ def full_backup(cmd, client, storage_resource_uri=None, storage_account_name=Non
21162135
storage_resource_uri = construct_storage_uri(
21172136
cmd.cli_ctx.cloud.suffixes.storage_endpoint, storage_account_name, blob_container_name)
21182137
poller = client.begin_backup(storage_resource_uri, sas_token=token, use_managed_identity=use_managed_identity)
2119-
from knack.util import todict
21202138
result = todict(poller.result())
21212139
result['status'] = poller.status()
21222140
return result

0 commit comments

Comments
 (0)