Convert Rails Net::HTTP request to MD5 Hex Digest - ruby-on-rails

In order to use a third-party API, I need to encode the Net::HTTP::Post request as an MD5 hex digest, which is then used as part of the signature. However, when I try to simply Digest::MD5.hexdigest(req), it throws a "Cannot convert to string error", and when I explicitly req.to_s, it just gives the MD5 of #<Net::HTTP::Post:0x112a0eef8>
I'm simply:
request = Net::HTTP::Post.new(url.path)
request.body = {
"key" => "val"
}.to_json
# later...
hexDigest = Digest::MD5.hexdigest(request)
which is the documented spec, I think: "[with the] JSON body containing the new information."
This is the relevant sample Java code they supply:
ByteArrayOutputStream requestOutputStream = new ByteArrayOutputStream();
httpMethod.getEntity().writeTo(requestOutputStream);
DigestUtils.md5Hex(requestOutputStream.toByteArray()).toLowerCase();
Any ideas?
Thanks!

Try to call 'to_s' method explicitly, it should help:
hexDigest = Digest::MD5.hexdigest(request.to_s)

The equivalent ruby code for those lines is:
OpenSSL::Digest::MD5.hexdigest(request.body)
httpMethod.getEntity() will return the json defined as the request body.
requestOutputStream.toByteArray() will return the array of bytes corresponding to the request body.

Related

Rails: Cannot match webhook signature in json response

I need to be able to verify the signature of a webhook but I cannot seem to match the value correctly. The tool I'm using provides the expected signature as a URL param with the request:
YOUR_CALLBACK_URL?signature=ofdiwefjojiefwojowefoi
# www.websitename.com?signature=ofdiwefjojiefwojowefoi
They state that the way they generate the signature is:
The signature is generated using an HMAC-SHA-256 base64 digest of the raw HTTP Body of the Webhook post using this Webhook secret.
You can generate the signature in php as follows:
$request_body = file_get_contents('php://input');
$s = hash_hmac('sha256', $request_body, 'mySecret', true);
echo base64_encode($s);
In my app, I attempt to generate a matching signature by doing the following:
key = ENV['ESIGNGENIE_SECRET']
data = params.to_json
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)).strip()
return signature == params["signature"]
This seems to always be wrong. Am I doing something wrong here? I can't tell if I'm encountering issues due to the way Rails parses the json object or what.
After doing some research I realized that my mistake was trying to use params to generate the signature when I should have used request.body.read. It should look like the following:
key = ENV['ESIGNGENIE_SECRET']
data = request.body.read
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)).strip()
return signature == params["signature"]

Twitter Application Only Auth

I'm trying to get an Application Only Auth token following the steps of this link:
https://dev.twitter.com/docs/auth/application-only-auth
I'm using Ruby on Rails and Rest Client to make the POST request needed and I'm setting the headers (I think) properly.
The step-by-step says:
URL encode the consumer key and the consumer secret according to RFC
1738. Note that at the time of writing, this will not actually change the consumer key and secret, but this step should still be performed
in case the format of those values changes in the future.
Concatenate the encoded consumer key, a colon character ":", and the
encoded consumer secret into a single string.
Base64 encode the string from the previous step.
And my code is:
require 'rest_client'
key = URI::encode('app_key')
secret = URI::encode('app_secret')
encoded = Base64.encode64("#{key}:#{secret}")
res = RestClient::Resource.new "https://api.twitter.com/oauth2/token/"
response = ''
options = {}
options['Authorization'] = "Basic #{encoded}"
options['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
res.post('grant_type=client_credentials', options) do |response, request, result|
response << "#{CGI::escapeHTML(response.inspect)}<br /><br />"
response << "#{CGI::escapeHTML(request.inspect)}<br /><br />"
response << "#{CGI::escapeHTML(result.inspect)}<br />"
end
render :text => txt
And I print out this:
"{\"errors\":[{\"label\":\"authenticity_token_error\",\"code\":99,\"message\":\"Unable to verify your credentials\"}]}"
#<RestClient::Request:0x9ece5d8 #method=:post, #headers={"Authorization"=>"Basic bXlfa2V5Om15X3NlY3JldA==\n", "Content-Type"=>"application/x-www-form-urlencoded;charset=UTF-8"}, #url="https://api.twitter.com/oauth2/token/", #cookies={}, #payload="", #user=nil, #password=nil, #timeout=nil, #open_timeout=nil, #block_response=nil, #raw_response=false, #verify_ssl=false, #ssl_client_cert=nil, #ssl_client_key=nil, #ssl_ca_file=nil, #tf=nil, #max_redirects=10, #processed_headers={"Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Authorization"=>"Basic bXlfa2V5Om15X3NlY3JldA==\n", "Content-Type"=>"application/x-www-form-urlencoded;charset=UTF-8", "Content-Length"=>"29"}, #args={:method=>:post, :url=>"https://api.twitter.com/oauth2/token/", :payload=>"grant_type=client_credentials", :headers=>{"Authorization"=>"Basic bXlfa2V5Om15X3NlY3JldA==\n", "Content-Type"=>"application/x-www-form-urlencoded;charset=UTF-8"}}>
#<Net::HTTPForbidden 403 Forbidden readbody=true>
My key and secret are valid.
Am I missing something?
Thanks!
EDIT:
Updating with the solution I've found.
The problem was on the Base64 convertion and string encoding.
I had to add a forced encoding parameter to the key+secret combination, for UTF-8 convertion:
encoded = Base64.encode64("#{key}:#{secret}".force_encoding('UTF-8'))
The Rails Base64.encode64 inserts a line break every 60 encoded characters.
The workaround was:
For Ruby 1.9+ (strict_ was included in Ruby 1.9)
Base64.strict_encode64(string)
For Ruby 1.9-
Base64.encode64(string).gsub('/\n/') # To remove the line break
Are you trying to implement Authorization with Tweeter (as OAuth Provider). Instead of writing it from the scratch following the API documentation, I would suggest to use OmniAuth. The setup & boilerplate code is fairly easy to use.
Read more about it at http://www.omniauth.org/ & https://github.com/intridea/omniauth/wiki
Let us know, if that helped you or not.

Passing special characters via apache HTTPClient

I have a servlet which accepts HTML content as part of the request param. The HTML is a localized one which may be a french, spanish etc... content.
I'm also using apache HTTP client to make a request to this servlet for test purpose, which has the following header definition:
HttpClient client = new HttpClient();
PostMethod method = new PostMethod("<URL>");
String html = FileUtils.readFileToString(inputHTMLFile, "UTF-8");
method.addParameter("html", html);
method.addRequestHeader("Accept", "*/*");
method.setRequestHeader("accept-charset", "UTF-8");
Whatever HTML is read has the character encoding utf-8, sample text:
Télécharger un fichier
However when i get the html from the request param that texts becomes T?l?charger un fichier
I went through few links such as http://www.oracle.com/technetwork/articles/javase/httpcharset-142283.html which talks about charset and how normally a browser would encode the special characters. If i were to URLEncode the html with UTF-8 and then decode that with same charset in the servlet i get the HTML as expected.
Is this the only thing i can do to preserve the charsets? Am i missing something?
Thanks.
Now that the issue with the file itself is fixed, try modifying your code as follows:
HttpClient client = new HttpClient();
PostMethod postMethod = new PostMethod("<URL>");
postMethod.getParams().setContentCharset("utf-8"); //The line I added
...
Note that the client needs to decode the request as UTF-8 now. French and Spanish worked correctly because their characters are included in the default ISO-8859-1 charset. Chinese characters are not. If the French and Spanish were decoded correctly on client, the client is decoding the request as ISO-8859-1, and sending UTF-8 could fail.
So you could try also adding this:
postMethod.setRequestheader("Content-Type", "application/x-www-form-url-encoded; charset=utf-8");
Just try this for post method.
HttpPost request = new HttpPost(webServiceUrl);
StringEntity str = new StringEntity(YourData);
str.setContentType("application/json");
HttpPost.setEntity(new StringEntity(str, HTTP.UTF_8));
You should better change string to base64 encoded and then send.
I think I've found the cause by examining EntityBuilder decompiled code: the EntityBuilder ignores the contentEncoding field regarding the parameters, it uses the one from contentType field. And by looking on org.apache.http.entity.ContentType the only one predefined value having UTF-8 is org.apache.http.entity.ContentType.APPLICATION_JSON.
So in my case
HttpPost method = new HttPost("<URL>");
EntityBuilder builder = EntityBuilder.create();
builder.setContentType(ContentType.APPLICATION_JSON);
builder.setContentEncoding(StandardCharsets.UTF_8.name());
...
method.setEntity(builder.build());
did the job (although I think setting contentType is redundant here).
I'm using httpclient-osgi version 4.5.4.
PostMethod method = new PostMethod("URL");
method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

HTTPBuilder HTTP Post url encoded parameters and accept xml response?

Hi there i am wondering how i can post a urlencoded string and read in an xml response using HTTPBuilder? I would like to use this inside a Grails application. The REST plugin is no option. I tried the examples given on http://groovy.codehaus.org/modules/http-builder/doc/post.html but this gives me no xml response to read in.
You can try something like this:
def httpBuilder = new HTTPBuilder("http://webite.url")
httpBuilder.request(Method.POST, URLENC){req->
headers.accept = "application/xml"
body = [ YOUR URL ENCODED POST]
response.success = {resp,xml->
//read xml response.
}
}

How to do it in Ruby on rails

This is a C# code:
byte[] pb = System.Text.Encoding.UTF8.GetBytes(policy.ToString());
// Encode those UTF-8 bytes using Base64
string policyB = Convert.ToBase64String(pb);
// Sign the policy with your Secret Key using HMAC SHA-1.
System.Security.Cryptography.HMACSHA1 hmac = new System.Security.Cryptography.HMACSHA1();
hmac.Key = System.Text.Encoding.UTF8.GetBytes(secretKey);
byte[] signb = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(policyB));
string signature = Convert.ToBase64String(signb);
How to do the same in Ruby on rails? More specifically I need to know functions to get bytes from string and base64 encode them and calculate hmac hash.
Not sure if it exactly the same, but it works for me:
#policy = ActiveSupport::Base64.encode64s(#policy)
# Sign policy with secret key
digest = OpenSSL::Digest::Digest.new('sha1')
#signature = ActiveSupport::Base64.encode64s(OpenSSL::HMAC.digest(digest, secretKey, #policy))
I'll try again.
There are a couple of HMAC libraries for ruby/rails that might make this much simpler:
http://auth-hmac.rubyforge.org/

Resources