# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 import logging import sys import boto3 from alias_management import AliasManager from grant_management import GrantManager from key_encryption import KeyEncrypt from key_management import KeyManager from key_policies import KeyPolicy # Add relative path to include demo_tools in this code example without need for setup. sys.path.append("../..") import demo_tools.question as q DASHES = "-" * 80 # snippet-start:[python.example_code.kms.kms_basics] class KMSScenario: """Runs an interactive scenario that shows how to get started with KMS.""" def __init__( self, key_manager: KeyManager, key_encryption: KeyEncrypt, alias_manager: AliasManager, grant_manager: GrantManager, key_policy: KeyPolicy, ): self.key_manager = key_manager self.key_encryption = key_encryption self.alias_manager = alias_manager self.grant_manager = grant_manager self.key_policy = key_policy self.key_id = "" self.alias_name = "" self.asymmetric_key_id = "" def kms_scenario(self): key_description = "Created by the AWS KMS API" print(DASHES) print( """ Welcome to the AWS Key Management SDK Basics scenario. This program demonstrates how to interact with AWS Key Management using the AWS SDK for Python (Boto3). The AWS Key Management Service (KMS) is a secure and highly available service that allows you to create and manage AWS KMS keys and control their use across a wide range of AWS services and applications. KMS provides a centralized and unified approach to managing encryption keys, making it easier to meet your data protection and regulatory compliance requirements. This Basics scenario creates two key types: - A symmetric encryption key is used to encrypt and decrypt data. - An asymmetric key used to digitally sign data. Let's get started... """ ) q.ask("Press Enter to continue...") print(DASHES) print(f"1. Create a symmetric KMS key\n") print( f"First, the program will creates a symmetric KMS key that you can used to encrypt and decrypt data." ) q.ask("Press Enter to continue...") self.key_id = self.key_manager.create_key(key_description)["KeyId"] print(f"A symmetric key was successfully created {self.key_id}.") q.ask("Press Enter to continue...") print(DASHES) print( """ 2. Enable a KMS key By default, when the SDK creates an AWS key, it is enabled. The next bit of code checks to determine if the key is enabled. """ ) q.ask("Press Enter to continue...") is_enabled = self.is_key_enabled(self.key_id) print(f"Is the key enabled? {is_enabled}") if not is_enabled: self.key_manager.enable_key(self.key_id) q.ask("Press Enter to continue...") print(DASHES) print(f"3. Encrypt data using the symmetric KMS key") plain_text = "Hello, AWS KMS!" print( f""" One of the main uses of symmetric keys is to encrypt and decrypt data. Next, the code encrypts the string "{plain_text}" with the SYMMETRIC_DEFAULT encryption algorithm. """ ) q.ask("Press Enter to continue...") encrypted_text = self.key_encryption.encrypt(self.key_id, plain_text) print(DASHES) print(f"4. Create an alias") print( """ Now, the program will create an alias for the KMS key. An alias is a friendly name that you can associate with a KMS key. The alias name should be prefixed with 'alias/'. """ ) alias_name = q.ask("Enter an alias name: ", q.non_empty) self.alias_manager.create_alias(self.key_id, alias_name) print(f"{alias_name} was successfully created.") self.alias_name = alias_name print(DASHES) print(f"5. List all of your aliases") q.ask("Press Enter to continue...") self.alias_manager.list_aliases(10) q.ask("Press Enter to continue...") print(DASHES) print(f"6. Enable automatic rotation of the KMS key") print( """ By default, when the SDK enables automatic rotation of a KMS key, KMS rotates the key material of the KMS key one year (approximately 365 days) from the enable date and every year thereafter. """ ) q.ask("Press Enter to continue...") self.key_manager.enable_key_rotation(self.key_id) print(DASHES) print(f"Key rotation has been enabled for key with id {self.key_id}") print( """ 7. Create a grant A grant is a policy instrument that allows Amazon Web Services principals to use KMS keys. It also can allow them to view a KMS key (DescribeKey) and create and manage grants. When authorizing access to a KMS key, grants are considered along with key policies and IAM policies. """ ) print( """ To create a grant you must specify a account_id. To specify the grantee account_id, use the Amazon Resource Name (ARN) of an AWS account_id. Valid principals include AWS accounts, IAM users, IAM roles, federated users, and assumed role users. """ ) account_id = q.ask( "Enter an account_id, or press enter to skip creating a grant... " ) grant = None if account_id != "": grant = self.grant_manager.create_grant( self.key_id, account_id, [ "Encrypt", "Decrypt", "DescribeKey", ], ) print(f"Grant created successfully with ID: {grant['GrantId']}") q.ask("Press Enter to continue...") print(DASHES) print(DASHES) print(f"8. List grants for the KMS key") q.ask("Press Enter to continue...") self.grant_manager.list_grants(self.key_id) q.ask("Press Enter to continue...") print(DASHES) print(f"9. Revoke the grant") print( """ The revocation of a grant immediately removes the permissions and access that the grant had provided. This means that any account_id (user, role, or service) that was granted access to perform specific KMS operations on a KMS key will no longer be able to perform those operations. """ ) q.ask("Press Enter to continue...") if grant is not None: self.grant_manager.revoke_grant(self.key_id, grant["GrantId"]) print(f"Grant ID: {grant['GrantId']} was successfully revoked!") q.ask("Press Enter to continue...") print(DASHES) print(f"10. Decrypt the data\n") print( """ Lets decrypt the data that was encrypted in an early step. The code uses the same key to decrypt the string that we encrypted earlier in the program. """ ) q.ask("Press Enter to continue...") decrypted_data = self.key_encryption.decrypt(self.key_id, encrypted_text) print(f"Data decrypted successfully for key ID: {self.key_id}") print(f"Decrypted data: {decrypted_data}") q.ask("Press Enter to continue...") print(DASHES) print(f"11. Replace a key policy\n") print( """ A key policy is a resource policy for a KMS key. Key policies are the primary way to control access to KMS keys. Every KMS key must have exactly one key policy. The statements in the key policy determine who has permission to use the KMS key and how they can use it. You can also use IAM policies and grants to control access to the KMS key, but every KMS key must have a key policy. By default, when you create a key by using the SDK, a policy is created that gives the AWS account that owns the KMS key full access to the KMS key. Let's try to replace the automatically created policy with the following policy. { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::0000000000:root"}, "Action": "kms:*", "Resource": "*" }] } """ ) account_id = q.ask("Enter your account ID or press enter to skip: ") if account_id != "": policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"AWS": f"arn:aws:iam::{account_id}:root"}, "Action": "kms:*", "Resource": "*", } ], } self.key_policy.set_new_policy(self.key_id, policy) print("Key policy replacement succeeded.") q.ask("Press Enter to continue...") else: print("Skipping replacing the key policy.") print(DASHES) print(f"12. Get the key policy\n") print( f"The next bit of code that runs gets the key policy to make sure it exists." ) q.ask("Press Enter to continue...") policy = self.key_policy.get_policy(self.key_id) print(f"The key policy is: {policy}") q.ask("Press Enter to continue...") print(DASHES) print(f"13. Create an asymmetric KMS key and sign your data\n") print( """ Signing your data with an AWS key can provide several benefits that make it an attractive option for your data signing needs. By using an AWS KMS key, you can leverage the security controls and compliance features provided by AWS, which can help you meet various regulatory requirements and enhance the overall security posture of your organization. """ ) q.ask("Press Enter to continue...") print(f"Sign and verify data operation succeeded.") self.asymmetric_key_id = self.key_manager.create_asymmetric_key() message = "Here is the message that will be digitally signed" signature = self.key_encryption.sign(self.asymmetric_key_id, message) if self.key_encryption.verify(self.asymmetric_key_id, message, signature): print("Signature verification succeeded.") else: print("Signature verification failed.") q.ask("Press Enter to continue...") print(DASHES) print(f"14. Tag your symmetric KMS Key\n") print( """ By using tags, you can improve the overall management, security, and governance of your KMS keys, making it easier to organize, track, and control access to your encrypted data within your AWS environment """ ) q.ask("Press Enter to continue...") self.key_manager.tag_resource(self.key_id, "Environment", "Production") self.clean_up() def is_key_enabled(self, key_id: str) -> bool: """ Check if the key is enabled or not. :param key_id: The key to check. :return: True if the key is enabled, otherwise False. """ response = self.key_manager.describe_key(key_id) return response["Enabled"] is True def clean_up(self): """ Delete resources created by this scenario. """ if self.alias_name != "": print(f"Deleting the alias {self.alias_name}.") self.alias_manager.delete_alias(self.alias_name) window = 7 # The window in days for a scheduled deletion. if self.key_id != "": print( """ Warning: Deleting a KMS key is a destructive and potentially dangerous operation. When a KMS key is deleted, all data that was encrypted under the KMS key is unrecoverable. """ ) if q.ask( f"Do you want to delete the key with ID {self.key_id} (y/n)?", q.is_yesno, ): print( f"The key {self.key_id} will be deleted with a window of {window} days. You can cancel the deletion before" ) print("the window expires.") self.key_manager.delete_key(self.key_id, window) self.key_id = "" if self.asymmetric_key_id != "": if q.ask( f"Do you want to delete the asymmetric key with ID {self.asymmetric_key_id} (y/n)?", q.is_yesno, ): print( f"The key {self.asymmetric_key_id} will be deleted with a window of {window} days. You can cancel the deletion before" ) print("the window expires.") self.key_manager.delete_key(self.asymmetric_key_id, window) self.asymmetric_key_id = "" if __name__ == "__main__": kms_scenario = None try: kms_client = boto3.client("kms") a_key_manager = KeyManager(kms_client) a_key_encrypt = KeyEncrypt(kms_client) an_alias_manager = AliasManager(kms_client) a_grant_manager = GrantManager(kms_client) a_key_policy = KeyPolicy(kms_client) kms_scenario = KMSScenario( key_manager=a_key_manager, key_encryption=a_key_encrypt, alias_manager=an_alias_manager, grant_manager=a_grant_manager, key_policy=a_key_policy, ) kms_scenario.kms_scenario() except Exception: logging.exception("Something went wrong with the demo!") if kms_scenario is not None: kms_scenario.clean_up() # snippet-end:[python.example_code.kms.kms_basics]