Import backup of external AWS KMS keys - amazon-web-services

As a disaster recovery measure, is it possible to import an external KMS key to decrypt data encrypted by a previous key?
In the AWS documentation it says the following:
Can't decrypt with any other KMS key
When you encrypt data under a KMS key, the ciphertext is permanently
associated with the KMS key and its key material. It cannot be
decrypted with any other KMS key, i̲n̲c̲l̲u̲d̲i̲n̲g̲ ̲a̲ ̲d̲i̲f̲f̲e̲r̲e̲n̲t̲ ̲K̲M̲S̲ ̲k̲e̲y̲ ̲w̲i̲t̲h̲ ̲t̲h̲e̲ ̲s̲a̲m̲e̲ ̲k̲e̲y̲ ̲m̲a̲t̲e̲r̲i̲a̲l̲.
This is a security feature of KMS keys.
https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html
I did some testing where I imported an externally generated key and then re-imported it into KMS (generating a second key with a second ID). Data encrypted by the first key could not be decrypted by the second key (even having the same key material).
But, can any AWS customer back up keys for post-malicious/accidental KMS key deletion recovery?

Related

storing a GPG key in AWS KMS

I have a series of files in AWS S3, encrypted with a GPG public key. I want to store the private key in AWS KMS, so I can programmatically decrypt them.
I have followed AWS docs for BYOK (https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html) but cannot encrypt my private key with the AWS wrapping key as it's "unable to load private key" when I follow step 2 of https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-encrypt-key-material.html.

How can I create two KMS keys with the same material to do encrypt/decrypt?

I am using KMS to encrypt/decrypt data (s3 object and string value). The data I encrypted is the file saved in s3 bucket. And some string values saved in database.
As an example, I use below command to encrypt the string value.
aws kms encrypt --key-id 59c41ce0-153b-487c-821b-7c1d994fca0c --plaintext QTEyMzQtMTIzNDU2WFk=
The key I created is a single region key. It is custom managed key and I use external origin and I have imported the key material to the KMS. Now I need to deploy my application in another region which means I need to decrypt the existing data from the other region.
The problem I have is that AWS doesn't support converting a single region key to multiple region. That means I have to re-create a new multiple region key. I own the key material and I expect I can import the same key material to the new multi-region key, then use this key to decrypt the existing data which was encrypted by the single region key.
But I got this error when I try to decrypt: An error occurred (IncorrectKeyException) when calling the Decrypt operation: The key ID in the request does not identify a CMK that can perform this operation..
It seems there are other data used by kms to encrypt the data besides key material. Is there a way to replicate the key with same material? I expect I am able to re-create the kms with the same material to decrypt the data.

How to use Terraform to store a new secret in AWS Secrets Manager using already KMS-encrypted string?

I need to write Git-revisioned Terraform code to put a secret string into AWS Secrets Manager. Given a secret string in a textfile:
% cat /tmp/plaintext-password
my-super-secret-password
I am able to make an encrypted version of it using a KMS key:
# Prints base64-encoded, encrypted string.
aws kms encrypt --key-id my_kms_uuid --plaintext fileb:///tmp/plaintext-password --output text --query CiphertextBlob
# abcdef...123456789/==
What Terraform code can be written to get that base64 string in AWS Secrets Manager, such that AWS knows it was encrypted with my_kms_uuid? I have tried the following:
resource "aws_secretsmanager_secret" "testing-secrets-secret" {
name = "secret-for-testing"
kms_key_id = "<my_kms_uuid>"
}
resource "aws_secretsmanager_secret_version" "testing-secrets-version" {
secret_string = "abcdef...123456789/=="
}
The problem is I can't figure out a way to tell AWS Secrets Manager that the string is already encrypted by KMS so it doesn't have to encrypt it again. Can this be done?
If your goal is to keep secret values out of the statefile, then you have two choices:
Encrypt the secret outside of Terraform, then store the encrypted value in Secrets Manager.
This will force all consumers of the secret to decrypt it before use. Since an encrypted secret includes the CMK used to encrypt it, there's no need for you to separately track the key ID.
There are several drawbacks to this approach. For one thing, you have to do two steps to use any secret: retrieve it and decrypt it. If you use ECS, you can't provide the name of the secret and let ECS to provide the decrypted value to your container.
A bigger drawback is that it can be very easy to forget which CMK is used for which secret, and accidentally delete the CMK (at which point the secret becomes unusable). Related is knowing which permissions to grant to the consumers, especially if you have a lot of CMKs.
Create the secret inside Terraform, and set its value manually.
This keeps the actual value in Secrets Manager, so you don't need to use two steps to decrypt it.
It is possible to use local-exec to generate the secret within the Terraform configuration: write a script that generates random data and then invokes the AWS CLI to store the value. However, this technique is more frequently used for things like SSH private keys that are created outside of the Terraform provisioning process.
Better than either of these solutions is to store your statefile somewhere that it isn't generally accessible. There are a bunch of backends that can do this for you.
When encrypting a secret string with KMS, the key that was used is actually specified within the output of the resulting ciphertext. This means we can do this the following way:
Encrypt the secret string with an existing KMS key in AWS aws kms encrypt --key-id arn:aws:kms:us-east-1:<account_id>:key/mrk-blahblahblah --plaintext fileb://<(printf 'SOME_SECRET_TEXT') --output text --query CiphertextBlob --region us-east-1
Then use aws_kms_secrets to decrypt it within Terraform (something like this).
Then you push the decrypted secret up into AWS Secrets Manager using aws_secretsmanager_secret_version.
The net result is only the encoded secret is kept in version control, but the password that's actually stored in Secrets Manager is the decoded string.

How to get a list of all the resourcers linked with an AWS KMS

I'd want to identify who is using a specified AWS KMS key.
In other words, given an AWS KMS key, which AWS resource is it being used on?

AWS KMS Decrypt: How is the KMS Key configured?

I am a little confused as to how a cipher text blob is decrypted with the AWS KMS client. Here is an example from the AWS Docs:
// Encrypt a data key
//
// Replace the following fictitious CMK ARN with a valid CMK ID or ARN
String keyId = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-
56ef-1234567890ab";
ByteBuffer plaintext = ByteBuffer.wrap(new byte[]{1,2,3,4,5,6,7,8,9,0});
EncryptRequest req = new
EncryptRequest().withKeyId(keyId).withPlaintext(plaintext);
ByteBuffer ciphertext = kmsClient.encrypt(req).getCiphertextBlob();
// Decrypt a data key
//
ByteBuffer ciphertextBlob = Place your ciphertext here;
DecryptRequest req = new DecryptRequest().withCiphertextBlob(ciphertextBlob);
ByteBuffer plainText = kmsClient.decrypt(req).getPlaintext();
No KMS key is provided in the decrypt method. Does that mean somehow the KMS key is encrypted in the cyphertext blob? If so...
how are the permissions granted to decrypt the encrypted ciphertext blob?
If I wanted to decrypt a value from an AWS service, do I create an IAM role to do so and configure the KMS Key to allow that role to decrypt?
Your examples is from Encrypting and Decrypting Data Keys:
These operations are designed to encrypt and decrypt data keys. They use an AWS KMS customer master key (CMK) in the encryption operations and they cannot accept more than 4 KB (4096 bytes) of data. Although you might use them to encrypt small amounts of data, such as a password or RSA key, they are not designed to encrypt application data.
CMKs:
are created in AWS KMS and never leave AWS KMS unencrypted. To use or manage your CMK, you access them through AWS KMS.
These operations are all using configured master keys from your AWS setup, rather than keys that are provided dynamically. Use AWS Management Console to manage these keys.
When using the encrypt method, the key ID is stored in the response:
{
"CiphertextBlob": blob,
"KeyId": "string"
}
It will be used during decryption.
AmazonKeyManagementServiceConfig kmsConfig = new AmazonKeyManagementServiceConfig();
kmsConfig.UseHttp = true;
kmsConfig.ServiceURL = serviceEndPoint;
//create client, specify Region end point or kms config
AmazonKeyManagementServiceClient kmsClient = new AmazonKeyManagementServiceClient(awsKeyForKMS, awsSecretKeyForKMS, kmsConfig);
DecryptRequest decryptRequest = new DecryptRequest();
// use hare above created encrypteddatakey to get plaindatakey
MemoryStream streamEncryptedDataKey = new MemoryStream(Convert.FromBase64String(encryptedDataKey));//convert to stream object
decryptRequest.CiphertextBlob = streamEncryptedDataKey;
DecryptResponse decry`enter code here`ptResp = kmsClient.Decrypt(decryptRequest);
plainDataKey = Convert.ToBase64String(decryptResp.Plaintext.ToArray());
// your decryption logic
DecryptTexts("encrypted data", PlainKey)
For the purposes on this answer, I am assuming you are using an AWS KMS CMK with symmetric encryption.
When using symmetric keys, the content of the cyphertext block does include which KMS key was used to encrypt it. See more about that here: https://docs.aws.amazon.com/kms/latest/developerguide/programming-encryption.html#decryption They still note you should specify a key to decrypt it, but isn't needed since it is in the cyphertext blob.
However, the presence of that key id doesn't grant any permissions to use that key for decrypting anything. You can have a user/entity that is permitted to encrypt things with no permission to decrypt with that key, including the message they just encrypted. You can control who has access to the decrypt function through two different ways.
The first is the 'Key Policy'. Key policies always 'win' are the first policy consulted when evaluating if an access is permitted. By default, the Key Policy is setup to say 'allow whoever to use this key that is permitted to by IAM'. (BTW, IAM is the second way of getting access, but only if the Key Policy allows it to be delegated to for that key). Key Policies only apply to the key they are applied to (even if the resource field is '*'). Key policies can grant access to specific entities/users/accounts and/or delegate to IAM. You can read more about the default key policy here: https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default
The second is IAM, where you are likely more familiar with in the general account permissions. This is where you can also grant access to kms:decrypt to policy statements and resources you wish it to be applied to.
However, back to your original questions:
how are the permissions granted to decrypt the encrypted ciphertext blob?
Through access to the kms:decrypt permission, either through the Key Policy or IAM (when delegation to IAM is enabled in the Key Policy).
If I wanted to decrypt a value from an AWS service, do I create an IAM role to do so and configure the KMS Key to allow that role to decrypt?
It doesn't matter who created the key, if a data element is protected by a KMS key, that key needs to be granted access to kms:decrypt. (Exception: some AWS services need grants, like EBS, instead of kms:decrypt due to low level technical issues, but the same principle applies). Make sure that the key policy has the IAM delegation enabled, and then feel free to attach a policy to a IAM role which permits access to kms:decrypt for that key to get access to decrypt operations for that user/entity which has that role. Of course, you needed to give permission to AWS to encrypt using that Key in the first place (and that is done through the Key Policy where the principal is an AWS service), but I assume you already did that since your question is about decrypt.