skip to content
ainoya.dev

Using Custom Key Material with KMS in LocalStack

/ 4 min read

Introduction

In development projects that utilize AWS components, LocalStack is an incredibly handy tool. By running a single LocalStack container, you can emulate various AWS services, including KMS (Key Management Service), which is particularly useful.

In this post, I’ll walk you through how to set up custom key material when creating KMS keys in LocalStack. This approach simplifies key preparation and is especially beneficial when sharing encrypted test data in a team environment.

Background

When storing sensitive customer information—like API keys for services they own—encrypting this data with KMS is a common practice. However, in a team development setting, a problem arises: each team member’s KMS initializes with different key material, making it impossible to decrypt each other’s encrypted test data.

One way to address this is by importing key material using aws kms import-key-material, but this process can be a bit cumbersome. Recently, it became possible to create keys with custom key material directly using awslocal kms create-key in LocalStack. This new method streamlines the key preparation steps and improves development efficiency.

Step-by-Step Guide

Here’s how you can set up custom key material when creating a KMS key in LocalStack. I recommend fixing the keyId for easier handling in scripts.

1. Set Up Custom Key Material and Key ID

Generate a 32-byte random key material and define a fixed key ID:

CUSTOM_KEY_MATERIAL="$(openssl rand -base64 32)"
CUSTOM_ID="00000000-0000-0000-0000-000000000001"
  • CUSTOM_KEY_MATERIAL: Generates a random 32-byte key material.
  • CUSTOM_ID: Sets a fixed key ID.

2. Create the Key

Use the awslocal command to create a key with custom key material:

awslocal kms create-key --tags "[{\"TagKey\":\"_custom_key_material_\",\"TagValue\":\"$CUSTOM_KEY_MATERIAL\"},{\"TagKey\":\"_custom_id_\",\"TagValue\":\"$CUSTOM_ID\"}]"
  • This command creates a new KMS key with the custom key material and key ID specified in the tags.

Important Notes

Key Material Size

The key material must be 32 bytes. If it’s not, you’ll encounter an error like this:

## This will result in an error

CUSTOM_KEY_MATERIAL="invalid material"
CUSTOM_ID="00000000-0000-0000-0000-000000000001"

awslocal kms create-key --tags "[{\"TagKey\":\"_custom_key_material_\",\"TagValue\":\"$CUSTOM_KEY_MATERIAL\"},{\"TagKey\":\"_custom_id_\",\"TagValue\":\"$CUSTOM_ID\"}]" --cli-binary-format raw-in-base64-out

## Error Message
An error occurred (InternalError) when calling the CreateKey operation (reached max retries: 2): exception while calling kms.CreateKey: Incorrect padding

Key Material Format

Ensure the key material is in Base64-encoded format. If you use a hex string, you’ll face errors during encryption:

## This will also result in an error

CUSTOM_KEY_MATERIAL="$(openssl rand -hex 32)"
CUSTOM_ID="00000000-0000-0000-0000-000000000001"

awslocal kms create-key --tags "[{\"TagKey\":\"_custom_key_material_\",\"TagValue\":\"$CUSTOM_KEY_MATERIAL\"},{\"TagKey\":\"_custom_id_\",\"TagValue\":\"$CUSTOM_ID\"}]" --cli-binary-format raw-in-base64-out

awslocal kms encrypt --key-id $CUSTOM_ID --plaintext supersecret --output text --query CiphertextBlob --cli-binary-format raw-in-base64-out

## Error Message
An error occurred (InternalError) when calling the Encrypt operation (reached max retries: 2): exception while calling kms.Encrypt: Invalid key size (384) for AES.

AWS CLI Option

If you’re using AWS CLI v2, you’ll need to specify the --cli-binary-format raw-in-base64-out option; otherwise, you’ll get an error like this:

## This will result in an error

awslocal kms encrypt --key-id $CUSTOM_ID --plaintext supersecret --output text --query CiphertextBlob

## Error Message
Invalid base64: "supersecret"

Refer to the AWS CLI options documentation for more details.

Testing Encryption and Decryption

Encrypt Data

PLAINTEXT="supersecret"

CIPHERTEXT=$(awslocal kms encrypt --key-id $CUSTOM_ID --plaintext "$PLAINTEXT" --output text --query CiphertextBlob --cli-binary-format raw-in-base64-out)

echo "Ciphertext: $CIPHERTEXT"

Decrypt Data

DECRYPTED_TEXT=$(awslocal kms decrypt --ciphertext-blob fileb://<(echo "$CIPHERTEXT" | base64 -d) --output text --query Plaintext --cli-binary-format raw-in-base64-out | base64 -d)

echo "Decrypted text: $DECRYPTED_TEXT"
  • Expected Output: Decrypted text: supersecret

Conclusion

By using custom key material when creating KMS keys in LocalStack, you can simplify key preparation and make it easier to share encrypted test data within your team.

Key Takeaways

  1. Key Material Format: The key material must conform to the allowed data key byte sizes.

Specifies the length of the data key in bytes. For example, use the value 64 to generate a 512-bit data key (64 bytes is 512 bits). For 128-bit (16-byte) and 256-bit (32-byte) data keys, use the KeySpec parameter.

generate_data_key - Boto3 1.35.24 documentation

  1. When using AWS CLI v2, always include the --cli-binary-format raw-in-base64-out option.

  2. Fixing the keyId makes scripting and automation more straightforward.

References