Off the back of local-kms, I've been getting a few questions regarding how to interact with it via the CLI. So here are a few examples of how you can use AWS KMS (or local-kms) via the CLI.
The examples here focus on demonstrating how to use AWS KMS, not as examples of how to perform 'good' encryption. Please don't use these snippets in production systems unless you know what you're doing.
AWS KMS supports both message signing and encryption operations using RSA keys; however a single key can only support one of these two operations. RSA keys can be created with a key size of 2048, 3072 or 4096 bits.
aws kms create-key \
--key-usage SIGN_VERIFY \
--customer-master-key-spec RSA_2048
You must select the --key-usage
at the point of creation. You will only be able to use
the generated key for the selected usage. You cannot change this once the create has been created.
This returns the key details:
{
"KeyMetadata": {
"AWSAccountId": "111122223333",
"KeyId": "5211d781-06f0-4062-82a1-0b5e8a99c468",
"Arn": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"CreationDate": 1591088481.619,
"Enabled": true,
"Description": "",
"KeyUsage": "SIGN_VERIFY",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "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"
]
}
}
All RSA key specs support multiple Signing Algorithms (unlike ECC keys). An explanation of them can be found in AWS' documentation here: RSA key specs for signing and verification
In all cases, when KMS is signing a message, it is in fact always signing the digest of that message, generated via a SHA hash function.
KMS supports two options for generating the digest of a message - you can generate it yourself in advance or, if your message is less than or equal to 4096 bytes, you can have KMS generate the digest for you.
To have KMS sign the message Hello
, you can call:
aws kms sign \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message Hello
Note that we must pass a signing algorithm that was listed under SigningAlgorithms
when the key was created. KMS will automatically hash our message using SHA-384 for us in this example.
This returns
{
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"Signature": "sgZbDf3jCCU0dbbYucn+YXOCX7UVCTMb+13OVcNvOLeo36IueNu6v7zUYtSjd4B7cAaprgyQ6wiOD+St2q6R9Ggbnhm3SGWKQdMNu1FC8+MK8yQRLlnX6CtYZDnAaosGqh7yBZHi8625Maj0sqC8I8Lel6f0s4fthBAfaJyuD+i53LHOTdCDNnHbbLMHCDtDjZO1j0XpYB9eIkeaq4d7YDjpNjqF7LzHO2pA6EDm7HlJZHGwKrZ02laEjtm2LYajlqHBvZCDeNfiW6l88ZugtENOKO4s/ti8+VSGRbTgh2eRCjgyC5ZGMaHiuN6andcgHJyfdxPmK0oKMYDoUIzwRQ==",
"SigningAlgorithm": "RSASSA_PSS_SHA_384"
}
The message's signature is returned, along with the Key ARN and the Signing Algorithm. The Signature is always returned as a base64 encoded string. i.e. the AWS CLI does not base64 decode it for us.
It's important to note that you'll need to pass the KeyId and SigningAlgorithm when Verifying the message with KMS, so keep a note of these too. It's especially important to record the SigningAlgorithm used with RSA keys as, unlike ECC keys, it cannot be derived from the KeyId.
You can save the signature directly to a file using:
aws kms sign \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message Hello \
--output text --query Signature | base64 --decode > signature-raw.sign
The reason we base64 decode the message here is that when passing it back to KMS for verification, the AWS CLI expects it unencoded.
We need to perform the extra hashing step ourselves in this context. This has two advantages:
When we hash the message ourselves, we must match the hash size with that of the signing algorithm we wish to use.
RSASSA_*_SHA_256
--> SHA-256
RSASSA_*_SHA_384
--> SHA-384
RSASSA_*_SHA_512
--> SHA-512
So for example, we can use OpenSSL to hash our message with:
echo -n Hello | openssl dgst -sha384
Which gives us:
3519fe5ad2c596efe3e276a6f351b8fc0b03db861782490d45f7598ebd0ab5fd5520ed102f38c4a5ec834e98668035fc
The AWS CLI actually wants the digest in unencoded binary however, so we can do:
echo -n Hello | openssl dgst -sha384 -binary > message.sha384
We can then sign the message similarly to how we did before:
aws kms sign \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message-type DIGEST \
--message fileb://message.sha384
In this instance we must specify that the message type as DIGEST
,
and we pass the binary digest in from the pre-generated file as the message.
This gives us:
{
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"Signature": "o2rp8HioAuih5dbSHTRxrTFzEUULx/mZr+xOUUpR08gW/iSb3hUbDrml+M7noXvxOqb3LeDd4EdEER3RbCaqHEq2XJw+HwYZ7SYNcinda4AUnp7EsQDbsJNPJr/bDnFC26CrCpVinHYKgzVdYrkOZ84p08yRrAR0i3hQYET3pIexcKDyCGfGNX1QSkTgVoLsqw6vixTau8sNcyRI0WWVwmdHP5q7/4CBw7b7Xs7MyRjoCS6vxGkFJvARUfDop6eEH7jHlyRPjgpTUJRheEjr0BNdLjXL0KMt3+eEFbLiaysmV8zPqfe6nJFRiRFspRQshbMVWCd7WYziPGe0F7sPhw==",
"SigningAlgorithm": "RSASSA_PSS_SHA_384"
}
As before we can generate the signature and save it directly to a file:
aws kms sign \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message-type DIGEST \
--message fileb://message.sha384 \
--output text --query Signature | base64 --decode > signature-digest.sign
To verify a message we must pass the same KeyId, Signing Algorithm and Message passed when we signed the message, plus the signature that was returned:
aws kms verify \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message Hello \
--signature fileb://signature-raw.sign
KMS will then verify that the signature passed is valid for the given combination of Key and Message:
{
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"SignatureValid": true,
"SigningAlgorithm": "RSASSA_PSS_SHA_384"
}
If the verification fails for any reason, KMS throws a KMSInvalidSignatureException
.
This works exactly as the RAW message type, other than we set --message-type DIGEST
, and we pass in the binary message digest.
aws kms verify \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--signing-algorithm RSASSA_PSS_SHA_384 \
--message-type DIGEST \
--message fileb://message.sha384 \
--signature fileb://signature-digest.sign
{
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"SignatureValid": true,
"SigningAlgorithm": "RSASSA_PSS_SHA_384"
}
Whilst there is no way to export the Private Key from KMS, you can export the Public Key and use it to verify messages locally that have been signed by KMS.
To export the public key we use:
aws kms get-public-key \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468
Which gives us:
{
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/5211d781-06f0-4062-82a1-0b5e8a99c468",
"PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1q5Pb4c+6eJe+iwkaC8V3ZLrJdZrf6xm+8+OApl25tjAh5ewj8OebriPM/8LzHDKJ66pekGR2AAv/PHcmIPkmJPui5iGxjpvJZrS+Yzwg4jBb3da5wqenZ/xYpw6aIZv1AI7F2vWUtS6Om/4MiZEr0AarA5UC05rEN9GdJGB25Rkozfh+Okqn1vDz9cnMUUdPDN4fJF4r+Qno/e0Jk8tN69ofkbx2IWCFPcy/SyeSnoKyJ4ebdTz70rbijdTYxp4XZ2z7nJsGgmU827y/ntmDJYyYrYpIiz/ZIqHS8zynhkci5l87n032OJCizHfIQ/stGuWlGSzhbQtoJgA9lFeMwIDAQAB",
"CustomerMasterKeySpec": "RSA_2048",
"KeyUsage": "SIGN_VERIFY",
"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"
]
}
PublicKey
here is a DER-encoded X.509 public key, base64 encoded.
We can save this to a file using:
aws kms get-public-key \
--key-id 5211d781-06f0-4062-82a1-0b5e8a99c468 \
--output text --query PublicKey | base64 --decode > key.der
Many applications for verifying signatures support DER encoded keys, however if needed you can convert it to a PEM encoding using:
openssl rsa -inform DER -in key.der -pubin > key.pem
If you used KMS to generate the message's digest originally, you will now need to generate a version yourself. OpenSSL's dgst
function directly
supports generating a digest and verifying it in a single command. There are two variations, one for each RSA padding option, as specific in the signing algorithm.
For RSASSA_PSS_SHA_384
:
echo -n Hello | openssl dgst -sha384 -sigopt rsa_padding_mode:pss -keyform DER -verify key.der -signature signature-raw.sign
For RSASSA_PKCS1_V1_5_SHA_384
:
echo -n Hello | openssl dgst -sha384 -sigopt rsa_padding_mode:pkcs1 -keyform DER -verify key.der -signature signature-raw.sign
If you generated the digest yourself originally, and still have it, you can use OpenSSL's pkeyutl
function.
For RSASSA_PSS_SHA_384
:
openssl pkeyutl -verify -pubin -keyform DER -inkey key.der \
-pkeyopt digest:sha384 -pkeyopt rsa_padding_mode:pss \
-in message.sha384 -sigfile signature-digest.sign
For RSASSA_PKCS1_V1_5_SHA_384
:
openssl pkeyutl -verify -pubin -keyform DER -inkey key.der \
-pkeyopt digest:sha384 -pkeyopt rsa_padding_mode:pkcs1 \
-in message.sha384 -sigfile signature-digest.sign