Flow Security Keys

Flow Security Keys

Published:

Amazon Connect Encryption Setup and Lambda Decryption

Amazon Connect provides the ability to capture customer DTMF input and encrypt this for security and compliance reasons. This can be used to collect data of any type with common use case being collecting card numbers and CVV codes for payments. In this post we're going to show you how to get set-up to collect this information, encrypt it within the Contact Flow and then decrypt it with an AWS Lambda Function.

Each Amazon Connect instance provides a set of sample contact flows, one such flow is called Sample secure input with no agent. This blog extends this to flow to use encryption.

Part 1: Creating Certificates on macOS

Step 1: Setup Working Directory

1mkdir ~/amazon-connect-certs
2cd ~/amazon-connect-certs

Step 2: Generate Private Key

1openssl genrsa -out private_key.pem 2048

Step 3: Create X.509 Certificate

1openssl req -new -x509 -key private_key.pem -out certificate.pem -days 365 \
2  -subj "/C=UK/ST=England/L=London/O=MyOrganization/OU=IT/CN=amazon-connect-encryption"

Step 4: Extract Public Key

1openssl x509 -in certificate.pem -pubkey -noout > public_key.pem

Step 5: Verify Files Created

1ls -la *.pem
2# Should show: certificate.pem, private_key.pem, public_key.pem

Step 6: Upload Public Key to Amazon Connect

  1. Go to Amazon Connect Console → FlowsFlow Security keys
  2. Click Add key
  3. Copy and paste the public key
1cat public_key.pem

4. Click Add

This will key you a Key Id

Amazon Connect Key Id
Amazon Connect Key Id

Step 7: Store Private Key Securely

Create a parameter store item using secure string that has the private_key.pem contents. You can use the AWS Console or using CLI. Ensure that you replace {key-id} with the Key Id provided in the step above.

1aws ssm put-parameter \
2  --name "/amazon-connect/encryption/{key-id}/private-key" \
3  --description "Private key for Amazon Connect encryption" \
4  --value "$(cat private_key.pem)" \
5  --type "SecureString" \
6  --overwrite

Step 8: AWS Lambda

Ensure that you have deployed the ECR images as provided by the Blog ECR repo: This blog requires the connect-encryption function, and that this is deployed as per the README.

Make sure that you have added the AWS Lambda function to the Amazon Connect console:

  1. Go to Amazon Connect Console → FlowsAWS Lambda
  2. Choose connect-encryption and click Add Lambda Function

Step 9: Configure Connect Flow

I have used the Sample secure input with no agent flow and adapted this. You can access the flow here and then use 'import' and change it as follows.

Store Customer Input
Store Customer Input

  1. Update the Key Id
  2. In certificate field, paste the results from the command below and Save
1cat certificate.pem

3. Open the next Flow block, AWS Lambda. From the Function ARN dropdown select your AWS Lambda function, connect-encryption, and Save.

4. Publish the flow, assign a phone number and then test.

Part 2: Testing

Place a call into your Contact Flow. Enter a neter using DTMF when prompted. If successful you will hear the message "The encrypted customer..." If you hear "There was a problem..." Then you need to review the guide and look at the logs to help identify where the error might be.

Troubleshooting:

  1. Check the logs in CloudWatch for
    1. Amazon Connect
    2. Lambda function

Between the two you will be able to find where the issue is.

AWS Lambda Logs

Request

The below shows the encrypted entry

1{
2  "Details": {
3      "ContactData": {
4          "Attributes": {},
5          "AwsRegion": "eu-west-2",
6          "Channel": "VOICE",
7          "ContactId": "1dec6044-d140-4092-be55-c67956177378",
8          "CustomerEndpoint": {
9              "Address": "+440000000000",
10              "Type": "TELEPHONE_NUMBER"
11          },
12          "CustomerId": null,
13          "Description": null,
14          "InitialContactId": "1dec6044-0000-0000-0000-c67956177378",
15          "InitiationMethod": "INBOUND",
16          "InstanceARN": "arn:aws:connect:eu-west-2:674248749882:instance/695202c7-0000-0000-0000-7a0bf91dfd94",
17          "LanguageCode": "en-US",
18          "MediaStreams": {
19              "Customer": {
20                  "Audio": null
21              }
22          },
23          "Name": null,
24          "PreviousContactId": "1dec6044-0000-0000-0000-c67956177378",
25          "Queue": null,
26          "References": {},
27          "RelatedContactId": null,
28          "SegmentAttributes": {
29              "connect:Purpose": {
30                  "ValueArn": null,
31                  "ValueInteger": null,
32                  "ValueList": null,
33                  "ValueMap": {
34                      "contact-attributes-search": {
35                          "ValueArn": null,
36                          "ValueInteger": null,
37                          "ValueList": null,
38                          "ValueMap": null,
39                          "ValueString": null
40                      }
41                  },
42                  "ValueString": null
43              },
44              "connect:Subtype": {
45                  "ValueArn": null,
46                  "ValueInteger": null,
47                  "ValueList": null,
48                  "ValueMap": null,
49                  "ValueString": "connect:Telephony"
50              }
51          },
52          "SystemEndpoint": {
53              "Address": "+440000000000",
54              "Type": "TELEPHONE_NUMBER"
55          },
56          "Tags": {
57              "aws:connect:instanceId": "695202c7-0000-0000-0000-7a0bf91dfd94",
58              "aws:connect:systemEndpoint": "+440000000000"
59          }
60      },
61      "Parameters": {
62          "key-id": "b1071b33-0944-47f3-945c-098f3725ac1d",
63          "my-secret-string": "AYADeMCUuxaDZI53u3qDEqfpwDUAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREEzcGg4WXc1dU1vTXNjVVlLM1BpTlRBUUtWWWZMWmU2Z05iQkwrekU0aFZFc05GaHMrNWNGTkU0WHhXTzc1SFVRQT09AAEADUFtYXpvbkNvbm5lY3QAJGIxMDcxYjMzLTA5NDQtNDdmMy05NDVjLTA5OGYzNzI1YWMxZAEAsQy+1ukKe7P0fNUfcaxXY/LJC+ClQK9GQQ3xCkAh9b1lnUvAdEqSPeGLzlTpSITR/72Wb4HrO2+YQdfkq69UgfwiJf0XYbZw82pA6SEskhsYTtLvGIq8iOF1u67NY0/BEHmpUz3KGHJPYa6Li2WsynKmlxwCxi7ChB7M5tIXRoN63EjnfeqJ1kzPZtBNVUxxzMyYmOKPKDGpwrA/+wKXj/9sa00E1ik9qqqOOi6FlPqJm3CgY//4UMCmdyksjFb5v3ho4BcUj/Xc15TTqB4isYjqy2zNImViHfIDuRvXmA2yypEHPMJ6IIfQYdm53NglMhn3W9vM7irmbdQ/xkMM5AIAAAAADAAAEADsrpXUgoesZFoxRQj/wTJVdeMXhlHRXyRCVpmG/////wAAAAGcGYwjdd8MWua7xc4AAAAMrrvgHRxRlE6wEH60CLZ2pv5YHbyOcFitE5pg+QBnMGUCMG5Prxlwhm1VftdXLEWf190d1N7BrmyT4oOS7DW06vj9U96OgAxS6Js5mWvdlgVUxwIxAIkRV0f2KACNPUrFpaHMRhGU8ropqYXR2IQWpWyHfTY1nQ4ar1gVN7Hpg9DLp21ZGg=="
64      }
65  },
66  "Name": "ContactFlowEvent"
67}

Response

The response shows the unencrypted data*

1{
2  "status-code": 200,
3  "data": {
4      "success": true,
5      "message": "Data decrypted successfully",
6      "contactId": "1dec6044-d140-4092-be55-c67956177378",
7      "requestId": "9675ddc6-5cb4-4c50-8858-a0c5ce9c0a0d",
8      "decryptedData": "4444444444444444",
9      "dataLength": 16
10  }
11}

Important*: This lambda is for educational purposes. You should never write the unencrypted payload to the logs.

Key rotation

You notice that the certification created is valid for 365 days, this is the case as we passed the param -days 365 when creating the key.

Amazon Connect support 2 keys so that key rotation is supported.

This blog post has been created to support key rotation. For example the KeyId created is passed by the AWS Lambda block in Amazon Connect to the Lambda. This param is then used to retrieve the RSA PRIVATE KEY from the parameters store. Therefore we can create a 2nd KEY and allow both keys to work simultaneously and dynamically. Then retire the first key before it expires.

This process ensure seamless rollback and no downtime in deployments.

Multiple Encryption Requirements

You may have a requirement to support 2 or more encryption consumers, this is a problem due to the limitation of Amazon Connect only allowing 2 Key Ids. With just 2 you cannot support more than 2 and with those key rotation would require downtime.

Therefore the approach in this blog can be adopted to work around this limitation.

Effectively the connect-encryption lambda would become a middleware, decrypting the payload and re-encrypting it for the destination system, this allows you to scale to 10s or 100s of encryption consumers.

I hope you found this educational and useful!