From ae11c025931a1a64dc44cb9891332541937959d1 Mon Sep 17 00:00:00 2001 From: sercan tor Date: Mon, 17 Apr 2023 20:59:33 +0100 Subject: [PATCH] KMS: add `get_public_key` (#6222) --- IMPLEMENTATION_COVERAGE.md | 2 +- moto/kms/models.py | 11 ++++++++++- moto/kms/responses.py | 17 +++++++++++++++++ tests/test_kms/test_kms_boto3.py | 14 ++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 6c53e5894..84e6c06ee 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -4056,7 +4056,7 @@ - [X] get_key_policy - [X] get_key_rotation_status - [ ] get_parameters_for_import -- [ ] get_public_key +- [X] get_public_key - [ ] import_key_material - [ ] list_aliases - [X] list_grants diff --git a/moto/kms/models.py b/moto/kms/models.py index 0b1f4a2b2..5b0d49dcc 100644 --- a/moto/kms/models.py +++ b/moto/kms/models.py @@ -4,7 +4,7 @@ from collections import defaultdict from copy import copy from datetime import datetime, timedelta from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding from typing import Any, Dict, List, Tuple, Optional, Iterable, Set @@ -682,5 +682,14 @@ class KmsBackend(BaseBackend): except InvalidSignature: return key.arn, False, signing_algorithm + def get_public_key(self, key_id: str) -> Tuple[Key, bytes]: + key = self.describe_key(key_id) + public_key = key.private_key.public_key().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + + return key, public_key + kms_backends = BackendDict(KmsBackend, "kms") diff --git a/moto/kms/responses.py b/moto/kms/responses.py index e5a359d00..775d21cc0 100644 --- a/moto/kms/responses.py +++ b/moto/kms/responses.py @@ -721,6 +721,23 @@ class KmsResponse(BaseResponse): } ) + def get_public_key(self) -> str: + key_id = self._get_param("KeyId") + + self._validate_key_id(key_id) + self._validate_cmk_id(key_id) + key, public_key = self.kms_backend.get_public_key(key_id) + return json.dumps( + { + "CustomerMasterKeySpec": key.key_spec, + "EncryptionAlgorithms": key.encryption_algorithms, + "KeyId": key.id, + "KeyUsage": key.key_usage, + "PublicKey": base64.b64encode(public_key).decode("UTF-8"), + "SigningAlgorithms": key.signing_algorithms, + } + ) + def _assert_default_policy(policy_name: str) -> None: if policy_name != "default": diff --git a/tests/test_kms/test_kms_boto3.py b/tests/test_kms/test_kms_boto3.py index d04cdc919..20bae139c 100644 --- a/tests/test_kms/test_kms_boto3.py +++ b/tests/test_kms/test_kms_boto3.py @@ -1335,6 +1335,20 @@ def test_verify_empty_signature(): ) +@mock_kms +def test_get_public_key(): + client = boto3.client("kms", region_name="us-east-1") + key = client.create_key(KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048") + key_id = key["KeyMetadata"]["KeyId"] + public_key_response = client.get_public_key(KeyId=key_id) + + public_key_response.should.contain("PublicKey") + public_key_response["SigningAlgorithms"].should.equal( + key["KeyMetadata"]["SigningAlgorithms"] + ) + public_key_response.shouldnt.contain("EncryptionAlgorithms") + + def create_simple_key(client, id_or_arn="KeyId", description=None, policy=None): with mock.patch.object(rsa, "generate_private_key", return_value=""): params = {}