KMS

KMS overview and attacks

Data Encryption & Decryption with KMS

KMS can directly encrypt/decrypt data but only up to 4096 bytes. Anything over this size limit requires leveraging a Data Encryption Key (DEK). Reference the official docs for more as there are other use cases and types of keys.

Encrypting Data

When leveraging the AWS CLI (AWS SDK under the hood), the data is sent to the KMS service, and a KMS key is used to encrypt the data. The data is sent to the service because the KMS key cannot leave.

# encrypting plaintext data directly
aws kms encrypt --key-id <keyId> --plaintext <plaintextData>

# encrypting a file containing plaintext
aws kms encrypt --key-id <keyId> --plaintext fileb://<plaintextFile>

Decrypting Data

Again, the data is sent to the KMS service to be decrypted.

# decrypting ciphertext data directly
aws kms decrypt --key-id <kms-key-id> --ciphertext <ciphertextData>

# decrypting a file containing encrypted data
aws kms decrypt --key-id <kms-key-id> --ciphertext-blob fileb://<encryptedFile> --output text --query Plaintext | base64 --decode > <outputFileName>

Data Encryption & Decryption with KMS Data Encryption Key (DEK)

Data encryption keys can be generated when you need to encrypt / decrypt data larger than 4096 bytes. These keys are generated from a KMS key and the plaintext version performs the encryption. After the plaintext key encrypts the data, it should be deleted, and the encrypted version of the key stored alongside the encrypted data. The encrypted key contains metadata which describes the KMS key required to decrypt itself.

When generating a data encryption key using the AWS CLI (or AWS SDK) you must remember to delete the plaintext key otherwise an attacker can access your data. The AWS Encryption SDK can perform this task for you.

Generating a DEK

aws kms generate-data-key --key-id <kms-key-id> --key-spec AES_256                                                        
{
    "CiphertextBlob": "AQIDAHjBdOEYbwSwF+14[snip]",
    "Plaintext": "AzSMaOZqgt[snip]",
    "KeyId": "arn:aws:kms:us-east-1:<aws-account-id>:key/<kms-key-id>"
}

Store the CiphertextBlob and Plaintext like so.

echo "AQIDAHj..." | base64 --decode > encrypted_key
echo "AzSMaOZqgt..." | base64 --decode > plaintext_key

Encrypting Data with DEK

Since the AWS CLI cannot utilize data encryption keys, you must use another solution such as openssl.

openssl enc -in <fileToEncrypt> -out <newEncryptedFileName> -e -aes256 -k fileb://plaintext_key 

Now, the plaintext_key should be deleted and the encrypted_key stored alongside the encrypted data.

Decrypting Data with DEK

Now, use the KMS key to decrypt the the encrypted_key and store the plaintext key in a file, plaintext_key .

aws kms decrypt --ciphertext-blob fileb://encrypted_key --key-id <kms-key-id> --query Plaintext --out text | base64 --decode > plaintext_key

Again, leverage openssl or similar to decrypt the data.

openssl enc -in encrypted_large_file.txt -out decrypted_large_file.txt -d -aes256 -k fileb://plaintext_key

AWS Encryption SDK

The AWS Encryption SDK can be leveraged as a CLI tool or in code such as Python, Java, JavaScript, .NET, and C. Utilizing this SDK will handle KMS and DEKs for you, simplifying the process compared to the previous examples of generating DEKs and using 3rd party solutions like openssl.

Encrypting Data with Encryption SDK (CLI)

aws-encryption-cli --encrypt --input plaintext_sdk_example.txt --output encrypted_sdk_example.txt --wrapping-keys key=<kms-key-id> --commitment-policy require-encrypt-require-decrypt --metadata-output metadata

A metadata file is generated which details the encryption process such as the KMS key used, algorithm, and more.

cat metadata

    {"header": {"algorithm": "AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384", "commitment_key": "YVeJwji5Cx1NYBizsDfi3u6Z7yydR3DSvtVBmb985BE=", "content_type": 2, "encrypted_data_keys": [{"encrypted_data_key":[snip]

Decrypting Data with Encryption SDK (CLI)

aws-encryption-cli --decrypt --input encrypted_sdk_example.txt --output decrypted_sdk_example.txt --wrapping-keys key=<kms-key-arn> --metadata-output metadata_d --max-encrypted-data-keys 1 --buffer --commitment-policy require-encrypt-require-decrypt

kms:CreateGrant

  • With this action available, you can provide yourself a Grant to a KMS key and effectively give yourself access.

  • Grants are considered along with key policies and IAM policies and often used for temporary permissions because you can create one, use its permissions, and delete it without changing your key policies or IAM policies.

aws kms create-grant --key-id <keyId> --grantee-principal <userARN> --operations Decrypt
# decrypt data with the grant
aws kms decrypt --grant-tokens <grantToken> --ciphertext-blob <cipherText> --key-id <keyId> --output text --query Plaintext | base64 --decode

kms:PutKeyPolicy

  • With this action available, you can update or replace the Key Policy for a KMS key to give yourself permissions.

aws kms put-key-policy --policy file://key-policy.json --policy-name default --key-id <keyId>
# sample key policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/<userName>"
            },
            "Action": [
                "kms:*"
            ],
            "Resource": "*"
        }
    ]
}

Last updated