Search code examples
rubyamazon-web-servicesamazon-s3encryptionaes

How to use S3 SSE C (Server Side Encryption with Client Provided Keys) with Ruby


I'm trying to upload a file to S3 and have it encrypted using the SSE-C encryption options. I can upload without the SSE-C options, but when I supply the sse_customer_key options I'm getting the following error:

ArgumentError: header x-amz-server-side-encryption-customer-key has field value "QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0\nnMUE2MTNENDRCOTcxRjA2Qzk1Mg=", this cannot include CR/LF

I'm not sure if the problem is with the key I'm generating or with the encoding. I've played around with different options here, but the AWS documentation is not very clear. In the general SSE-C documentation it says you need to supply a x-amz-server-side​-encryption​-customer-key header, which is described as this:

Use this header to provide the 256-bit, base64-encoded encryption key for Amazon S3 to use to encrypt or decrypt your data.

However, if I look at the Ruby SDK documentation for uploading a file the 3 options have a slightly different description

  • :sse_customer_algorithm (String) — Specifies the algorithm to use to when encrypting the object (e.g.,
  • :sse_customer_key (String) — Specifies the customer-provided encryption key for Amazon S3 to use in
  • :sse_customer_key_md5 (String) — Specifies the 128-bit MD5 digest of the encryption key according to RFC

(I didn't copy that wrong, the AWS documentation is literally half-written like that)

So the SDK documentation makes it seem like you supply the raw sse_customer_key and that it would base64-encode it on your behalf (which makes sense to me).

So right now I'm building the options like this:

  sse_customer_algorithm: :AES256,
  sse_customer_key: sse_customer_key,
  sse_customer_key_md5: Digest::MD5.hexdigest(sse_customer_key)

I previously tried doing Base64.encode64(sse_customer_key) but that gave me a different error:

Aws::S3::Errors::InvalidArgument: The secret key was invalid for the specified algorithm

I'm not sure if I'm generating the key incorrectly or if I'm supplying the key incorrectly (or if it's a different problem altogether).

This is how I'm generating the key:

require "openssl"
OpenSSL::Cipher.new("AES-256-CBC").random_key

Solution

  • Oh, did you notice that your key contains '\n'? That's most probably why you get the CR/LF error: QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0(\n)nMUE2MTNENDRCOTcxRjA2Qzk1Mg=

    As mentioned by the colleague in the comments, strict_encode64 is an option, as it complies to RFC 2045.

    By the way, I got this insight from here: https://bugs.ruby-lang.org/issues/14664

    Hope it helps! :)