0

I am trying to decrypt a parameter stored on SSM that is encrypted with a user managed KMS key, which I just created.

  • This post uses outdated methods
  • This post says that the context used on creation should be also used on decryption with the option EncryptionContext. But when I created the key and the parameter I did not used a context. I also checked on CloudTrail and there's no information about context. And I also didn't find any place to declare a context when creating a new parameter.
  • There is no example in the examples repo

This Lambda is being executed with the correct permissions to Decrypt with the key and to Read from the SSM parameter store.

I am sure the parameter is fetched correctly, because I am able to retrieve the stored parameter if I do not encrypt it with the KMS key.

I also tryied using another library base64-js to encrypt the string to Uint8Array, but the result is the same.

This is the sample code:

import { DecryptCommand, KMSClient } from '@aws-sdk/client-kms';
import { GetParameterCommand, SSMClient } from '@aws-sdk/client-ssm';

const kmsClient = new KMSClient({ region: process.env.REGION });
const ssmClient = new SSMClient({ region: process.env.REGION });

try {
    const response = await ssmClient.send(new GetParameterCommand({
        Name: `/path/to/param`
    }));
    // Value below verified without KMS key
    const sureItIsValid = response.Parameter?.Value as string

    // Obtained the same result for buff using base64-js lib
    const buff: Uint8Array = Buffer.from(sureItIsValid, 'base64');
    const command = new DecryptCommand({
        CiphertextBlob: buff,
        // The KeyId was also verified using the alias
        KeyId: 'arn:aws:kms:<REGION>:...',
    });

    const secrets = await kmsClient.send(command);

    console.error('result');
    console.log(secrets.Plaintext?.toString());
  } catch (error) {
    console.error('error');
    console.error(JSON.stringify(error));
  }

And I get:

ERROR   error
ERROR   {"name":"InvalidCiphertextException","$fault":"client","$metadata":{"httpStatusCode":400,"requestId":"the-request-id","attempts":1,"totalRetryDelay":0},"__type":"InvalidCiphertextException","message":"UnknownError"}
fedonev
  • 20,327
  • 2
  • 25
  • 34
Kmelow
  • 203
  • 1
  • 3
  • 12

1 Answers1

3

Add WithDecryption: true to your GetParameterCommand. SSM will call KMS to decrypt* the SecretString paramter and return the plaintext to us in Parameter.Value:

const command = new GetParameterCommand({
    Name: '/path/to/param',
    WithDecryption: true,
});

* You are using the CDK to handle your Lambda permissions, so the following will work:

param.grantRead(func); // let your Lambda function read the SSM Parameter
key.grantDecrypt(func); // let your Lambda Function decrypt the SSM Parameter
fedonev
  • 20,327
  • 2
  • 25
  • 34
  • Nice, thanks for the `WithDecryption: true` I didn't thought of that. Now, about the policy I gave it to the Lambda Function that is handling the request `kms.key.grantDecrypt(handler)`, which correctly gives it permission to decrypt acording to the console and CF. When creating the `const ssm = StringParameter.fromSecureStringParameterAttributes(...)` I pass the key in `encryptionKey`. And I also give the Lambda the right to read from ssm `ssm.grantRead(handler)`. In the AWS Console I can see the lambda has permission to `kms:Decrypt` – Kmelow Oct 25 '22 at 17:45
  • I tryied with the `WithDecryption: true` param, but still get `{"name":"InvalidCiphertextException","$fault":"client","$metadata":{"httpStatusCode":400,"requestId":"the-request-id","attempts":1,"totalRetryDelay":0},"__type":"InvalidCiphertextException","message":"UnknownError"}` – Kmelow Oct 25 '22 at 17:47
  • @Kmelow - Your CDK Lambda permissions look fine. Can you confirm your parameter is a `SecureString`and uses the KMS KeyId in your stack? Note that CDK can neither modifiy the parameter nor set its encryption key. You must set the encryption key on the parameter using the API or Console. – fedonev Oct 25 '22 at 21:36
  • yes I created the key using the Console as a `SecureString` (the creation was successful) and made sure the key to encrypt is the same I am using in my CDK. I also tested using a string and a `base64` string on the parameter value – Kmelow Oct 26 '22 at 08:01
  • @Kmelow: Can you confirm that your Lambda's `GetParameter` call \*without decryption\* returns a ciphertext token in `Parameter.Value`? Can you decrypt the token locally with the CLI + user creds? Can you `get-parameter --with-decryption` locally from the CLI? – fedonev Oct 26 '22 at 09:17