AWS Security Blog

How to Help Protect Sensitive Data with AWS KMS

August 31, 2021: AWS KMS is replacing the term customer master key (CMK) with AWS KMS key and KMS key. The concept has not changed. To prevent breaking changes, AWS KMS is keeping some variations of this term. More info.


AWS Key Management Service (AWS KMS) celebrated its one-year launch anniversary in November 2015, and organizations of all sizes are using it to effectively manage their encryption keys. KMS also successfully completed the PCI DSS 3.1 Level 1 assessment as well as the latest SOC assessment in August 2015.

One question KMS customers frequently ask is about how how to encrypt Primary Account Number (PAN) data within AWS because PCI DSS sections 3.5 and 3.6 require the encryption of credit card data at rest and has stringent requirements around the management of encryption keys. One KMS encryption option is to encrypt your PAN data using customer data keys (CDKs) that are exportable out of KMS. Alternatively, you also can use KMS to directly encrypt PAN data by using an AWS KMS key. In this blog post, I will show you how to help protect sensitive PAN data by using KMS keys.

The use of a KMS key to directly encrypt data removes some of the burden of having developers manage encryption libraries. Additionally, a KMS key cannot be exported from KMS, which alleviates the concern about someone saving the encryption key in an insecure location. You can also leverage AWS CloudTrail so that you have logs of the key’s use.

For the purpose of this post, I have three different AWS Identity and Access Management (IAM) roles to help ensure the security of the PAN data being encrypted:

  1. KeyAdmin – This is the general key administrator role, which has the ability to create and manage the KMS keys. A key administrator does not have the ability to directly use the keys for encrypt and decrypt functions. Keep in mind that because the administrator does have the ability to change a key’s policy, they could escalate their own privilege by changing this policy to give themselves encrypt/decrypt permissions.
  2. PANEncrypt – This role allows the user only to encrypt an object using the KMS key.
  3. PANDecrypt – This role allows the user only to decrypt an object using the KMS key.

If you don’t already have a KMS key that you wish to use to encrypt the sensitive PAN data, you can create one with the following command. (Throughout this post, remember to replace the placeholder text in red with your account-specific information.)

$ aws kms create-key --profile KeyAdmin --description "Key used to encrypt and decrypt sensitive PAN data" --policy file://Key_Policy

Notice the use of –profile KeyAdmin in the previous command. This forces the command to be run as a role specified within my configuration file that has permissions to create a KMS key. We will be using different roles, as defined in the following key policy (to which file://Key_Policy in the previous command refers), to manipulate and use the KMS key. For additional details about how to assume roles within the CLI, see Assuming a Role.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAccessForKeyAdministrators",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/KeyAdmin"   
      },
      "Action": [
        "kms:Create*",
        "kms:Describe*",
        "kms:Enable*",
        "kms:List*",
        "kms:Put*",
        "kms:Update*",
        "kms:Revoke*",
        "kms:Disable*",
        "kms:Get*",
        "kms:Delete*",
        "kms:ScheduleKeyDeletion",
        "kms:CancelKeyDeletion"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowEncryptionWithTheKey",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/PANEncrypt"
      },
      "Action": [
        "kms:Encrypt",
        "kms:ReEncrypt*",
        "kms:GenerateDataKey*",
        "kms:DescribeKey",
        “kms:ListKeys” 
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowDecryptionWithTheKey",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/PANDecrypt"
      },                             
      "Action": [
        "kms:Decrypt",
      ],
      "Resource": "*"
    }
  ]
}

After the new KMS key is created, I can then assign it an alias so that it will be easier to identify in the future. In this case, I will create the alias SensitivePANKey, as shown in the following command.

$ aws kms create-alias --profile KeyAdmin --alias-name alias/SensitivePANKey --target-key-id arn:aws:kms:us-east-1:123456789012:key/221c9ce1-9da8-44e9-801b-faf1EXAMPLE 

Now that I have a KMS key with least-privilege permissions to limit who can manage and use it, I can start to use it to encrypt PAN data. To keep things simple in this post, I will be using AWS CLI commands to accomplish this, but this can also be done through an AWS SDK and incorporated into an application.

Using the PANEncrypt role, the following CLI command takes in a string of data (in this case “Sensitive PAN Data”), encrypts it using the key I created earlier in this post, and sends the Base64-encoded ciphertext into a new file called encrypted-123-1449012738. Notice that I also use EncryptionContext to further protect the encrypted data. The sensitive PAN data is sent to KMS over TLS (with ciphers that enforce perfect forward secret) and is then encrypted under AES-GCM using a 256-bit key.

$ aws kms encrypt --profile PANEncrypt --key-id alias/SensitivePANKey --plaintext "Sensitive PAN Data" --query CiphertextBlob --encryption-context UserName=user@domain.com,Date=1449012738 --output text | base64 --decode > encrypted-123-1449012738

Because the EncryptionContext must be the same when I decrypt the file, I gave the file a unique name that can help us rebuild the EncryptionContext when it comes time to decrypt the object. The file name structure is: encrypted-GUIDDate. The GUID allows us to look up the user’s user name within our directory, and then I use the date as part of the context. As Greg Rubin discussed in another AWS Security Blog post, the EncryptionContext can help ensure the integrity of the encrypted data.

From here, I can use the following command to put this encrypted object in an Amazon S3 bucket.

$ aws s3 cp encrypted-123-1449012738 s3://Secure-S3-Bucket-For-PAN/ --region us-west-2 --sse aws:kms

For this example, I chose to use SSE-KMS encryption with a default KMS key, but this could have been a custom KMS key as well. From here, I update a database with the location of this encrypted object within the S3 bucket.

When I need to retrieve the PAN data, I can make the following CLI call to get the encrypted object from my S3 bucket.

$ aws s3 cp s3://Secure-S3-Bucket-For-PAN/ encrypted-123-1449012738 . --region us-west-2

Finally to decrypt the object, I run the following command using the PANDecrypt role.

$ echo “Decrypted PAN Data: $(aws kms decrypt --profile PANDecrypt --ciphertext-blob fileb://encrypted-123-1449012738 --encryption-context UserName=user@domain.com,Date=1449012738 --output text --query Plaintext | base64 --decode)”

Notice that I use the same EncryptionContext as I did when I encrypted the sensitive PAN data. To get this EncryptionContext, I again look up the UserName from the GUID and then include the Date. Then for the purpose of this example, I print this sensitive data to the screen, but in a real-world example, this can be passed to another application or service.

Now that I have shown that KMS can directly encrypt and decrypt sensitive PAN data, this can be rolled out as a service within an application environment. As a best practice, you should avoid using the same KMS key to encrypt more than 2 billion objects. After that point, the security of the resulting ciphertexts may be weakened. To mitigate this risk, you can choose to have KMS rotate your KMS key annually or you can create multiple KMS keys to handle your workloads safely. Additionally, this service should be composed of two different component services: one that provides encryption, and another that has enhanced controls around it and is used to decrypt the sensitive data. These component services would address storage of the ciphertext, metadata, error conditions, and so on. With the integration of CloudTrail into KMS and application logs, an organization can have detailed records of the calls into the service and the use of KMS keys across the organization.

If you have questions or comments about this post, either post them below or visit the KMS forum.

– Matt

Want more AWS Security how-to content, news, and feature announcements? Follow us on Twitter.