Learn the basics of AWS KMS with an AWS SDK - AWS SDK Code Examples

There are more AWS SDK examples available in the AWS Doc SDK Examples GitHub repo.

Learn the basics of AWS KMS with an AWS SDK

The following code examples show how to:

  • Create a KMS key.

  • List KMS keys for your account and get details about them.

  • Enable and disable KMS keys.

  • Generate a symmetric data key that can be used for client-side encryption.

  • Generate an asymmetric key used to digitally sign data.

  • Tag keys.

  • Delete KMS keys.

Java
SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

Run a scenario at a command prompt.

import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.regions.Region; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.kms.model.AlreadyExistsException; import software.amazon.awssdk.services.kms.model.DisabledException; import software.amazon.awssdk.services.kms.model.EnableKeyRotationResponse; import software.amazon.awssdk.services.kms.model.KmsException; import software.amazon.awssdk.services.kms.model.NotFoundException; import software.amazon.awssdk.services.kms.model.RevokeGrantResponse; import java.util.List; import java.util.Scanner; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; /** * Before running this Java V2 code example, set up your development * environment, including your credentials. * * For more information, see the following documentation topic: * * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html */ public class KMSScenario { public static final String DASHES = new String(new char[80]).replace("\0", "-"); private static String accountId = ""; private static final Logger logger = LoggerFactory.getLogger(KMSScenario.class); static KMSActions kmsActions = new KMSActions(); static Scanner scanner = new Scanner(System.in); static String aliasName = "alias/dev-encryption-key"; public static void main(String[] args) { final String usage = """ Usage: <granteePrincipal> Where: granteePrincipal - The principal (user, service account, or group) to whom the grant or permission is being given. """; if (args.length != 1) { logger.info(usage); return; } String granteePrincipal = args[0]; String policyName = "default"; accountId = kmsActions.getAccountId(); String keyDesc = "Created by the AWS KMS API"; logger.info(DASHES); logger.info(""" Welcome to the AWS Key Management SDK Basics scenario. This program demonstrates how to interact with AWS Key Management using the AWS SDK for Java (v2). 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... """); waitForInputToContinue(scanner); try { // Run the methods that belong to this scenario. String targetKeyId = runScenario(granteePrincipal, keyDesc, policyName); requestDeleteResources(aliasName, targetKeyId); } catch (Throwable rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } } private static String runScenario(String granteePrincipal, String keyDesc, String policyName) throws Throwable { logger.info(DASHES); logger.info("1. Create a symmetric KMS key\n"); logger.info("First, the program will creates a symmetric KMS key that you can used to encrypt and decrypt data."); waitForInputToContinue(scanner); String targetKeyId; try { CompletableFuture<String> futureKeyId = kmsActions.createKeyAsync(keyDesc); targetKeyId = futureKeyId.join(); logger.info("A symmetric key was successfully created " + targetKeyId); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(""" 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. """); waitForInputToContinue(scanner); boolean isEnabled; try { CompletableFuture<Boolean> futureIsKeyEnabled = kmsActions.isKeyEnabledAsync(targetKeyId); isEnabled = futureIsKeyEnabled.join(); logger.info("Is the key enabled? {}", isEnabled); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } throw cause; } if (!isEnabled) try { CompletableFuture<Void> future = kmsActions.enableKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("3. Encrypt data using the symmetric KMS key"); String plaintext = "Hello, AWS KMS!"; logger.info(""" One of the main uses of symmetric keys is to encrypt and decrypt data. Next, the code encrypts the string {} with the SYMMETRIC_DEFAULT encryption algorithm. """, plaintext); waitForInputToContinue(scanner); SdkBytes encryptedData; try { CompletableFuture<SdkBytes> future = kmsActions.encryptDataAsync(targetKeyId, plaintext); encryptedData = future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof DisabledException kmsDisabledEx) { logger.info("KMS error occurred due to a disabled key: Error message: {}, Error code {}", kmsDisabledEx.getMessage(), kmsDisabledEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("4. Create an alias"); logger.info(""" The alias name should be prefixed with 'alias/'. The default, 'alias/dev-encryption-key'. """); waitForInputToContinue(scanner); try { CompletableFuture<Void> future = kmsActions.createCustomAliasAsync(targetKeyId, aliasName); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof AlreadyExistsException kmsExistsEx) { if (kmsExistsEx.getMessage().contains("already exists")) { logger.info("The alias '" + aliasName + "' already exists. Moving on..."); } } else { logger.error("An unexpected error occurred: " + rt.getMessage(), rt); deleteKey(targetKeyId); throw cause; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("5. List all of your aliases"); waitForInputToContinue(scanner); try { CompletableFuture<Object> future = kmsActions.listAllAliasesAsync(); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("6. Enable automatic rotation of the KMS key"); logger.info(""" 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. """); waitForInputToContinue(scanner); try { CompletableFuture<EnableKeyRotationResponse> future = kmsActions.enableKeyRotationAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(""" 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. """); waitForInputToContinue(scanner); String grantId = null; try { CompletableFuture<String> futureGrantId = kmsActions.grantKeyAsync(targetKeyId, granteePrincipal); grantId = futureGrantId.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("8. List grants for the KMS key"); waitForInputToContinue(scanner); try { CompletableFuture<Object> future = kmsActions.displayGrantIdsAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("9. Revoke the grant"); logger.info(""" The revocation of a grant immediately removes the permissions and access that the grant had provided. This means that any principal (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. """); waitForInputToContinue(scanner); try { CompletableFuture<RevokeGrantResponse> future = kmsActions.revokeKeyGrantAsync(targetKeyId, grantId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { if (kmsEx.getMessage().contains("Grant does not exist")) { logger.info("The grant ID '" + grantId + "' does not exist. Moving on..."); } else { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); throw cause; } } else { logger.info("An unexpected error occurred: " + rt.getMessage()); deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("10. Decrypt the data\n"); logger.info(""" 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. """); waitForInputToContinue(scanner); String decryptedData = ""; try { CompletableFuture<String> future = kmsActions.decryptDataAsync(encryptedData, targetKeyId); decryptedData = future.join(); logger.info("Decrypted data: " + decryptedData); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } logger.info("Decrypted text is: " + decryptedData); waitForInputToContinue(scanner); logger.info(DASHES); logger.info("11. Replace a key policy\n"); logger.info(""" 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": "*" }] """); waitForInputToContinue(scanner); try { CompletableFuture<Boolean> future = kmsActions.replacePolicyAsync(targetKeyId, policyName, accountId); boolean success = future.join(); if (success) { logger.info("Key policy replacement succeeded."); } else { logger.error("Key policy replacement failed."); } } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("12. Get the key policy\n"); logger.info("The next bit of code that runs gets the key policy to make sure it exists."); waitForInputToContinue(scanner); try { CompletableFuture<String> future = kmsActions.getKeyPolicyAsync(targetKeyId, policyName); String policy = future.join(); if (!policy.isEmpty()) { logger.info("Retrieved policy: " + policy); } } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("13. Create an asymmetric KMS key and sign your data\n"); logger.info(""" 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. """); waitForInputToContinue(scanner); try { CompletableFuture<Boolean> future = kmsActions.signVerifyDataAsync(); boolean success = future.join(); if (success) { logger.info("Sign and verify data operation succeeded."); } else { logger.error("Sign and verify data operation failed."); } } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info("14. Tag your symmetric KMS Key\n"); logger.info(""" 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 """); waitForInputToContinue(scanner); try { CompletableFuture<Void> future = kmsActions.tagKMSKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } deleteAliasName(aliasName); deleteKey(targetKeyId); throw cause; } waitForInputToContinue(scanner); return targetKeyId; } // Deletes KMS resources with user input. private static void requestDeleteResources(String aliasName, String targetKeyId) { logger.info(DASHES); logger.info("15. Schedule the deletion of the KMS key\n"); logger.info(""" By default, KMS applies a waiting period of 30 days, but you can specify a waiting period of 7-30 days. When this operation is successful, the key state of the KMS key changes to PendingDeletion and the key can't be used in any cryptographic operations. It remains in this state for the duration of the waiting period. 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. """); logger.info("Would you like to delete the Key Management resources? (y/n)"); String delAns = scanner.nextLine().trim(); if (delAns.equalsIgnoreCase("y")) { logger.info("You selected to delete the AWS KMS resources."); waitForInputToContinue(scanner); try { CompletableFuture<Void> future = kmsActions.deleteSpecificAliasAsync(aliasName); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } waitForInputToContinue(scanner); try { CompletableFuture<Void> future = kmsActions.disableKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } try { CompletableFuture<Void> future = kmsActions.deleteKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } } else { logger.info("The Key Management resources will not be deleted"); } logger.info(DASHES); logger.info("This concludes the AWS Key Management SDK scenario"); logger.info(DASHES); } // This method is invoked from Exceptions to clean up the resources. private static void deleteKey(String targetKeyId) { try { CompletableFuture<Void> future = kmsActions.disableKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } try { CompletableFuture<Void> future = kmsActions.deleteKeyAsync(targetKeyId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } } // This method is invoked from Exceptions to clean up the resources. private static void deleteAliasName(String aliasName) { try { CompletableFuture<Void> future = kmsActions.deleteSpecificAliasAsync(aliasName); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof KmsException kmsEx) { logger.info("KMS error occurred: Error message: {}, Error code {}", kmsEx.getMessage(), kmsEx.awsErrorDetails().errorCode()); } else { logger.info("An unexpected error occurred: " + rt.getMessage()); } } } private static void waitForInputToContinue(Scanner scanner) { while (true) { logger.info(""); logger.info("Enter 'c' followed by <ENTER> to continue:"); String input = scanner.nextLine(); if (input.trim().equalsIgnoreCase("c")) { logger.info("Continuing with the program..."); logger.info(""); break; } else { // Handle invalid input. logger.info("Invalid input. Please try again."); } } } }

Define a class that wraps KMS actions.

public class KMSActions { private static final Logger logger = LoggerFactory.getLogger(KMSActions.class); private static KmsAsyncClient kmsAsyncClient; /** * Retrieves an asynchronous AWS Key Management Service (KMS) client. * <p> * This method creates and returns a singleton instance of the KMS async client, with the following configurations: * <ul> * <li>Max concurrency: 100</li> * <li>Connection timeout: 60 seconds</li> * <li>Read timeout: 60 seconds</li> * <li>Write timeout: 60 seconds</li> * <li>API call timeout: 2 minutes</li> * <li>API call attempt timeout: 90 seconds</li> * <li>Retry policy: up to 3 retries</li> * <li>Credentials provider: environment variable credentials provider</li> * </ul> * <p> * If the client instance has already been created, it is returned instead of creating a new one. * * @return the KMS async client instance */ private static KmsAsyncClient getAsyncClient() { if (kmsAsyncClient == null) { SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() .maxConcurrency(100) .connectionTimeout(Duration.ofSeconds(60)) .readTimeout(Duration.ofSeconds(60)) .writeTimeout(Duration.ofSeconds(60)) .build(); ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() .apiCallTimeout(Duration.ofMinutes(2)) .apiCallAttemptTimeout(Duration.ofSeconds(90)) .retryPolicy(RetryPolicy.builder() .numRetries(3) .build()) .build(); kmsAsyncClient = KmsAsyncClient.builder() .httpClient(httpClient) .overrideConfiguration(overrideConfig) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) .build(); } return kmsAsyncClient; } /** * Creates a new symmetric encryption key asynchronously. * * @param keyDesc the description of the key to be created * @return a {@link CompletableFuture} that completes with the ID of the newly created key * @throws RuntimeException if an error occurs while creating the key */ public CompletableFuture<String> createKeyAsync(String keyDesc) { CreateKeyRequest keyRequest = CreateKeyRequest.builder() .description(keyDesc) .keySpec(KeySpec.SYMMETRIC_DEFAULT) .keyUsage(KeyUsageType.ENCRYPT_DECRYPT) .build(); return getAsyncClient().createKey(keyRequest) .thenApply(resp -> resp.keyMetadata().keyId()) .exceptionally(ex -> { throw new RuntimeException("An error occurred while creating the key: " + ex.getMessage(), ex); }); } /** * Asynchronously checks if a specified key is enabled. * * @param keyId the ID of the key to check * @return a {@link CompletableFuture} that, when completed, indicates whether the key is enabled or not * * @throws RuntimeException if an exception occurs while checking the key state */ public CompletableFuture<Boolean> isKeyEnabledAsync(String keyId) { DescribeKeyRequest keyRequest = DescribeKeyRequest.builder() .keyId(keyId) .build(); CompletableFuture<DescribeKeyResponse> responseFuture = getAsyncClient().describeKey(keyRequest); return responseFuture.whenComplete((resp, ex) -> { if (resp != null) { KeyState keyState = resp.keyMetadata().keyState(); if (keyState == KeyState.ENABLED) { logger.info("The key is enabled."); } else { logger.info("The key is not enabled. Key state: {}", keyState); } } else { throw new RuntimeException(ex); } }).thenApply(resp -> resp.keyMetadata().keyState() == KeyState.ENABLED); } /** * Asynchronously enables the specified key. * * @param keyId the ID of the key to enable * @return a {@link CompletableFuture} that completes when the key has been enabled */ public CompletableFuture<Void> enableKeyAsync(String keyId) { EnableKeyRequest enableKeyRequest = EnableKeyRequest.builder() .keyId(keyId) .build(); CompletableFuture<EnableKeyResponse> responseFuture = getAsyncClient().enableKey(enableKeyRequest); responseFuture.whenComplete((response, exception) -> { if (exception == null) { logger.info("Key with ID [{}] has been enabled.", keyId); } else { if (exception instanceof KmsException kmsEx) { throw new RuntimeException("KMS error occurred while enabling key: " + kmsEx.getMessage(), kmsEx); } else { throw new RuntimeException("An unexpected error occurred while enabling key: " + exception.getMessage(), exception); } } }); return responseFuture.thenApply(response -> null); } /** * Encrypts the given text asynchronously using the specified KMS client and key ID. * * @param keyId the ID of the KMS key to use for encryption * @param text the text to encrypt * @return a CompletableFuture that completes with the encrypted data as an SdkBytes object */ public CompletableFuture<SdkBytes> encryptDataAsync(String keyId, String text) { SdkBytes myBytes = SdkBytes.fromUtf8String(text); EncryptRequest encryptRequest = EncryptRequest.builder() .keyId(keyId) .plaintext(myBytes) .build(); CompletableFuture<EncryptResponse> responseFuture = getAsyncClient().encrypt(encryptRequest).toCompletableFuture(); return responseFuture.whenComplete((response, ex) -> { if (response != null) { String algorithm = response.encryptionAlgorithm().toString(); logger.info("The string was encrypted with algorithm {}.", algorithm); } else { throw new RuntimeException(ex); } }).thenApply(EncryptResponse::ciphertextBlob); } /** * Creates a custom alias for the specified target key asynchronously. * * @param targetKeyId the ID of the target key for the alias * @param aliasName the name of the alias to create * @return a {@link CompletableFuture} that completes when the alias creation operation is finished */ public CompletableFuture<Void> createCustomAliasAsync(String targetKeyId, String aliasName) { CreateAliasRequest aliasRequest = CreateAliasRequest.builder() .aliasName(aliasName) .targetKeyId(targetKeyId) .build(); CompletableFuture<CreateAliasResponse> responseFuture = getAsyncClient().createAlias(aliasRequest); responseFuture.whenComplete((response, exception) -> { if (exception == null) { logger.info("{} was successfully created.", aliasName); } else { if (exception instanceof ResourceExistsException) { logger.info("Alias [{}] already exists. Moving on...", aliasName); } else if (exception instanceof KmsException kmsEx) { throw new RuntimeException("KMS error occurred while creating alias: " + kmsEx.getMessage(), kmsEx); } else { throw new RuntimeException("An unexpected error occurred while creating alias: " + exception.getMessage(), exception); } } }); return responseFuture.thenApply(response -> null); } /** * Asynchronously lists all the aliases in the current AWS account. * * @return a {@link CompletableFuture} that completes when the list of aliases has been processed */ public CompletableFuture<Object> listAllAliasesAsync() { ListAliasesRequest aliasesRequest = ListAliasesRequest.builder() .limit(15) .build(); ListAliasesPublisher paginator = getAsyncClient().listAliasesPaginator(aliasesRequest); return paginator.subscribe(response -> { response.aliases().forEach(alias -> logger.info("The alias name is: " + alias.aliasName()) ); }) .thenApply(v -> null) .exceptionally(ex -> { if (ex.getCause() instanceof KmsException) { KmsException e = (KmsException) ex.getCause(); throw new RuntimeException("A KMS exception occurred: " + e.getMessage()); } else { throw new RuntimeException("An unexpected error occurred: " + ex.getMessage()); } }); } /** * Enables key rotation asynchronously for the specified key ID. * * @param keyId the ID of the key for which to enable key rotation * @return a CompletableFuture that represents the asynchronous operation of enabling key rotation * @throws RuntimeException if there was an error enabling key rotation, either due to a KMS exception or an unexpected error */ public CompletableFuture<EnableKeyRotationResponse> enableKeyRotationAsync(String keyId) { EnableKeyRotationRequest enableKeyRotationRequest = EnableKeyRotationRequest.builder() .keyId(keyId) .build(); CompletableFuture<EnableKeyRotationResponse> responseFuture = getAsyncClient().enableKeyRotation(enableKeyRotationRequest); responseFuture.whenComplete((response, exception) -> { if (exception == null) { logger.info("Key rotation has been enabled for key with id [{}]", keyId); } else { if (exception instanceof KmsException kmsEx) { throw new RuntimeException("Failed to enable key rotation: " + kmsEx.getMessage(), kmsEx); } else { throw new RuntimeException("An unexpected error occurred: " + exception.getMessage(), exception); } } }); return responseFuture; } /** * Grants permissions to a specified principal on a customer master key (CMK) asynchronously. * * @param keyId The unique identifier for the customer master key (CMK) that the grant applies to. * @param granteePrincipal The principal that is given permission to perform the operations that the grant permits on the CMK. * @return A {@link CompletableFuture} that, when completed, contains the ID of the created grant. * @throws RuntimeException If an error occurs during the grant creation process. */ public CompletableFuture<String> grantKeyAsync(String keyId, String granteePrincipal) { List<GrantOperation> grantPermissions = List.of( GrantOperation.ENCRYPT, GrantOperation.DECRYPT, GrantOperation.DESCRIBE_KEY ); CreateGrantRequest grantRequest = CreateGrantRequest.builder() .keyId(keyId) .name("grant1") .granteePrincipal(granteePrincipal) .operations(grantPermissions) .build(); CompletableFuture<CreateGrantResponse> responseFuture = getAsyncClient().createGrant(grantRequest); responseFuture.whenComplete((response, ex) -> { if (ex == null) { logger.info("Grant created successfully with ID: " + response.grantId()); } else { if (ex instanceof KmsException kmsEx) { throw new RuntimeException("Failed to create grant: " + kmsEx.getMessage(), kmsEx); } else { throw new RuntimeException("An unexpected error occurred: " + ex.getMessage(), ex); } } }); return responseFuture.thenApply(CreateGrantResponse::grantId); } /** * Asynchronously displays the grant IDs for the specified key ID. * * @param keyId the ID of the AWS KMS key for which to list the grants * @return a {@link CompletableFuture} that, when completed, will be null if the operation succeeded, or will throw a {@link RuntimeException} if the operation failed * @throws RuntimeException if there was an error listing the grants, either due to an {@link KmsException} or an unexpected error */ public CompletableFuture<Object> displayGrantIdsAsync(String keyId) { ListGrantsRequest grantsRequest = ListGrantsRequest.builder() .keyId(keyId) .limit(15) .build(); ListGrantsPublisher paginator = getAsyncClient().listGrantsPaginator(grantsRequest); return paginator.subscribe(response -> { response.grants().forEach(grant -> { logger.info("The grant Id is: " + grant.grantId()); }); }) .thenApply(v -> null) .exceptionally(ex -> { Throwable cause = ex.getCause(); if (cause instanceof KmsException) { throw new RuntimeException("Failed to list grants: " + cause.getMessage(), cause); } else { throw new RuntimeException("An unexpected error occurred: " + cause.getMessage(), cause); } }); } /** * Revokes a grant for the specified AWS KMS key asynchronously. * * @param keyId The ID or key ARN of the AWS KMS key. * @param grantId The identifier of the grant to be revoked. * @return A {@link CompletableFuture} representing the asynchronous operation of revoking the grant. * The {@link CompletableFuture} will complete with a {@link RevokeGrantResponse} object * if the operation is successful, or with a {@code null} value if an error occurs. */ public CompletableFuture<RevokeGrantResponse> revokeKeyGrantAsync(String keyId, String grantId) { RevokeGrantRequest grantRequest = RevokeGrantRequest.builder() .keyId(keyId) .grantId(grantId) .build(); CompletableFuture<RevokeGrantResponse> responseFuture = getAsyncClient().revokeGrant(grantRequest); responseFuture.whenComplete((response, exception) -> { if (exception == null) { logger.info("Grant ID: [" + grantId + "] was successfully revoked!"); } else { if (exception instanceof KmsException kmsEx) { if (kmsEx.getMessage().contains("Grant does not exist")) { logger.info("The grant ID '" + grantId + "' does not exist. Moving on..."); } else { throw new RuntimeException("KMS error occurred: " + kmsEx.getMessage(), kmsEx); } } else { throw new RuntimeException("An unexpected error occurred: " + exception.getMessage(), exception); } } }); return responseFuture; } /** * Asynchronously decrypts the given encrypted data using the specified key ID. * * @param encryptedData The encrypted data to be decrypted. * @param keyId The ID of the key to be used for decryption. * @return A CompletableFuture that, when completed, will contain the decrypted data as a String. * If an error occurs during the decryption process, the CompletableFuture will complete * exceptionally with the error, and the method will return an empty String. */ public CompletableFuture<String> decryptDataAsync(SdkBytes encryptedData, String keyId) { DecryptRequest decryptRequest = DecryptRequest.builder() .ciphertextBlob(encryptedData) .keyId(keyId) .build(); CompletableFuture<DecryptResponse> responseFuture = getAsyncClient().decrypt(decryptRequest); responseFuture.whenComplete((decryptResponse, exception) -> { if (exception == null) { logger.info("Data decrypted successfully for key ID: " + keyId); } else { if (exception instanceof KmsException kmsEx) { throw new RuntimeException("KMS error occurred while decrypting data: " + kmsEx.getMessage(), kmsEx); } else { throw new RuntimeException("An unexpected error occurred while decrypting data: " + exception.getMessage(), exception); } } }); return responseFuture.thenApply(decryptResponse -> decryptResponse.plaintext().asString(StandardCharsets.UTF_8)); } /** * Asynchronously replaces the policy for the specified KMS key. * * @param keyId the ID of the KMS key to replace the policy for * @param policyName the name of the policy to be replaced * @param accountId the AWS account ID to be used in the policy * @return a {@link CompletableFuture} that completes with a boolean indicating * whether the policy replacement was successful or not */ public CompletableFuture<Boolean> replacePolicyAsync(String keyId, String policyName, String accountId) { String policy = """ { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::%s:root"}, "Action": "kms:*", "Resource": "*" }] } """.formatted(accountId); PutKeyPolicyRequest keyPolicyRequest = PutKeyPolicyRequest.builder() .keyId(keyId) .policyName(policyName) .policy(policy) .build(); // First, get the current policy to check if it exists return getAsyncClient().getKeyPolicy(r -> r.keyId(keyId).policyName(policyName)) .thenCompose(response -> { logger.info("Current policy exists. Replacing it..."); return getAsyncClient().putKeyPolicy(keyPolicyRequest); }) .thenApply(putPolicyResponse -> { logger.info("The key policy has been replaced."); return true; }) .exceptionally(throwable -> { if (throwable.getCause() instanceof LimitExceededException) { logger.error("Cannot replace policy, as only one policy is allowed per key."); return false; } throw new RuntimeException("Error replacing policy", throwable); }); } /** * Asynchronously retrieves the key policy for the specified key ID and policy name. * * @param keyId the ID of the AWS KMS key for which to retrieve the policy * @param policyName the name of the key policy to retrieve * @return a {@link CompletableFuture} that, when completed, contains the key policy as a {@link String} */ public CompletableFuture<String> getKeyPolicyAsync(String keyId, String policyName) { GetKeyPolicyRequest policyRequest = GetKeyPolicyRequest.builder() .keyId(keyId) .policyName(policyName) .build(); return getAsyncClient().getKeyPolicy(policyRequest) .thenApply(response -> { String policy = response.policy(); logger.info("The response is: " + policy); return policy; }) .exceptionally(ex -> { throw new RuntimeException("Failed to get key policy", ex); }); } /** * Asynchronously signs and verifies data using AWS KMS. * * <p>The method performs the following steps: * <ol> * <li>Creates an AWS KMS key with the specified key spec, key usage, and origin.</li> * <li>Signs the provided message using the created KMS key and the RSASSA-PSS-SHA-256 algorithm.</li> * <li>Verifies the signature of the message using the created KMS key and the RSASSA-PSS-SHA-256 algorithm.</li> * </ol> * * @return a {@link CompletableFuture} that completes with the result of the signature verification, * {@code true} if the signature is valid, {@code false} otherwise. * @throws KmsException if any error occurs during the KMS operations. * @throws RuntimeException if an unexpected error occurs. */ public CompletableFuture<Boolean> signVerifyDataAsync() { String signMessage = "Here is the message that will be digitally signed"; // Create an AWS KMS key used to digitally sign data. CreateKeyRequest createKeyRequest = CreateKeyRequest.builder() .keySpec(KeySpec.RSA_2048) .keyUsage(KeyUsageType.SIGN_VERIFY) .origin(OriginType.AWS_KMS) .build(); return getAsyncClient().createKey(createKeyRequest) .thenCompose(createKeyResponse -> { String keyId = createKeyResponse.keyMetadata().keyId(); SdkBytes messageBytes = SdkBytes.fromString(signMessage, Charset.defaultCharset()); SignRequest signRequest = SignRequest.builder() .keyId(keyId) .message(messageBytes) .signingAlgorithm(SigningAlgorithmSpec.RSASSA_PSS_SHA_256) .build(); return getAsyncClient().sign(signRequest) .thenCompose(signResponse -> { byte[] signedBytes = signResponse.signature().asByteArray(); VerifyRequest verifyRequest = VerifyRequest.builder() .keyId(keyId) .message(SdkBytes.fromByteArray(signMessage.getBytes(Charset.defaultCharset()))) .signature(SdkBytes.fromByteBuffer(ByteBuffer.wrap(signedBytes))) .signingAlgorithm(SigningAlgorithmSpec.RSASSA_PSS_SHA_256) .build(); return getAsyncClient().verify(verifyRequest) .thenApply(verifyResponse -> { return (boolean) verifyResponse.signatureValid(); }); }); }) .exceptionally(throwable -> { throw new RuntimeException("Failed to sign or verify data", throwable); }); } /** * Asynchronously tags a KMS key with a specific tag. * * @param keyId the ID of the KMS key to be tagged * @return a {@link CompletableFuture} that completes when the tagging operation is finished */ public CompletableFuture<Void> tagKMSKeyAsync(String keyId) { Tag tag = Tag.builder() .tagKey("Environment") .tagValue("Production") .build(); TagResourceRequest tagResourceRequest = TagResourceRequest.builder() .keyId(keyId) .tags(tag) .build(); return getAsyncClient().tagResource(tagResourceRequest) .thenRun(() -> { logger.info("{} key was tagged", keyId); }) .exceptionally(throwable -> { throw new RuntimeException("Failed to tag the KMS key", throwable); }); } /** * Deletes a specific KMS alias asynchronously. * * @param aliasName the name of the alias to be deleted * @return a {@link CompletableFuture} representing the asynchronous operation of deleting the specified alias */ public CompletableFuture<Void> deleteSpecificAliasAsync(String aliasName) { DeleteAliasRequest deleteAliasRequest = DeleteAliasRequest.builder() .aliasName(aliasName) .build(); return getAsyncClient().deleteAlias(deleteAliasRequest) .thenRun(() -> { logger.info("Alias {} has been deleted successfully", aliasName); }) .exceptionally(throwable -> { throw new RuntimeException("Failed to delete alias: " + aliasName, throwable); }); } /** * Asynchronously disables the specified AWS Key Management Service (KMS) key. * * @param keyId the ID or Amazon Resource Name (ARN) of the KMS key to be disabled * @return a CompletableFuture that, when completed, indicates that the key has been disabled successfully */ public CompletableFuture<Void> disableKeyAsync(String keyId) { DisableKeyRequest keyRequest = DisableKeyRequest.builder() .keyId(keyId) .build(); return getAsyncClient().disableKey(keyRequest) .thenRun(() -> { logger.info("Key {} has been disabled successfully",keyId); }) .exceptionally(throwable -> { throw new RuntimeException("Failed to disable key: " + keyId, throwable); }); } /** * Deletes a KMS key asynchronously. * * <p><strong>Warning:</strong> 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 becomes unrecoverable. * This means that any files, databases, or other data that were encrypted using the deleted KMS key * will become permanently inaccessible. Exercise extreme caution when deleting KMS keys.</p> * * @param keyId the ID of the KMS key to delete * @return a {@link CompletableFuture} that completes when the key deletion is scheduled */ public CompletableFuture<Void> deleteKeyAsync(String keyId) { ScheduleKeyDeletionRequest deletionRequest = ScheduleKeyDeletionRequest.builder() .keyId(keyId) .pendingWindowInDays(7) .build(); return getAsyncClient().scheduleKeyDeletion(deletionRequest) .thenRun(() -> { logger.info("Key {} will be deleted in 7 days", keyId); }) .exceptionally(throwable -> { throw new RuntimeException("Failed to schedule key deletion for key ID: " + keyId, throwable); }); } public String getAccountId(){ try (StsClient stsClient = StsClient.create()){ GetCallerIdentityResponse callerIdentity = stsClient.getCallerIdentity(); return callerIdentity.account(); } } }
PHP
SDK for PHP
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

echo "\n"; echo "--------------------------------------\n"; echo <<<WELCOME Welcome to the AWS Key Management Service SDK Basics scenario. This program demonstrates how to interact with AWS Key Management Service using the AWS SDK for PHP (v3). 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 KMS 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...\n WELCOME; echo "--------------------------------------\n"; $this->pressEnter(); $this->kmsClient = new KmsClient([]); // Initialize the KmsService class with the client. This allows you to override any defaults in the client before giving it to the service class. $this->kmsService = new KmsService($this->kmsClient); // 1. Create a symmetric KMS key. echo "\n"; echo "1. Create a symmetric KMS key.\n"; echo "First, we will create a symmetric KMS key that is used to encrypt and decrypt data by invoking createKey().\n"; $this->pressEnter(); $key = $this->kmsService->createKey(); $this->resources['symmetricKey'] = $key['KeyId']; echo "Created a customer key with ARN {$key['Arn']}.\n"; $this->pressEnter(); // 2. Enable a KMS key. echo "\n"; echo "2. Enable a KMS key.\n"; echo "By default when you create an AWS key, it is enabled. The code checks to determine if the key is enabled. If it is not enabled, the code enables it.\n"; $this->pressEnter(); $keyInfo = $this->kmsService->describeKey($key['KeyId']); if(!$keyInfo['Enabled']){ echo "The key was not enabled, so we will enable it.\n"; $this->pressEnter(); $this->kmsService->enableKey($key['KeyId']); echo "The key was successfully enabled.\n"; }else{ echo "The key was already enabled, so there was no need to enable it.\n"; } $this->pressEnter(); // 3. Encrypt data using the symmetric KMS key. echo "\n"; echo "3. Encrypt data using the symmetric KMS key.\n"; echo "One of the main uses of symmetric keys is to encrypt and decrypt data.\n"; echo "Next, we'll encrypt the string 'Hello, AWS KMS!' with the SYMMETRIC_DEFAULT encryption algorithm.\n"; $this->pressEnter(); $text = "Hello, AWS KMS!"; $encryption = $this->kmsService->encrypt($key['KeyId'], $text); echo "The plaintext data was successfully encrypted with the algorithm: {$encryption['EncryptionAlgorithm']}.\n"; $this->pressEnter(); // 4. Create an alias. echo "\n"; echo "4. Create an alias.\n"; $aliasInput = testable_readline("Please enter an alias prefixed with \"alias/\" or press enter to use a default value: "); if($aliasInput == ""){ $aliasInput = "alias/dev-encryption-key"; } $this->kmsService->createAlias($key['KeyId'], $aliasInput); $this->resources['alias'] = $aliasInput; echo "The alias \"$aliasInput\" was successfully created.\n"; $this->pressEnter(); // 5. List all of your aliases. $aliasPageSize = 10; echo "\n"; echo "5. List all of your aliases, up to $aliasPageSize.\n"; $this->pressEnter(); $aliasPaginator = $this->kmsService->listAliases(); foreach($aliasPaginator as $pages){ foreach($pages['Aliases'] as $alias){ echo $alias['AliasName'] . "\n"; } break; } $this->pressEnter(); // 6. Enable automatic rotation of the KMS key. echo "\n"; echo "6. Enable automatic rotation of the KMS key.\n"; echo "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."; $this->pressEnter(); $this->kmsService->enableKeyRotation($key['KeyId']); echo "The key's rotation was successfully set for key: {$key['KeyId']}\n"; $this->pressEnter(); // 7. Create a grant. echo "7. Create a grant.\n"; echo "\n"; echo "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.\n"; $granteeARN = testable_readline("Please enter the Amazon Resource Name (ARN) of an Amazon Web Services principal. Valid principals include Amazon Web Services accounts, IAM users, IAM roles, federated users, and assumed role users. For help with the ARN syntax for a principal, see IAM ARNs in the Identity and Access Management User Guide. \nTo skip this step, press enter without any other values: "); if($granteeARN){ $operations = [ "ENCRYPT", "DECRYPT", "DESCRIBE_KEY", ]; $grant = $this->kmsService->createGrant($key['KeyId'], $granteeARN, $operations); echo "The grant Id is: {$grant['GrantId']}\n"; }else{ echo "Steps 7, 8, and 9 will be skipped.\n"; } $this->pressEnter(); // 8. List grants for the KMS key. if($granteeARN){ echo "8. List grants for the KMS key.\n\n"; $grantsPaginator = $this->kmsService->listGrants($key['KeyId']); foreach($grantsPaginator as $page){ foreach($page['Grants'] as $grant){ echo $grant['GrantId'] . "\n"; } } }else{ echo "Skipping step 8...\n"; } $this->pressEnter(); // 9. Revoke the grant. if($granteeARN) { echo "\n"; echo "9. Revoke the grant.\n"; $this->pressEnter(); $this->kmsService->revokeGrant($grant['GrantId'], $keyInfo['KeyId']); echo "{$grant['GrantId']} was successfully revoked!\n"; }else{ echo "Skipping step 9...\n"; } $this->pressEnter(); // 10. Decrypt the data. echo "\n"; echo "10. Decrypt the data.\n"; echo "Let's decrypt the data that was encrypted before.\n"; echo "We'll use the same key to decrypt the string that we encrypted earlier in the program.\n"; $this->pressEnter(); $decryption = $this->kmsService->decrypt($keyInfo['KeyId'], $encryption['CiphertextBlob'], $encryption['EncryptionAlgorithm']); echo "The decrypted text is: {$decryption['Plaintext']}\n"; $this->pressEnter(); // 11. Replace a Key Policy. echo "\n"; echo "11. Replace a Key Policy.\n"; echo "A key policy is a resource policy for a KMS key. Key policies are the primary way to control access to KMS keys.\n"; echo "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.\n"; echo " You can also use IAM policies and grants to control access to the KMS key, but every KMS key must have a key policy.\n"; echo "We will replace the key's policy with a new one:\n"; $stsClient = new StsClient([]); $result = $stsClient->getCallerIdentity(); $accountId = $result['Account']; $keyPolicy = <<< KEYPOLICY { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::$accountId:root"}, "Action": "kms:*", "Resource": "*" }] } KEYPOLICY; echo $keyPolicy; $this->pressEnter(); $this->kmsService->putKeyPolicy($keyInfo['KeyId'], $keyPolicy); echo "The Key Policy was successfully replaced!\n"; $this->pressEnter(); // 12. Retrieve the key policy. echo "\n"; echo "12. Retrieve the key policy.\n"; echo "Let's get some information about the new policy and print it to the screen.\n"; $this->pressEnter(); $policyInfo = $this->kmsService->getKeyPolicy($keyInfo['KeyId']); echo "We got the info! Here is the policy: \n"; echo $policyInfo['Policy'] . "\n"; $this->pressEnter(); // 13. Create an asymmetric KMS key and sign data. echo "\n"; echo "13. Create an asymmetric KMS key and sign data.\n"; echo "Signing your data with an AWS key can provide several benefits that make it an attractive option for your data signing needs.\n"; echo "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.\n"; echo "First we'll create the asymmetric key.\n"; $this->pressEnter(); $keySpec = "RSA_2048"; $keyUsage = "SIGN_VERIFY"; $asymmetricKey = $this->kmsService->createKey($keySpec, $keyUsage); $this->resources['asymmetricKey'] = $asymmetricKey['KeyId']; echo "Created the key with ID: {$asymmetricKey['KeyId']}\n"; echo "Next, we'll sign the data.\n"; $this->pressEnter(); $algorithm = "RSASSA_PSS_SHA_256"; $sign = $this->kmsService->sign($asymmetricKey['KeyId'], $text, $algorithm); $verify = $this->kmsService->verify($asymmetricKey['KeyId'], $text, $sign['Signature'], $algorithm); echo "Signature verification result: {$sign['signature']}\n"; $this->pressEnter(); // 14. Tag the symmetric KMS key. echo "\n"; echo "14. Tag the symmetric KMS key.\n"; echo "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.\n"; echo "Let's tag our symmetric key as Environment->Production\n"; $this->pressEnter(); $this->kmsService->tagResource($key['KeyId'], [ [ 'TagKey' => "Environment", 'TagValue' => "Production", ], ]); echo "The key was successfully tagged!\n"; $this->pressEnter(); // 15. Schedule the deletion of the KMS key echo "\n"; echo "15. Schedule the deletion of the KMS key.\n"; echo "By default, KMS applies a waiting period of 30 days, but you can specify a waiting period of 7-30 days.\n"; echo "When this operation is successful, the key state of the KMS key changes to PendingDeletion and the key can't be used in any cryptographic operations.\n"; echo "It remains in this state for the duration of the waiting period.\n\n"; echo "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.\n\n"; $cleanUp = testable_readline("Would you like to delete the resources created during this scenario, including the keys? (y/n): "); if($cleanUp == "Y" || $cleanUp == "y"){ $this->cleanUp(); } echo "--------------------------------------------------------------------------------\n"; echo "This concludes the AWS Key Management SDK Basics scenario\n"; echo "--------------------------------------------------------------------------------\n"; namespace Kms; use Aws\Kms\Exception\KmsException; use Aws\Kms\KmsClient; use Aws\Result; use Aws\ResultPaginator; use AwsUtilities\AWSServiceClass; class KmsService extends AWSServiceClass { protected KmsClient $client; protected bool $verbose; /*** * @param KmsClient|null $client * @param bool $verbose */ public function __construct(KmsClient $client = null, bool $verbose = false) { $this->verbose = $verbose; if($client){ $this->client = $client; return; } $this->client = new KmsClient([]); } /*** * @param string $keySpec * @param string $keyUsage * @param string $description * @return array */ public function createKey(string $keySpec = "", string $keyUsage = "", string $description = "Created by the SDK for PHP") { $parameters = ['Description' => $description]; if($keySpec && $keyUsage){ $parameters['KeySpec'] = $keySpec; $parameters['KeyUsage'] = $keyUsage; } try { $result = $this->client->createKey($parameters); return $result['KeyMetadata']; }catch(KmsException $caught){ // Check for error specific to createKey operations if ($caught->getAwsErrorMessage() == "LimitExceededException"){ echo "The request was rejected because a quota was exceeded. For more information, see Quotas in the Key Management Service Developer Guide."; } throw $caught; } } /*** * @param string $keyId * @param string $ciphertext * @param string $algorithm * @return Result */ public function decrypt(string $keyId, string $ciphertext, string $algorithm = "SYMMETRIC_DEFAULT") { try{ return $this->client->decrypt([ 'CiphertextBlob' => $ciphertext, 'EncryptionAlgorithm' => $algorithm, 'KeyId' => $keyId, ]); }catch(KmsException $caught){ echo "There was a problem decrypting the data: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @param string $text * @return Result */ public function encrypt(string $keyId, string $text) { try { return $this->client->encrypt([ 'KeyId' => $keyId, 'Plaintext' => $text, ]); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "DisabledException"){ echo "The request was rejected because the specified KMS key is not enabled.\n"; } throw $caught; } } /*** * @param string $keyId * @param int $limit * @return ResultPaginator */ public function listAliases(string $keyId = "", int $limit = 0) { $args = []; if($keyId){ $args['KeyId'] = $keyId; } if($limit){ $args['Limit'] = $limit; } try{ return $this->client->getPaginator("ListAliases", $args); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "InvalidMarkerException"){ echo "The request was rejected because the marker that specifies where pagination should next begin is not valid.\n"; } throw $caught; } } /*** * @param string $keyId * @param string $alias * @return void */ public function createAlias(string $keyId, string $alias) { try{ $this->client->createAlias([ 'TargetKeyId' => $keyId, 'AliasName' => $alias, ]); }catch (KmsException $caught){ if($caught->getAwsErrorMessage() == "InvalidAliasNameException"){ echo "The request was rejected because the specified alias name is not valid."; } throw $caught; } } /*** * @param string $keyId * @param string $granteePrincipal * @param array $operations * @param array $grantTokens * @return Result */ public function createGrant(string $keyId, string $granteePrincipal, array $operations, array $grantTokens = []) { $args = [ 'KeyId' => $keyId, 'GranteePrincipal' => $granteePrincipal, 'Operations' => $operations, ]; if($grantTokens){ $args['GrantTokens'] = $grantTokens; } try{ return $this->client->createGrant($args); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "InvalidGrantTokenException"){ echo "The request was rejected because the specified grant token is not valid.\n"; } throw $caught; } } /*** * @param string $keyId * @return array */ public function describeKey(string $keyId) { try { $result = $this->client->describeKey([ "KeyId" => $keyId, ]); return $result['KeyMetadata']; }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "NotFoundException"){ echo "The request was rejected because the specified entity or resource could not be found.\n"; } throw $caught; } } /*** * @param string $keyId * @return void */ public function disableKey(string $keyId) { try { $this->client->disableKey([ 'KeyId' => $keyId, ]); }catch(KmsException $caught){ echo "There was a problem disabling the key: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @return void */ public function enableKey(string $keyId) { try { $this->client->enableKey([ 'KeyId' => $keyId, ]); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "NotFoundException"){ echo "The request was rejected because the specified entity or resource could not be found.\n"; } throw $caught; } } /*** * @return array */ public function listKeys() { try { $contents = []; $paginator = $this->client->getPaginator("ListKeys"); foreach($paginator as $result){ foreach ($result['Content'] as $object) { $contents[] = $object; } } return $contents; }catch(KmsException $caught){ echo "There was a problem listing the keys: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @return Result */ public function listGrants(string $keyId) { try{ return $this->client->listGrants([ 'KeyId' => $keyId, ]); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "NotFoundException"){ echo " The request was rejected because the specified entity or resource could not be found.\n"; } throw $caught; } } /*** * @param string $keyId * @return Result */ public function getKeyPolicy(string $keyId) { try { return $this->client->getKeyPolicy([ 'KeyId' => $keyId, ]); }catch(KmsException $caught){ echo "There was a problem getting the key policy: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $grantId * @param string $keyId * @return void */ public function revokeGrant(string $grantId, string $keyId) { try{ $this->client->revokeGrant([ 'GrantId' => $grantId, 'KeyId' => $keyId, ]); }catch(KmsException $caught){ echo "There was a problem with revoking the grant: {$caught->getAwsErrorMessage()}.\n"; throw $caught; } } /*** * @param string $keyId * @param int $pendingWindowInDays * @return void */ public function scheduleKeyDeletion(string $keyId, int $pendingWindowInDays = 7) { try { $this->client->scheduleKeyDeletion([ 'KeyId' => $keyId, 'PendingWindowInDays' => $pendingWindowInDays, ]); }catch(KmsException $caught){ echo "There was a problem scheduling the key deletion: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @param array $tags * @return void */ public function tagResource(string $keyId, array $tags) { try { $this->client->tagResource([ 'KeyId' => $keyId, 'Tags' => $tags, ]); }catch(KmsException $caught){ echo "There was a problem applying the tag(s): {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @param string $message * @param string $algorithm * @return Result */ public function sign(string $keyId, string $message, string $algorithm) { try { return $this->client->sign([ 'KeyId' => $keyId, 'Message' => $message, 'SigningAlgorithm' => $algorithm, ]); }catch(KmsException $caught){ echo "There was a problem signing the data: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @param int $rotationPeriodInDays * @return void */ public function enableKeyRotation(string $keyId, int $rotationPeriodInDays = 365) { try{ $this->client->enableKeyRotation([ 'KeyId' => $keyId, 'RotationPeriodInDays' => $rotationPeriodInDays, ]); }catch(KmsException $caught){ if($caught->getAwsErrorMessage() == "NotFoundException"){ echo "The request was rejected because the specified entity or resource could not be found.\n"; } throw $caught; } } /*** * @param string $keyId * @param string $policy * @return void */ public function putKeyPolicy(string $keyId, string $policy) { try { $this->client->putKeyPolicy([ 'KeyId' => $keyId, 'Policy' => $policy, ]); }catch(KmsException $caught){ echo "There was a problem replacing the key policy: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $aliasName * @return void */ public function deleteAlias(string $aliasName) { try { $this->client->deleteAlias([ 'AliasName' => $aliasName, ]); }catch(KmsException $caught){ echo "There was a problem deleting the alias: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } /*** * @param string $keyId * @param string $message * @param string $signature * @param string $signingAlgorithm * @return bool */ public function verify(string $keyId, string $message, string $signature, string $signingAlgorithm) { try { $result = $this->client->verify([ 'KeyId' => $keyId, 'Message' => $message, 'Signature' => $signature, 'SigningAlgorithm' => $signingAlgorithm, ]); return $result['SignatureValid']; }catch(KmsException $caught){ echo "There was a problem verifying the signature: {$caught->getAwsErrorMessage()}\n"; throw $caught; } } }
Python
SDK for Python (Boto3)
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

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()

Wrapper class and methods for KMS key management.

class KeyManager: def __init__(self, kms_client): self.kms_client = kms_client self.created_keys = [] @classmethod def from_client(cls) -> "KeyManager": """ Creates a KeyManager instance with a default KMS client. :return: An instance of KeyManager initialized with the default KMS client. """ kms_client = boto3.client("kms") return cls(kms_client) def create_key(self, key_description: str) -> dict[str, any]: """ Creates a key with a user-provided description. :param key_description: A description for the key. :return: The key ID. """ try: key = self.kms_client.create_key(Description=key_description)["KeyMetadata"] self.created_keys.append(key) return key except ClientError as err: logging.error( "Couldn't create your key. Here's why: %s", err.response["Error"]["Message"], ) raise def describe_key(self, key_id: str) -> dict[str, any]: """ Describes a key. :param key_id: The ARN or ID of the key to describe. :return: Information about the key. """ try: key = self.kms_client.describe_key(KeyId=key_id)["KeyMetadata"] return key except ClientError as err: logging.error( "Couldn't get key '%s'. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise def enable_key_rotation(self, key_id: str) -> None: """ Enables rotation for a key. :param key_id: The ARN or ID of the key to enable rotation for. """ try: self.kms_client.enable_key_rotation(KeyId=key_id) except ClientError as err: logging.error( "Couldn't enable rotation for key '%s'. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise def create_asymmetric_key(self) -> str: """ Creates an asymmetric key in AWS KMS for signing messages. :return: The ID of the created key. """ try: key = self.kms_client.create_key( KeySpec="RSA_2048", KeyUsage="SIGN_VERIFY", Origin="AWS_KMS" )["KeyMetadata"] self.created_keys.append(key) return key["KeyId"] except ClientError as err: logger.error( "Couldn't create your key. Here's why: %s", err.response["Error"]["Message"], ) raise def tag_resource(self, key_id: str, tag_key: str, tag_value: str) -> None: """ Add or edit tags on a customer managed key. :param key_id: The ARN or ID of the key to enable rotation for. :param tag_key: Key for the tag. :param tag_value: Value for the tag. """ try: self.kms_client.tag_resource( KeyId=key_id, Tags=[{"TagKey": tag_key, "TagValue": tag_value}] ) except ClientError as err: logging.error( "Couldn't add a tag for the key '%s'. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise def delete_key(self, key_id: str, window: int) -> None: """ Deletes a list of keys. 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. :param key_id: The ARN or ID of the key to delete. :param window: The waiting period, in days, before the KMS key is deleted. """ try: self.kms_client.schedule_key_deletion( KeyId=key_id, PendingWindowInDays=window ) except ClientError as err: logging.error( "Couldn't delete key %s. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise

Wrapper class and methods for KMS key aliases.

class AliasManager: def __init__(self, kms_client): self.kms_client = kms_client self.created_key = None @classmethod def from_client(cls) -> "AliasManager": """ Creates an AliasManager instance with a default KMS client. :return: An instance of AliasManager initialized with the default KMS client. """ kms_client = boto3.client("kms") return cls(kms_client) def create_alias(self, key_id: str, alias: str) -> None: """ Creates an alias for the specified key. :param key_id: The ARN or ID of a key to give an alias. :param alias: The alias to assign to the key. """ try: self.kms_client.create_alias(AliasName=alias, TargetKeyId=key_id) except ClientError as err: if err.response["Error"]["Code"] == "AlreadyExistsException": logger.error( "Could not create the alias %s because it already exists.", key_id ) else: logger.error( "Couldn't encrypt text. Here's why: %s", err.response["Error"]["Message"], ) raise def list_aliases(self, page_size: int) -> None: """ Lists aliases for the current account. :param page_size: The number of aliases to list per page. """ try: alias_paginator = self.kms_client.get_paginator("list_aliases") for alias_page in alias_paginator.paginate( PaginationConfig={"PageSize": page_size} ): print(f"Here are {page_size} aliases:") pprint(alias_page["Aliases"]) if alias_page["Truncated"]: answer = input( f"Do you want to see the next {page_size} aliases (y/n)? " ) if answer.lower() != "y": break else: print("That's all your aliases!") except ClientError as err: logging.error( "Couldn't list your aliases. Here's why: %s", err.response["Error"]["Message"], ) raise def delete_alias(self, alias: str) -> None: """ Deletes an alias. :param alias: The alias to delete. """ try: self.kms_client.delete_alias(AliasName=alias) except ClientError as err: logger.error( "Couldn't delete alias %s. Here's why: %s", alias, err.response["Error"]["Message"], ) raise

Wrapper class and methods for KMS key encryption.

class KeyEncrypt: def __init__(self, kms_client): self.kms_client = kms_client @classmethod def from_client(cls) -> "KeyEncrypt": """ Creates a KeyEncrypt instance with a default KMS client. :return: An instance of KeyEncrypt initialized with the default KMS client. """ kms_client = boto3.client("kms") return cls(kms_client) def encrypt(self, key_id: str, text: str) -> str: """ Encrypts text by using the specified key. :param key_id: The ARN or ID of the key to use for encryption. :param text: The text to encrypt. :return: The encrypted version of the text. """ try: response = self.kms_client.encrypt(KeyId=key_id, Plaintext=text.encode()) print( f"The string was encrypted with algorithm {response['EncryptionAlgorithm']}" ) return response["CiphertextBlob"] except ClientError as err: if err.response["Error"]["Code"] == "DisabledException": logger.error( "Could not encrypt because the key %s is disabled.", key_id ) else: logger.error( "Couldn't encrypt text. Here's why: %s", err.response["Error"]["Message"], ) raise def decrypt(self, key_id: str, cipher_text: str) -> bytes: """ Decrypts text previously encrypted with a key. :param key_id: The ARN or ID of the key used to decrypt the data. :param cipher_text: The encrypted text to decrypt. :return: The decrypted text. """ try: return self.kms_client.decrypt(KeyId=key_id, CiphertextBlob=cipher_text)[ "Plaintext" ] except ClientError as err: logger.error( "Couldn't decrypt your ciphertext. Here's why: %s", err.response["Error"]["Message"], ) raise def sign(self, key_id: str, message: str) -> str: """ Signs a message with a key. :param key_id: The ARN or ID of the key to use for signing. :param message: The message to sign. :return: The signature of the message. """ try: return self.kms_client.sign( KeyId=key_id, Message=message.encode(), SigningAlgorithm="RSASSA_PSS_SHA_256", )["Signature"] except ClientError as err: logger.error( "Couldn't sign your message. Here's why: %s", err.response["Error"]["Message"], ) raise def verify(self, key_id: str, message: str, signature: str) -> bool: """ Verifies a signature against a message. :param key_id: The ARN or ID of the key used to sign the message. :param message: The message to verify. :param signature: The signature to verify. :return: True when the signature matches the message, otherwise False. """ try: response = self.kms_client.verify( KeyId=key_id, Message=message.encode(), Signature=signature, SigningAlgorithm="RSASSA_PSS_SHA_256", ) valid = response["SignatureValid"] print(f"The signature is {'valid' if valid else 'invalid'}.") return valid except ClientError as err: if err.response["Error"]["Code"] == "SignatureDoesNotMatchException": print("The signature is not valid.") else: logger.error( "Couldn't verify your signature. Here's why: %s", err.response["Error"]["Message"], ) raise

Wrapper class and methods for KMS key grants.

class GrantManager: def __init__(self, kms_client): self.kms_client = kms_client @classmethod def from_client(cls) -> "GrantManager": """ Creates a GrantManager instance with a default KMS client. :return: An instance of GrantManager initialized with the default KMS client. """ kms_client = boto3.client("kms") return cls(kms_client) def create_grant( self, key_id: str, principal: str, operations: [str] ) -> dict[str, str]: """ Creates a grant for a key that lets a principal generate a symmetric data encryption key. :param key_id: The ARN or ID of the key. :param principal: The principal to grant permission to. :param operations: The operations to grant permission for. :return: The grant that is created. """ try: return self.kms_client.create_grant( KeyId=key_id, GranteePrincipal=principal, Operations=operations, ) except ClientError as err: logger.error( "Couldn't create a grant on key %s. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise def list_grants(self, key_id): """ Lists grants for a key. :param key_id: The ARN or ID of the key to query. :return: The grants for the key. """ try: paginator = self.kms_client.get_paginator("list_grants") grants = [] page_iterator = paginator.paginate(KeyId=key_id) for page in page_iterator: grants.extend(page["Grants"]) print(f"Grants for key {key_id}:") pprint(grants) return grants except ClientError as err: logger.error( "Couldn't list grants for key %s. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise def revoke_grant(self, key_id: str, grant_id: str) -> None: """ Revokes a grant so that it can no longer be used. :param key_id: The ARN or ID of the key associated with the grant. :param grant_id: The ID of the grant to revoke. """ try: self.kms_client.revoke_grant(KeyId=key_id, GrantId=grant_id) except ClientError as err: logger.error( "Couldn't revoke grant %s. Here's why: %s", grant_id, err.response["Error"]["Message"], ) raise

Wrapper class and methods for KMS key policies.

class KeyPolicy: def __init__(self, kms_client): self.kms_client = kms_client @classmethod def from_client(cls) -> "KeyPolicy": """ Creates a KeyPolicy instance with a default KMS client. :return: An instance of KeyPolicy initialized with the default KMS client. """ kms_client = boto3.client("kms") return cls(kms_client) def set_new_policy(self, key_id: str, policy: dict[str, any]) -> None: """ Sets the policy of a key. Setting a policy entirely overwrites the existing policy, so care is taken to add a statement to the existing list of statements rather than simply writing a new policy. :param key_id: The ARN or ID of the key to set the policy to. :param policy: A new key policy. The key policy must allow the calling principal to make a subsequent PutKeyPolicy request on the KMS key. This reduces the risk that the KMS key becomes unmanageable """ try: self.kms_client.put_key_policy(KeyId=key_id, Policy=json.dumps(policy)) except ClientError as err: logger.error( "Couldn't set policy for key %s. Here's why %s", key_id, err.response["Error"]["Message"], ) raise def get_policy(self, key_id: str) -> dict[str, str]: """ Gets the policy of a key. :param key_id: The ARN or ID of the key to query. :return: The key policy as a dict. """ if key_id != "": try: response = self.kms_client.get_key_policy( KeyId=key_id, ) policy = json.loads(response["Policy"]) except ClientError as err: logger.error( "Couldn't get policy for key %s. Here's why: %s", key_id, err.response["Error"]["Message"], ) raise else: pprint(policy) return policy else: print("Skipping get policy demo.")