Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypting values using Amazon KMS, storing / retrieving using DynamoDB with Lambda (NodeJS)

I have a Lambda(NodeJS) function that writes data to DynamoDB. Some of that data needs to be encrypted. I'm encrypting using KMS encrypt and storing. When I retrieve from Dynamo using a different Lambda function and try to decrypt, I get an error. If I encrypt and then turn around a decrypt, I'm able to do that, but if I'm reading the encrypted value from the DB, it won't decrypt. My encrypt/store code is below:

console.log('Loading event');

var AWS = require('aws-sdk');

var keyId = "arn:aws:kms:us-east-1:5423542542:key/xxxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxx";
var tableName = "person";
var dynamoDBConfiguration = {
    "region": "us-west-2"
};
AWS.config.update(dynamoDBConfiguration);
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var kms = new AWS.KMS({region: 'us-east-1'});
var newId = "1234-56789-101112-13141516";
var item = {};

exports.handler = function (event, context) {
    console.log('ssn');
    //encrypt it
    var ssnParams = {
        KeyId: keyId,
        Plaintext: "123-45-6789"
    };
    kms.encrypt(ssnParams, function (err, data) {
        if (err) {
            console.log(err, err.stack);
        }
        else {
            console.log(' ssn encrypted');

            var enc_ssn = data.CiphertextBlob;
            item["SSN"] = {"Value": {"B": enc_ssn}};
            item["First_Name"] = {"Value": {"S": "Joe"}};
            item["Last_Name"] = {"Value": {"S": "Blow"}};
            dynamodb.updateItem({
                "TableName": tableName,
                "AttributeUpdates": item,
                "ReturnValues": "ALL_NEW",
                "Key": {
                    "id": {"S": newId}
                }

            }, function (err, data) {
                if (err) {
                    context.done(err);
                }
                else {
                    console.log('great success: %j', data);
                    context.succeed("Person Successfully Inserted");
                }
            });
        }
    });
};

My retrieval/decrypt code is as follows:

console.log('Loading event');
var AWS = require('aws-sdk');
var dynamoDBConfiguration = {
    "region": "us-west-2"
};
AWS.config.update(dynamoDBConfiguration);
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var keyId = "arn:aws:kms:us-east-1:5423542542:key/xxxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxx";
var tableName = "person";
var kms = new AWS.KMS({region: 'us-east-1'});

exports.handler = function (event, context) {
    console.log(JSON.stringify(event, null, '  '));
    var params = {};
    var id = event.id;
    console.log(id);
    if (id && id !== '') {
        params = {
            "TableName": tableName,
            KeyConditionExpression: "id = :id",
            ExpressionAttributeValues: {
                ':id': {'S': id}
            }
        };
        dynamodb.query(params, function (err, data) {
            if (err) {
                context.done(err);
            }
            else {
                var person = data.Items[0];
                console.log('query success');
                console.log(person);
                if (person.SSN) {
                    console.log('have ssn');
                    var b_ssn = person.SSN;
                    console.log(b_ssn);
                    person.SSNtext = "";
                    var encryptedParams = {
                        CiphertextBlob: Buffer(b_ssn, 'base64'),
                    };
                    kms.decrypt(encryptedParams, function (err, decrypteddata) {
                        if (err) {
                            console.log(err, err.stack);
                            //context.done(err);
                        }
                        else {
                            person.SSNtext = decrypteddata.Plaintext.toString();
                            console.log(decrypteddata.Plaintext.toString());
                            context.succeed(person);
                        }
                    });
                }
            }
        });
    }
    else {
        params = {
            "TableName": tableName
        };
        dynamodb.scan(params, function (err, data) {
            if (err) {
                context.done(err);
            }
            else {
                console.log('scan success');
                context.succeed(data);
            }
        });
    }
};

When I run this code, I get the following error:

START RequestId: 639590ac-cb95-11e5-91e4-d706c725f529 Version: $LATEST
2016-02-04T23:16:58.713Z    639590ac-cb95-11e5-91e4-d706c725f529    Loading event
2016-02-04T23:17:00.215Z    639590ac-cb95-11e5-91e4-d706c725f529    {
  "id": "1234-56789-101112-13141516"
}
2016-02-04T23:17:00.215Z    639590ac-cb95-11e5-91e4-d706c725f529    1234-56789-101112-13141516
2016-02-04T23:17:00.954Z    639590ac-cb95-11e5-91e4-d706c725f529    query success
2016-02-04T23:17:00.954Z    639590ac-cb95-11e5-91e4-d706c725f529    { Last_Name: { S: 'Blow' },
  id: { S: '1234-56789-101112-13141516' },
  First_Name: { S: 'Joe' },
  SSN: { B: <Buffer 0a 20 ec 00 75 21 f2 61 7d ba 2e 38 7e c6 fd 24 6d 32 b4 c2 b3 29 47 9e 9b 97 f2 a8 46 f2 d0 38 da 37 12 92 01 01 01 02 00 78 ec 00 75 21 f2 61 7d ba 2e ...> } }
2016-02-04T23:17:00.956Z    639590ac-cb95-11e5-91e4-d706c725f529    have ssn
2016-02-04T23:17:00.956Z    639590ac-cb95-11e5-91e4-d706c725f529    { B: <Buffer 0a 20 ec 00 75 21 f2 61 7d ba 2e 38 7e c6 fd 24 6d 32 b4 c2 b3 29 47 9e 9b 97 f2 a8 46 f2 d0 38 da 37 12 92 01 01 01 02 00 78 ec 00 75 21 f2 61 7d ba 2e ...> }
2016-02-04T23:17:01.573Z    639590ac-cb95-11e5-91e4-d706c725f529    { [InvalidCiphertextException: null]
  message: null,
  code: 'InvalidCiphertextException',
  time: Thu Feb 04 2016 23:17:01 GMT+0000 (UTC),

I can encrypt and decrypt the encrypted value, but when I store the value, retrieve it and try to decrypt it, it fails. Any help would be greatly appreciated.

like image 581
scoDubblT Avatar asked Feb 04 '16 23:02

scoDubblT


1 Answers

Okay - I've got this working and I wanted to post here in case someone else may be struggling with the same thing. When you put data into DynamoDB, you use something like this:

item["First_Name"] = {"Value":{"S": "Joe"}};

when I retrieved it, I didn't get a string back, I got an object. So when I have a row called person that I just retrieved, I have to then get to the value like this:

first_name = person.First_Name.S;
//results in first_name = "Joe";

So the issue I was having is that I was trying to pass the object person.First_Name to the decrypt method and not the value of person.First_Name.S

like image 124
scoDubblT Avatar answered Oct 08 '22 18:10

scoDubblT