Using AWS KMS Key for Message Signing Up to 4096 Bytes

This article explains how AWS KMS can be used for message singing upto 4096 bytes. Message signing creates a digital signature of the message and can be used to verify the message authenticity. Sometimes the customer receive error message “Signature Verification Failure”. This article explains possible reasons for this error message and how to resolve it.

What is KMS Sign?

You can use AWS KMS Sign API to create a digital signature for a message by using the private key in an asymmetric signing KMS key. To verify the signature, use the Verify operation, or use the public key in the same asymmetric KMS key outside of AWS KMS.

Digital signatures are generated and verified by using asymmetric key pair, such as an RSA or ECC pair that is represented by an asymmetric KMS key. The key owner (or an authorized user) uses their private key to sign a message. Anyone with the public key can verify that the message was signed with that particular private key and that the message hasn’t changed since it was signed.

To use the Sign operation, provide the following information:

  • Use the KeyId parameter to identify an asymmetric KMS key with a KeyUsage value of SIGN_VERIFY.
  • Use the Message parameter to specify the message.
  • Choose a signing algorithm that is compatible with the KMS key.

Important Note: When signing a message, be sure to record the KMS key and the signing algorithm. This information is required to verify the signature.

As we will be using a KMS CMK key for message signing, you need to either generate a new KMS CMK key or use an existing key. Please confirm the key usage is SIGN_VERIFY. If you accidentally select any other key usage type such as ENCRYPT_DECRYPT and GENERATE_VERIFY_MAC, you will not be able to create the digital signature and sign the message.

In this article we will be generating a new RSA KMS CMK key.

Generating an new RSA KMS CMK key.

You can use “create-key” API to create a KMS key.

aws kms create-key \
--key-usage SIGN_VERIFY \
--customer-master-key-spec RSA_2048

This would generate the following key output.

{
"KeyMetadata": {
"AWSAccountId": "690900989641",
"KeyId": "f0182183-e5e3-4518-99d4-647d6cc9494f",
"Arn": "arn:aws:kms:us-east-1:690900989641:key/f0182183-e5e3-4518-99d4-647d6cc9494f",
"CreationDate": "2023-04-21T14:42:35.021000-07:00",
"Enabled": true,
"Description": "",
"KeyUsage": "SIGN_VERIFY",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "RSA_2048",
"KeySpec": "RSA_2048",
"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"
],
"MultiRegion": false
}
}

You can also create and an alias for the KMS key for simplicity. If successful, the below command will display no output.

aws kms create-alias \
--alias-name alias/message-signing \
--target-key-id f0182183-e5e3-4518-99d4-647d6cc9494f

Let us also create a message file called “message.txt” which we will be using for signing. The message file can contain any data of user preference. You can also provide the message on the terminal when you are keying the CLI command, but if the message is large, its recommended to use a file.

Signing a message up to 4096 bytes.

If the message size is less than 4096 bytes the message is considered RAW. You can simply call the KMS sign command as below to generate the digital signature. In this article we will be using RSA SHA256 as the signing algorithm.

aws kms sign \
--key-id alias/message-signing \
--signing-algorithm RSASSA_PSS_SHA_256 \
--message-type RAW \
--message fileb://message.txt (or path/to/file/name)

The Signature is always returned as a base64 encoded string. i.e. the AWS CLI does not base64 decode it for us.

Output:
{
"KeyId": "arn:aws:kms:us-east-1:690900989641:key/f0182183-e5e3-4518-99d4-647d6cc9494f",
"Signature": "RtB0KL0PSn6xvFsxS9dP65xgLvn1vSM12Yza8o/cOTSloFFejyMjylf/viEmUZ72OGE3yVrb/vgMXbYft9KL+fv5oGujMIll/UrnAFJFRs/fBwO+Z1za/Egp173plWcj23+ocjL4C0DX2aIAdL8jLoW5RTJBsrzuYVBa6KA7uolT40SIjtLMvufPMQxRjtehS8fhLhvRjr/Mda8puzik3zNF2nEx3JFaeXdTLNGrJpMi97TY4oK/MCpc8sl2o5EB4FnWHPzrERpPiA0+svgaj6mIpzrv1aOBJSw6N+p31jvXFuE5BZc3Ui6wNhcY8nAIgHTRNUs2APRxvT8AmgcZmA==",
"SigningAlgorithm": "RSASSA_PSS_SHA_256"
}

You can also save the signature directly to a file using the below CLI command.

aws kms sign \
--key-id alias/message-signing \
--signing-algorithm RSASSA_PSS_SHA_256 \
--message-type RAW \
--message fileb://message.txt \
--output text --query Signature | base64 --decode > signature-raw.txt

You can verify the digital signature that was generated as below:

aws kms verify \
--key-id alias/message-signing \
--signing-algorithm RSASSA_PSS_SHA_256 \
--message-type RAW \
--message fileb://message.txt \
--signature fileb://signature-raw.txt

As you can verify in the output, the signature valid is indicated as “True”.

Output:
{
"KeyId": "arn:aws:kms:us-east-1:690900989641:key/f0182183-e5e3-4518-99d4-647d6cc9494f",
"SignatureValid": true,
"SigningAlgorithm": "RSASSA_PSS_SHA_256"
}

If you want to verify the message externally to AWS, you can pass the following to your end user and the end user can verify the authenticity and integrity of the message and confirm the message hasn’t changed since it was signed.

  • the original message file i.e message.txt
  • the signature file i.e signature-raw.txt
  • signing algorithm i.e RSASSA_PSS_SHA_256
  • the public key of the KMS key

While there is no way to export the Private Key from KMS, you can download the public key form the KMS console or via get-public-key CLI API call.

The user can verify the input data i.e the message file (which must be a hash) against the signature file and indicate if the verification succeeded or failed. Before the end user can verify the message signature, the user need to create a hash of the message because openssl verify command only accepts hash digest of a file and AWS CLI only accepts the digest in unencoded binary format. Hence the user need to perform the hash as below:

openssl dgst -sha256 -binary message.txt > message-hash.txt

Now the user can run the below OpenSSL command to verify the signature against the hashed content of the original message file.

openssl pkeyutl -verify -pubin -keyform PEM -inkey message-signing-public-key.pem \
-pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \
-in message-hash.txt -sigfile signature-raw.txt
Output:
Signature Verified Successfully

If there is any mistake in the process the output would indicate “Signature Verification Failure” error message.

Leave a Comment

Your email address will not be published. Required fields are marked *