aws

Data Security with AWS Key Management Service – Part II

In the previous post, I shared the workflow for symmetric key encryption. This article will focus on a workflow for asymmetric keys for encryption, digital signing and verification in AWS Key Management Service (KMS). While a symmetric key is mostly used in the context of Data at Rest where data is owned by a singular entity/team, an asymmetric key is used mostly in the context of Data in Transit where data is transferred between two entities.

An asymmetric key (using different keys for encryption and decryption) has the following main benefits

  • Separates the process of encryption and decryption between two keys. A private key is never shared with the outside world and thus private to the owner, whereas the public key can be safely shared.
  • A key pair enables establishing trust between entities. Only a correct private key (thus a valid entity) can decrypt a message which was encrypted using its public key.
  • A sender can digitally sign messages (or documents) with their private key. The receiver can verify the integrity of the message using the sender’s public key.

How to Encrypt using asymmetric CMK in KMS

The first step is to create an asymmetric Customer Master Key (CMK) in KMS. As of now, AWS restricts usage of this CMK to either encryption/decryption OR sign/verify but not both. However, a generated data key pair can be used for both purposes.

# Creating Asymmetric CMK for Encryption and Decryption using default option of AWS creating the key material
aws kms create-key --description "a-cmk for EncryptAndDecrypt" --key-usage ENCRYPT_DECRYPT --customer-master-key-spec RSA_4096 --origin AWS_KMS

# Above command returns following output
{
    "KeyMetadata": {
        "AWSAccountId": "2008XXXXXXXX",
        "KeyId": "3edcaa6f-6d34-4887-afff-7c5eb35fd772",
        "Arn": "arn:aws:kms:ap-south-1:2008XXXXXXXX:key/3edcaa6f-6d34-4887-afff-7c5eb35fd772",
        "CreationDate": "2020-12-17T23:56:33.398000+05:30",
        "Enabled": true,
        "Description": "ToEncryptAndDecrypt",
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "Origin": "AWS_KMS",
        "KeyManager": "CUSTOMER",
        "CustomerMasterKeySpec": "RSA_4096",
        "EncryptionAlgorithms": [
            "RSAES_OAEP_SHA_1",
            "RSAES_OAEP_SHA_256"
        ]
    }
}

The private key of an asymmetric CMK, never leaves AWS KMS unencrypted.

# Download the public key, decode from base64, save it in file
aws kms get-public-key --key-id 3edcaa6f-6d34-4887-afff-7c5eb35fd772 --output text --query PublicKey | base64 --decode > public_key.der

Any data submitted to the Encrypt, Decrypt, or Re-Encrypt APIs that require use of asymmetric operations must also be less than 4KB.

https://aws.amazon.com/kms/faqs/
# To encrypt, use "encrypt" API and and pass the CMK key id. KMS encrypts using your public key.
aws kms encrypt --key-id 3edcaa6f-6d34-4887-afff-7c5eb35fd772 --plaintext fileb://data.txt --encryption-algorithm RSAES_OAEP_SHA_256 --output text --query CiphertextBlob | base64 --decode > data.cipher

# To decrypt, use "decrypt" API and pass the same CMK key id. KMS decrypts using your private key.
aws kms decrypt --key-id 3edcaa6f-6d34-4887-afff-7c5eb35fd772 --ciphertext-blob fileb://data.cipher --encryption-algorithm RSAES_OAEP_SHA_256 --output text --query Plaintext | base64 --decode > recover_data.txt

How to Encrypt using asymmetric data key pair

If for any reason, you do not want to use asymmetric CMK or want to use the same key pair for encryption and sign/verify then the data key pair is a better option. KMS protects the private data key under symmetric CMK.

There is a limit on max data size which can be encrypted by RSA (as explained here). To overcome this limit, a hybrid encryption process is used (explained here). Assuming if both parties have access to each other’s public key then-

  • The sender generates a random symmetric key
  • Encrypt large data with this symmetric key
  • Encrypt the random symmetric key using the receiver’s public key
  • Share the encrypted data and encrypted random key to the receiver
  • The sender then removes the random symmetric key from local storage and memory

Tip – To generate data key pair, KMS only needs symmetric CMK key id (or alias) and key pair type. The symmetric CMK is used by KMS to encrypt and decrypt private data key. KMS does not use a private master key for the encryption of data keys.

https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#data-key-pairs
# Generate Data Key pair without receiving plaintext private key. 
# This command returns plaintext public key and encrypted private key (as base64 encoded). 
# Copy and save them in a file (e.g. public_key.base64, private_key_cipher.base64) 
aws kms generate-data-key-pair-without-plaintext --key-id alias/twc/cmk --key-pair-spec RSA_4096

# Decode and save the public data key (which was returned as plaintext) in a file
openssl enc -d -base64 -A -in public_key.base64 -out public_key.der

# Then use above output to encrypt the actual data (max 4 KB)
openssl rsautl -encrypt -in data.txt -inkey public_key.der -keyform DER -pubin -out data.encrypted

Asymmetric encryption workflow

Asymmetric encryption workflow using KMS data key pair with S3 as storage option

How to Decrypt using asymmetric data key pair

# Decode the base64 encrypted private data key and save in a file
cat private_key_cipher.base64 | base64 --decode > private_key.cipher

# Use above file to get plaintext private data key from KMS. Remember to pass the correct symmetric CMK key id
aws kms decrypt --key-id alias/twc/cmk --ciphertext-blob fileb://private_key.cipher --output text --query Plaintext > private_key_plaintext.base64

# Decode this base64 plaintext private key and save in a file
openssl enc -d -base64 -A -in private_key_plaintext.base64 -out private_key.der

# Finally decrypt the data
openssl rsautl -decrypt -in data.encrypted -inkey private_key.der -keyform DER -out recoverdata.txt

Asymmetric decryption workflow

Asymmetric decryption workflow using KMS data key pair with S3 as storage option

How to sign and verify messages using AWS KMS

In the context of data-in-transit, a very important use case is to first ensure the message (or document) received is the same as the source (not a single bit was changed) and secondly, the message was sent by the person who claims to be the source. Digital signature and verification solves both of these requirements of message integrity and trusted identity. KMS provides a way for users to perform digital signature and verification of messages either in the AWS realm through CMK itself or outside AWS using generated data key pair.

# Create an asymmetric CMK to sign and verify. This can't be used for encryption and decryption.
aws kms create-key --description "SignAndVerify" --key-usage SIGN_VERIFY --customer-master-key-spec RSA_4096

# Above command produces following output
{
    "KeyMetadata": {
        "AWSAccountId": "2008XXXXXXXX",
        "KeyId": "1f5ea5c2-f216-4dcc-9145-99323ec2cb86",
        "Arn": "arn:aws:kms:ap-south-1:2008XXXXXXXX:key/1f5ea5c2-f216-4dcc-9145-99323ec2cb86",
        "CreationDate": "2020-12-19T17:25:35.168000+05:30",
        "Enabled": true,
        "Description": "ToSignAndVerify",
        "KeyUsage": "SIGN_VERIFY",
        "KeyState": "Enabled",
        "Origin": "AWS_KMS",
        "KeyManager": "CUSTOMER",
        "CustomerMasterKeySpec": "RSA_4096",
        "SigningAlgorithms": [
            "RSASSA_PKCS1_V1_5_SHA_256",
            "RSASSA_PKCS1_V1_5_SHA_384",
            "RSASSA_PKCS1_V1_5_SHA_512",
            "RSASSA_PSS_SHA_256",
            "RSASSA_PSS_SHA_384",
            "RSASSA_PSS_SHA_512"
        ]
    }
}

KMS provides a single key id or alias which internally references both public and private key

# To sign any data, we need to use "sign" KMS API, then base64 decode and save it in a file
aws kms sign --key-id 1f5ea5c2-f216-4dcc-9145-99323ec2cb86 --message fileb://document.txt --signing-algorithm RSASSA_PKCS1_V1_5_SHA_256 --output text --query Signature | base64 --decode > signature

# Now, the sender will encrypt "document.txt" with the receiver's public key.
# Then send both encrypted message and signature to an AWS user with access to kms:Verify API
# To verify, a receiver will first use their private key to decrypt the document
# Then call "verify" KMS API on the document and signature files
aws kms verify --key-id 1f5ea5c2-f216-4dcc-9145-99323ec2cb86 --message fileb://document.txt --signature fileb://signature --signing-algorithm RSASSA_PKCS1_V1_5_SHA_256

#Above command produces the following output
{
    "KeyId": "arn:aws:kms:ap-south-1:2008XXXXXXXX:key/1f5ea5c2-f216-4dcc-9145-99323ec2cb86",
    "SignatureValid": true,
    "SigningAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256"
}

Tip – KMS doesn’t require downloading or sharing a public key to verify a message signed by asymmetric CMK itself. It needs a valid key id (or alias) and type of signing-algorithm used by the sender.

Use cases for using KMS CMK for sign/verify

  • The key access is controlled by IAM and audited by Cloudtrail
  • Can be used with cross-account CMK
  • Following inputs needs to be known to the AWS user verifying the message
    • key-id or key alias
    • signing-algorithm
    • kms:Verify permission on the key
  • Upto 4 KB of message can be signed
  • For data more than 4 KB, AWS recommends the following
    • Sender creates a digest (e.g. SHA256), then creates a signature of the digest.
    • Receiver needs to first create a digest (using the same algorithm) of the data, then verify this hash.
  • My suggestion is to maintain a git repo of cryptography related utility code that is shared among all teams.
  • For large data, a common pattern is to securely uploaded data and signature in a S3 bucket (SSE-S3 enabled), along with key alias and signing-algorithm added in the metadata.

How to sign and verify messages using OpenSSL (or outside KMS)

There are situations, where the person verifying the data is not an AWS user or doesn’t have access to KMS API. In such cases, teams can use KMS as a central service to generate data key pairs and share the public key to the receiver. The signing will be done at your end not using KMS API but some other crypto libraries available to both parties. OpenSSL is one such very popular and commonly used library. Similarly, the receiver will use your public key to verify the message using OpenSSL or any such standard library.

Though KMS doesn’t track data key usage (inside or outside of AWS), it will still log the API call made to generate data key pair.

# Assuming you have generated and saved the data key pair from KMS as shown in the previous example

# Sender signs the message and creates a file called data.signature
openssl dgst -sha256 -sign private_key.der -keyform DER -out data.signature data.txt

# Receiver verifies the message with sender's public key
openssl dgst -sha256 -verify public_key.der -keyform DER -signature data.signature data.txt

# above command produces (if the message integrity is not compromised and the sender is the right entity)
Verified OK

# if in case the data.txt was compromised or the public key is different/invalid then OpenSSL will show the following output
Verification Failure
digital sign and verify workflow

References

  1. https://github.com/awsdocs/aws-kms-developer-guide/blob/master/doc_source/importing-keys-encrypt-key-material.md
  2. https://awscli.amazonaws.com/v2/documentation/api/latest/reference/kms/index.html
  3. https://docs.aws.amazon.com/kms/latest/developerguide/symm-asymm-choose.html#key-spec-rsa
  4. https://docs.aws.amazon.com/kms/latest/developerguide/download-public-key.html
  5. https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#data-key-pairs
  6. https://www.openssl.org/docs/man1.0.2/man1/openssl-dgst.html

Thanks for reading, welcome your feedback.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s