KMS Demystified

KMS Demystified

AWS KMS is one of those services that works tirelessly in the background but doesn't get a lot of love. It's common to automatically encrypt every service as part of development and infrastructure-creation workflow, and this usually happens through KMS AWS-owned keys. There is nothing wrong with this, and usually, this will easily fill any encryption compliance needs you have. However, there are more less-commonly known features and configurations without KMS that are not talked about as much.

What is KMS? Put simply, KMS is the AWS key management service that allows you to encrypt your data with keys that are either managed by AWS or you. You can create both symmetric and asymmetric keys. KMS uses envelope encryption, which means it encrypts data with a root key. The data key is what is used to encrypt your data.

root key --> encrypts data key --> encrypts plaintext

The keys are decrypted within a FIPS validated HSM, and when not in use they are encrypted and stored in persistent storage isolated from the HSMs. If there were ever an attempt to remove the actual hardware hosting the key material, the power would be killed to the storage rack, wiping all key material from storage. This differs from other HSMs where material is persisted on HSMs. That is the key difference between AWS KMS and other standard providers: The HSMs in use do not persistently store key data.

Note: The KMS Key itself never leaves AWS. There is no ability to 'download' the key for usage in other places, except Data keys

Types of Keys

  • KMS Keys

    • Customer Managed Keys (CMK), confusingly AWS decided to replace this term with 'AWS KMS key' and 'KMS key'

      • A key that you specifically create and manage. You configure the policies, rotation, creating aliases, etc.

      • These keys should be used to protect the most sensitive workloads because of the following:

        • Users will no longer just require permissions granting access to the data (s3:GetObject), but rather also access to the key to decrypt the data (kms:Decrypt).

        • The key policy can be edited to further restrict/grant access

        • Key usage is recorded in Cloudtrail

    • AWS Managed Keys

      • These keys show up in KMS prefixed by the service name, ex: aws/s3. You do not control the policy nor rotation

      • The most widely used keys due to ease of use

      • Allows auditing via Cloudtrail

    • AWS Owned Keys

      • Owned are managed by AWS, and do not tie to one account specifically.

      • Nothing notable about these keys, there will be zero interaction with them besides checking a box in a service

      • Not audited as part of Cloudtrail

  • Data Keys

    • Kata keys are symmetric keys similar to what is detailed above, hoewver you can download these keys for use outside of AWS. When using a data key, you are returned:

      • plaintext data key for optional immediate use

      • encrypted copy of the data key for storage with data

        • When using, you must use the AWS KMS service to decrypt this data key
    • Encryption is done as normal using the plaintext key, plaintext --> encrypt plaintext data key --> encrypt ciphertext

    • Decryption must be done via the KMS Decrypt API call, passing the encrypted version of your data key. This decrypts your plaintext data key, which is used to decrypt the data.

      • The following scenario is taking directly from AWS docs and providers an interesting, albeit complex, example

        •   You create an encrypted EBS volume and specify a KMS key 
            to protect it. Amazon EBS asks AWS KMS to use your 
            KMS key to generate an encrypted data key for the volume. 
            Amazon EBS stores the encrypted data key with the volume's 
            metadata.
          
            When you attach the EBS volume to an EC2 instance, 
            Amazon EC2 uses your KMS key to decrypt the EBS volume's 
            encrypted data key. Amazon EC2 uses the data key in the 
            Nitro hardware, which is repsonsible for encrypting 
            all disk I/O to the EBS volume. The data key persists 
            in the Nitro hardware while the EBS volume is attached 
            to the EC2 instance.
          
            You perform an action that makes the KMS key unusable. 
            This has no immediate effect on the EC2 instance or 
            the EBS volume. Amazon EC2 uses the data key—not the 
            KMS key—to encrypt all disk I/O while the volume is 
            attached to the instance.
          
            However, when the encrypted EBS volume is detached 
            from the EC2 instance, Amazon EBS removes the data 
            key from the Nitro hardware. The next time the encrypted 
            EBS volume is attached to an EC2 instance, the attachment 
            fails, because Amazon EBS cannot use the KMS key to 
            decrypt the volume's encrypted data key. To use the EBS 
            volume again, you must make the KMS key usable again.
          

Key Storage

KMS supports multiple storage options for storing your keys securely utilizing either CloudHSM or an external custom key store such as Hashicorp vault. I won't discuss these here, but know that each is an option. CloudHSM is an excellent product that is FIPS 140-2 validated. The good thing about CloudHSM is the ability to integrate it tightly with your IAM access controls, meaning you don't have to spend weeks configuring logging, permissions, policies, etc.

ABAC

Attribute-based access control (ABAC) provides a unique way to manage permissions to KMS CMKs without having to consistently edit permissions. However, it is important to consistently audit key tags to ensure principals are not inadvertently locked out of accessing a key. Give permissions to edit tags to only people who need them, ideally this is a dual-control scenario where multiple people must be involved to edit permissions.

Note: Remember that a key alias doesn't represent a KMS key in the resource section of an IAM policy. Giving a user access to use alias/MyEncryptionKey does not give that user access to the target key.

Some of the unique features around KMS are specific KMS ABAC condition keys such as the following:

  • kms:ResourceAlias - aliases associated with key match the aliases defined in the pattern

    • This is a multivalue key that supports the ForAnyValue:StringEquals condition

    • Used only with IAM policies

      •   {
            "Sid": "AliasBasedIAMPolicy",
            "Effect": "Allow",
            "Action": "kms:GenerateDataKey",
            "Resource": [
              "arn:aws:kms:*:111122223333:key/*",
              "arn:aws:kms:*:444455556666:key/*"
            ],
            "Condition": {
              "ForAnyValue:StringEquals": {
                "kms:ResourceAliases": "alias/finance-key"
              }
            }
          }
        
  • kms:RequestAlias - Alias that represents the KMS key in the request matches the alias defined in the policy

    • The same as above applies with the added fact that these can be used with both IAM policies and key policies

    • An example use case of this policy would be ensuring a user is only allowed to use the Encrypt operation with a specific key alias

The benefit of using the kms:ResourceAlias is the fact that you can use an alias in a policy without needing to list every single key ARN in the policy. Aliases are much more readable. An alias acts as a pointer to a key, so the alias target key can change and the alias remains the same. This could be considered a good or bad thing, depending on how your key management process is:

  • Pro: If you need to change the underlying key, simply point the alias at a new key

  • Con: Permissions to the underlying key are required, meaning applications may stop working if an alias is changed or removed without coordination

Note: Alias do not work with policies in the Resource block. You must use the Resource/Request alias condition key.

Utliize the aws:ResourceTag/tag-key condition key to control which users have access to which specific keys based on the tag of that key. Similarly the aws:RequestTag/tag-key can be utilized to control deletions of keys. This gives extreme flexibility to controlling who can do what with keys, and makes controlling access to highly-sensitive data much easier.

ABAC helps to avoid the problem of having to constantly edit key policies. Instead, you specify user key access based on tags. For example, you apply a permission policy to users/groups who are in charge of production development that gives them kms:Encrypt and kms:GenerateDataKey access to keys with a tag of Env=Prod

KeySpec

The Key spec is a property of a key that represents the configuration of that key. It differs depending upon the type. More can be read here. The important part of KeySpec is that it can be used as a condition key to strictly control what type of keys are created. The following policy for example allows the principal to create only RSA asymmetric KMS keys:

{
  "Effect": "Allow",
  "Action": "kms:CreateKey",
  "Resource": "*",
  "Condition": {
    "StringLike": {
      "kms:KeySpec": "RSA_*"
    }
  }
}

Something like this is incredibly useful but hardly implemented. You can force users to only perform kms:Encrypt and kms:Decrypt with key with a spec of SYMMETRIC_DEFAULT. You can also deny permission to delete, for example, your RSA keys if you require them for business-critical operations. The possibilities are endless.

AEAD

AEAD stands for authenticated encryption and associated data. More can be read about it here. But to wrap your head around it, take it at face value. Authenticated encryption -- the ciphertext is authenticated to prevent tampering with it directly. Associated data -- data that is associated with the encrypted data through an encryption context.

An EncryptionContext is the KMS version of authenticated data. Put simply, it is plaintext data that gets associated with the encrypted data to provide extra integrity protection.

For example, if you put data into a k/v database, you might encrypt that data first. You then proceed to use the unencrypted database key as the EncryptionContext. An attacker then tries to retrieve the encrypted data, however without knowing the key (in the k/v pair) is the context, they aren't able to provide any EncryptionContext. Their attempt has not authenticated properly and they are blocked from performing the kms:Decrypt operation.

There are much more intricate details with AEAD that are beyond the scope of even the public AWS docs, and there is no need to dive into the implementation unless you're curious. AWS handles it all for you.