5

I’m using serverless-kms-secrets on serverless frameword to set some ENV variables I want to consume using Ruby.

I can confirm that the plugin works perfectly, it generated the file with the encrypted variable and I can see the encrypted variable in my environment on AWS lambda. The problem is that I can’t decrypt it in Ruby. The code that decrypts it (correctly) in the plugin is here, I understand it gets the string saved in the file and encodes it using Base64, so no big deal. In Ruby:

token = "blablabla"
client = Aws::KMS::Client.new(region: 'us-east-1')
blob = Base64.encode64(token)
client.decrypt({ciphertext_blob: blob})
....
Aws::KMS::Errors::InvalidCiphertextException ()

The client should get my credentials automatically, but I’m not sure I understand how the keyArn is used, doesn’t look relevant though.

Does anybody have any idea how to solve this?

ngw
  • 1,222
  • 1
  • 14
  • 34

2 Answers2

2

Instead of encoding the string I had to decode it.

token = "blablabla"
client = Aws::KMS::Client.new(region: 'us-east-1')
blob = Base64.decode64(token)
client.decrypt({ciphertext_blob: blob})
ngw
  • 1,222
  • 1
  • 14
  • 34
1

It looks like the ciphertext_blob argument in Aws::KMS::Client#decrypt expects a binary string that includes the encrypted Ciphertext that you want to decrypt.

In your example, you are passing in an unencrypted Base64 encoded string into decrypt. Instead, you need to to pass in an encrypted binary string.

To get an encrypted string we can call Aws::KMS::Client#encrypt with your keyId (also know as your ARN) and the string you want to encrypt in plaintext.

In the response from that call we get back a ciphertext_blob which is the encrypted binary string that we need to use in order to decode.

Sometimes you might see that binary data "unpacked", which you can demonstrate doing ciphertext_blob.unpack('H*'). If you have unpacked data and want to decrypt it, you will need to pack it: encrypted_upacked_blob.pack('H*').

Here's a full example of a round trip encoding and decoding of a plaintext string:

require 'aws-sdk-kms'

client = Aws::KMS::Client.new

key_id = 'arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab'

plaintext_to_encrypt = 'blablabla'

encrypt_response = client.encrypt({
  key_id: key_id,
  plaintext: plaintext_to_encrypt,
})

encrypt_response.ciphertext_blob
# => "\x01\x02\x02\x00xt/Jyu\x85B\xCA\x16v\xDAa3DM1$\e8Y\xF9\x812\x1E\xA9\xD3\xE3R\x1E/}\xCA\x...

encrypted_upacked_blob = encrypt_response.ciphertext_blob.unpack('H*')
# => ["0102020078742f4a79758542ca1676da6133444d31241b3859f981321ea9d3e3521e2f7dca01a7f89f2ee03...

encrypted_packed_blob = encrypted_upacked_blob.pack('H*')
# => "\x01\x02\x02\x00xt/Jyu\x85B\xCA\x16v\xDAa3DM1$\e8Y\xF9\x812\x1E\xA9\xD3\xE3R\x1E/}\xCA\x...

decrypt_response = client.decrypt({
  ciphertext_blob: encrypted_packed_blob
})

decrypted_plaintext = decrypt_response.plaintext
# => "blablabla"

This example combines two examples provided by AWS: Encrypting Data in AWS KMS using Ruby SDK and Decrypting a Data Blob in AWS KMS.

For a better understand of what [blob].pack("H*") is doing, check out this StackOverflow post and Ruby's Array#pack documentation.

Scott Bartell
  • 2,801
  • 3
  • 24
  • 36
  • Nothing changes, still `Aws::KMS::Errors::InvalidCiphertextException ()` – ngw Mar 18 '19 at 15:24
  • @ngw it looks like you were using an unencrypted string when it needs to be encrypted. I updated my answer to take that into account and provided a full example that I just tested. – Scott Bartell Mar 21 '19 at 08:17
  • Nope, it’s encrypted by the node.js library – ngw Mar 21 '19 at 23:25
  • @ngw okay, how are you getting it from the node.js library to Ruby? – Scott Bartell Mar 22 '19 at 00:58
  • I just followed the instructions on https://github.com/nordcloud/serverless-kms-secrets. I have both a yaml file with the encrypted value in and the same thing in my ENV. I'm sure it's encrypted correctly because I can decrypt it using js. – ngw Mar 22 '19 at 14:34