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.