How to use S3 SSE C (Server Side Encryption with Client Provided Keys) with Ruby - 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

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! :)

First of all, please make sure that you are using the latest version of the SDK (2.2.2.2) from here
So, As I understand while we generate the presigned URL, we have to specify the SSECustomerMethod and when consuming the URL, the "x-amz-server-side-encryption-customer-key" header is set with the customer key, you also need to set the "x-amz-server-side-encryption-customer-algorithm" header.
var getPresignedUrlRequest = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = "EncryptedObject",
SSECustomerMethod= SSECustomerMethod.AES256,
Expires = DateTime.Now.AddMinutes(5)
};
var url = AWSClients.S3.GetPreSignedURL(getPresignedUrlRequest);
var webRequest = HttpWebRequest.Create(url);
webRequest.Headers.Add("x-amz-server-side-encryption-customer-algorithm", "AES256");
webRequest.Headers.Add("x-amz-server-side-encryption-customer-key", base64Key);
using (var response = webRequest.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var contents = reader.ReadToEnd();
}

Related

Ruby YouTube Data API v3 insert caption always returns error

I am trying to use the Ruby SDK to upload videos to YouTube automatically. Inserting a video, deleting a video, and setting the thumbnail for a video works fine, but for some reason trying to add captions results in an invalid metadata client error regardless of the parameters I use.
I wrote code based on the documentation and code samples in other languages (I can't find any examples of doing this in Ruby with the current gem). I am using the google-apis-youtube_v3 gem, version 0.22.0.
Here is the relevant part of my code (assuming I have uploaded a video with id 'XYZ123'):
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'google-apis-youtube_v3'
def authorize [... auth code omitted ...] end
def get_service
service = Google::Apis::YoutubeV3::YouTubeService.new
service.key = API_KEY
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize
service
end
body = {
"snippet": {
"videoId": 'XYZ123',
"language": 'en',
"name": 'English'
}
}
s = get_service
s.insert_caption('snippet', body, upload_source: '/path/to/my-captions.vtt')
I have tried many different combinations, but the result is always the same:
Google::Apis::ClientError: invalidMetadata: The request contains invalid metadata values, which prevent the track from being created. Confirm that the request specifies valid values for the snippet.language, snippet.name, and snippet.videoId properties. The snippet.isDraft property can also be included, but it is not required. status_code: 400
It seems that there really is not much choice for the language and video ID values, and there is nothing remarkable about naming the captions as "English". I am really at a loss as to what could be wrong with the values I am passing in.
Incidentally, I get exactly the same response even if I just pass in nil as the body.
I looked at the OVERVIEW.md file included with the google-apis-youtube_v3 gem, and it referred to the Google simple REST client Usage Guide, which in turn mentions that most object properties do not use camel case (which is what the underlying JSON representation uses). Instead, in most cases properties must be sent using Ruby's "snake_case" convention.
Thus it turns out that the snippet should specify video_id and not videoId.
That seems to have let the request go through, so this resolves this issue.
The response I'm getting now has a status of "failed" and a failure reason of "processingFailed", but that may be the subject of another question if I can't figure it out.

iOS: how to debug kSecTrustResultRecoverableTrustFailure

I have read quite a few posts and sources now but couldn't find a definite answer.
I'm getting kSecTrustResultRecoverableTrustFailure on my SecTrustEvaluate() call and I would like to figure out why this is so (i.e. I want to figure out where exactly the trust chain validation fails and why). on OSX there seem to be some related function called SecTrustGetResult, but this is deprecated now even on OSX
How can I figure out where the validation fails? i'm fine with using private API's as I'm using this only during debugging to understand what exactly is going on inside.
thanks
Just use SecTrustCopyProperties() after calling SecTrustEvaluate():
SecTrustRef trust = ...;
SecTrustResultType trustResult = kSecTrustResultOtherError;
OSStatus status = SecTrustEvaluate(trust, &trustResult);
if (trustResult == kSecTrustResultRecoverableTrustFailure) {
NSArray * trustProperties = (__bridge_transfer id)
SecTrustCopyProperties(certTrust);
}
trustProperties is an array of dictionaries, one dictionary per cert in the cert chain evaluated. Every dictionary has an entry title, containing the name of the cert and if the cert didn't evaluate, it also contains an entry error containing the error. E.g. if the problem was that the cert has expired, the value of error will be CSSMERR_TP_CERT_EXPIRED.

Verifying signature of non-hashed data with Ruby OpenSSL

I have an RSA public key, some data and a signature of that data. I need to verify the signature. However, the signature is not of a digest of the data, but of the entire data. (The data itself is only 16 bytes, so the signer doesn't bother to hash the data before signing it.) I can verify the signature in C by specifying a NULL engine when initializing the context:
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(verify_key, NULL);
However, I have been unable to find an equivalent in Ruby's OpenSSL::PKey::PKey verify method. That method requires a Digest object, and there is no Digest that I can find that doesn't actually hash but just returns the data as-is. I tried creating my own Digest subclass, but I don't believe that can work, since the underlying OpenSSL library won't know about the existence of a custom digest type.
Am I stuck, or is there a way to solve this problem given that I cannot modify the code run by the signer?
Summarizing the answer from the comments in order to remove this question from the "Unanswered" filter...
owlstead:
Have you tried to find a function like public_decrypt? It may work, as normally you should not encryption with a private key and decrypt with a public key. With a bit of luck it will accept the signature version of PKCS#1 padding (note that the padding used for encryption and signing is different in PKCS#1).
Wammer:
Of course - decrypting the signature with the public key and verifying that it matches the data works fine. So far this is working fine with the standard PKCS#1 padding, but I'll do some more research to see if the differing encryption and signing paddings are a problem in practice. Thanks.
owlstead:
After a decrypt and validation of the padding, all that is left is a (if possible, secure) compare. So that would replace the verification function pretty well. Most of the security is in the modular arithmetic and padding.

How can I asymmetrically encrypt data using OpenPGP with Ruby?

This feels like it should be dead simple, yet I'm not having any luck.
The scenario is this: I have a public *.asc key file. I want to use this file (not my personal keyring) to encrypt data on a server, so that I can decrypt it locally with a secret key.
From the command line I can achieve this using gpg, but I'd prefer to use a Ruby library that isn't just a wrapper around the CLI (i.e., presumably one that provides bindings to the C library). I've looked at the GPGME and OpenPGP gems and haven't been able to figure out how to use them. The documentation (especially for OpenPGP) is quite sparse.
Here, for example, is something I've tried using GPGME, without any luck:
key = GPGME::Data.new(File.open(path_to_file))
data = GPGME::Data.new("I want to encrypt this string.")
# Raises GPGME::Error::InvalidValue
GPGME::Ctx.new do |ctx|
e = ctx.encrypt(key, data)
end
Has anyone been through this already? Surely this can't be that complicated?
I believe I've now got this figured out. It was actually just a few simple pieces I was missing:
Initializing the GPGME::Ctx object with a keylist_mode of GPGME::KEYLIST_MODE_EXTERN.
Importing the public key file using GPGME::Ctx#import.
Using GPGME::Crypto#encrypt to perform the encryption and specifying the correct recipient.
So my solution now looks like this:
key = GPGME::Data.new(File.open(path_to_file))
data = GPGME::Data.new("I want to encrypt this string.")
GPGME::Ctx.new(GPGME::KEYLIST_MODE_EXTERN) do |ctx|
ctx.import(key)
crypto = GPGME::Crypto.new(:armor => true, :always_trust => true)
e = crypto.encrypt(data, :recipients => "recipient#domain.com")
end

What is the safest way to store a password using Code Igniter?

I am using Code Igniter for my current project.
As of now, I am using MD5 for password hashing, but I have read at a lot of places, that it is not a good practice to do so.
What should I go with?
Using a salt
Or should I use bcrypt
Also, if bcrypt is recommended, then how to use it with Code Igniter?
EDIT
I have put these files in application/libraries
PasswordHash.php
c/Makefile
c/crypt_private.c
In my controller, I am using this code -
$params = array(
'phpass_hash_strength' => 8,
'phpass_hash_portable' => FALSE
);
$this->load->library('PasswordHash', $params);
$password = $this->passwordhash->HashPassword($pwd);
I am getting these errors -
A PHP Error was encountered
Severity: Notice
Message: Uninitialized string offset: 3
Filename: libraries/PasswordHash.php
Line Number: 116
A PHP Error was encountered
Severity: Warning
Message: strpos() [function.strpos]: Empty delimiter
Filename: libraries/PasswordHash.php
Line Number: 116
Update
Removed PasswordHash.php, using SimpleLoginSecure now.
Use bcrypt. This discussion came up here in the comments to my answer. You can use a library such as phppass to really simplify the password encryption.
On the matter of salt. Use it! Otherwise somebody can simply go to this site and download the rainbow tables that will cover the large majority of passwords the average users chooses. Especially with all the security leaks in the last few months, now is not the time to be saying you won't use something as simple to implement as random salt.
UPDATE
To use PHPPass with CI, download and extract the files from the phppass website, linked above. Put the PasswordHash.php file into your CI application/libraries directory.
In your code, you then load the library via: $this->load->library('PasswordHash',array(8, FALSE));
Hashing passwords is then as simple as $this->PasswordHash->HashPassword($password);
To later check if a password is correct, it is as simple as:
$password = $_POST['password'];
$actualPassword = /*Get the hashed password from your db*/;
$check = $this->PasswordHash->CheckPassword($password, $actualPassword);
I've taken this demo from http://dev.myunv.com/articles/secure-passwords-with-phpass/ which gives you a lot more informations. I've modified that tutorial slightly to utilize CI's loader which is why you don't need the include or new statements.
why use md5() when it is just as easy to use sha1() ?
Also salting the passwords is always a good idea as it effectively removes the threat of a Rainbow Table attack
In my experience a salted SHA1 hash is pleanty secure for 99% of web application situations.
Code Igniter has changed since the time this question was asked. But for the benefit of some who may not have come across the extensive documentation of CI or haven't seen this before, CI has an encryption class which provides a two-way data encryption using the Mcrypt library of PHP.
After initializing the class using:
$this->load->library('encrypt');
You can encrypt as follows:
$msg = 'My secret message';
$encrypted_string = $this->encrypt->encode($msg);
and decrypt as follows:
$encrypted_string = 'APANtByIGI1BpVXZTJgcsAG8GZl8pdwwa84';
$plaintext_string = $this->encrypt->decode($encrypted_string);
CI also has a non-decodable 1-way hashing:
$hash = $this->encrypt->sha1('Some string');
For more information see:
http://www.codeigniter.com/user_guide/libraries/encryption.html

Resources