SSL Error on HTTP POST (Unknown Protocol) - ruby

Trying to connect to Imgur API via SSL gives me an error. Here's the code and the error:
API_URI = URI.parse('https://api.imgur.com')
API_PUBLIC_KEY = 'Client-ID --'
ENDPOINTS = {
:image => '/3/image',
:gallery => '/3/gallery'
}
# Public: Upload an image
#
# args - The image path for the image to upload
#
def upload(image_path)
http = Net::HTTP.new(API_URI.host)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
params = {'image' => File.open(image_path)}
request = Net::HTTP::Post.new(API_URI.request_uri)
request.set_form_data(params)
request.add_field('Authorization', API_PUBLIC_KEY)
response = http.request(request)
puts response.body
end
And the error:
`connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol (OpenSSL::SSL::SSLError)
I know VERIFY_NODE is not good practice but I just want to test the connection for now.
Ruby version: 1.9.2

Specifying the port when creating the HTTP client fixed this problem.
http = Net::HTTP.new(API_URI.host, API_URI.port)
or
http = Net::HTTP.new(API_URI.host, 443)

For me it was because I had started the server as a http (tcp://) servers instead of https (ssl://).
i.e.
bundle exec puma config.ru -b 'tcp://0.0.0.0:3456?key=/path/to/key.key&cert=/path/to/cert.crt'
instead of:
bundle exec puma config.ru -b 'ssl://0.0.0.0:3456?key=/path/to/key.key&cert=/path/to/cert.crt'

Related

OpenSSL::SSL::SSLError: Ruby client's server ca certificate does not work while it worked with curl

I got certificate from customer to connect with their VPN, but it does not work with ruby code while it worked with curl command. Curl command is as follows:
curl --cacert cert.cer -d '{"acb": 123 }' -H 'Content-Type: application/json' 'https://demo.com'
In ruby, I am trying to do the following to connect the client APIs provided to us for transactions.
require 'net/http'
require 'json'
require 'uri'
full_url = "https://demo.com"
uri = URI.parse(full_url)
data = { "acb": 123 }
headers = { 'Content-Type' => "application/json" }
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
raw = File.read("path_to_the_certificate")
http.cert = OpenSSL::X509::Certificate.new(raw)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data.to_json
response = http.request(request)
puts response.code
puts response.body
We also tried to pass our server's certificate as follows, but that doesn't work either
http.ca_path='/etc/pki/tls/certs'
http.ca_file='/etc/pki/tls/certs/cert.cer'
http.cert = OpenSSL::X509::Certificate.new(File.read("/path/client.crt"))
http.key = OpenSSL::PKey::RSA.new(File.read("/path/client.key"))
Getting the following error while
OpenSSL::SSL::SSLError (SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate))
I think the issue with their self-signed certificate. It fails verification.
However, you can manually disable it with
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
verify_mode[RW]
Sets the flags for server the certification verification at beginning of SSL/TLS session.
OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
from https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html
I tried to replicate it locally and it worked with this fix.
It should be that the vpn certificate is self-signed, you need to specify your own cacert, so you specify cacert file as the file used by the curl above, not the cacert file that comes with the system
add this line:
http.ca_file = "cacert filename"
Like this:
require 'net/http'
require 'json'
require 'uri'
full_url = "https://localhost/test.html"
uri = URI.parse(full_url)
data = { "acb": 123 }
headers = { 'Content-Type' => "application/json" }
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# You need to specify the cacert file used for curl above (filename: cert.cer)
http.ca_file = "/root/myca/cacert.crt"
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data.to_json
response = http.request(request)
puts response.code
puts response.body
you should add your certificate in .pem format to (depending on the version) either:
C:\Ruby{version number}{-x64 - if 64 bit operating system}\ssl
e.g. C:\Ruby25-x64\ssl
or to
C:\Ruby{version number}{-x64 - if 64 bit operating system}\lib\ruby{version number}\rubygems\ssl_certs{your cn}
e.g. C:\Ruby25-x64\lib\ruby\2.5.0\rubygems\ssl_certs\client.cn
and then in the C:\Ruby{The version number}{-x64 - If it's a 64-bit operating system}\ssl\certs run the c_rehash.r script
For Apps using PayPal::SDK.configure(...)
PayPal::SDK.configure(
mode: ...,
client_id: ...,
client_secret: ...,
# Deliberately set ca_file to nil so the system's Cert Authority is used,
# instead of the bundled paypal.crt file which is out-of-date due to:
# https://www.paypal.com/va/smarthelp/article/discontinue-use-of-verisign-g5-root-certificates-ts2240
ssl_options: { ca_file: nil }
)
For apps using a YAML config file
ssl_options:
ca_file: null

OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello

I am trying to pull a binary from our repo in Artifactory that is of HTTPS.
/var/chef/cookbooks/app/providers/setup.rb:48:in `determineRepoURL'
/var/chef/cookbooks/app/providers/setup.rb:86:in `deploy_compile_time_config_application'
/var/chef/cookbooks/app/providers/setup.rb:24:in `block (2 levels) in class_from_file'
/var/chef/cookbooks/app/providers/setup.rb:20:in `block in class_from_file'
...
[2019-03-26T17:11:39-07:00] ERROR: Running exception handlers
[2019-03-26T17:11:39-07:00] ERROR: Exception handlers complete
[2019-03-26T17:11:39-07:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
Chef Client failed. 5 resources updated
[2019-03-26T17:11:39-07:00] ERROR: app_setup[test] (app::default line 40) had an error: OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server hello A: tlsv1 alert protocol version
Previously I had
def determineRepoURL(*param)
url = "#{repository_url}/api/search/pattern?pattern=#{param[0]}:#{name}/#{version}/*#{param[1]}"
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
request.basic_auth "#{user}", "#{token}"
response = http.request request # Net::HTTPResponse object
result = JSON.parse(response.body)
list = result['files'].sort.reverse
return list[0]
end
after adding
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
I still have the same error. However, for some reason, this only happens on Chef's 11.8.2, I tried my script with Chef's 12.6.0 and I did not encounter this SSL certificate issue, does anyone have a bypass for 11.8.2 as I do not have access to 12.6.0
Ruby version: 1.8.7
OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013

ADAL Proxy Error while connecting through Rake Task

I am following this website for connecting to Microsoft Graph to get the access token using ADAL gem.
I have written a Rake task to perform this. This works fine in the open network but fails in the Corporate Network.
I have set the proxy credentials in the bash profile and in the bashrc file in my terminal
export http_proxy=http://username:pwd#proxy.corporate.com:8080/
export HTTP_PROXY=http://username:pwd#proxy.corporate.com:8080/
export https_proxy=http://username:pwd#proxy.corporate.com:8080/
export HTTPS_PROXY=http://username:pwd#proxy.corporate.com:8080/
I am receiving
SocketError: Failed to open TCP connection to login.microsoftonline.com:443 (Hostname not known: login.microsoftonline.com)
Solution by OP.
Issue has been resolved after tweeking the user_credential.rb file in ADAL gem
File to edit: /azure-activedirectory-library-for-ruby-24e1b7f0dc37/lib/adal/user_credential.rb
def realm_discovery_response
#realm_discovery_response ||=
JSON.parse(Net::HTTP.get(realm_discovery_uri))
end
Replace the above code with the below as Net::HTTP.get doesnot take the system proxy use and only Net::HTTP.Get.new works
def realm_discovery_response
uri = URI.parse(realm_discovery_uri.to_s)
puts "URI #{uri}"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
req = Net::HTTP::Get.new(uri.request_uri)
res = http.request(req)
#realm_discovery_response ||= JSON.parse(res.body)
end

Ruby https with ca certification does not work while it worked with curl

In ruby, I try to do a ssl connection to a nginx server I setup locally, with an auto-signed certificate. My code is :
require "net/http"
require "net/https"
require "openssl"
require "uri"
require "pp"
request = Net::HTTP::Get.new("/")
response = Net::HTTP.start(
"localhost",
443,
{
:use_ssl => true,
:key => OpenSSL::PKey::RSA.new(File.read("/home/gg/crt/client.key")),
:cert => OpenSSL::X509::Certificate.new(File.read("/home/gg/crt/client.crt")),
:ca_file => "/home/gg/crt/ca.pem",
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
:verify_depth => 5,
}
) do |http|
http.request(request)
end
puts response.inspect
puts response.body
When I run it it return
/home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `connect': SSL_connect returned=1 errno=0 state=error: certificate verify failed (OpenSSL::SSL::SSLError)
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `block in connect'
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/timeout.rb:74:in `timeout'
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `connect'
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:863:in `do_start'
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:852:in `start'
from /home/gg/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:583:in `start'
from testso.rb:8:in `<main>'
But I have the correct result if I run it with curl:
curl https://localhost --key crt/client.key --cert crt/client.crt --cacert crt/ca.pem
What am i doing wrong?
To possibly help others, here is a working solution with Ruby 2.0, for an HTTP GET.
require "net/http"
uri = URI.parse('https://your_url.com')
http = Net::HTTP.new(uri.host, uri.port)
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ssl_version]=nil
http.use_ssl = true
http.ca_path='/etc/pki/tls/certs/'
http.ca_file='/etc/pki/tls/certs/YOUR_CERT_CHAIN_FILE'
http.cert = OpenSSL::X509::Certificate.new(File.read("YOUR_CERT)_FILE"))
http.key = OpenSSL::PKey::RSA.new(File.read("YOUR_KEY_FILE"))
#SSLv3 is cracked, and often not allowed
http.ssl_version = :TLSv1_2
#### This is IMPORTANT
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
#Crete the GET request
request = Net::HTTP::Get.new(uri.request_uri)
#Add Headers, if needed
request.add_field 'X_REMOTE_USER', 'USER_NAME'
request.add_field 'Accept', '*'
#Get Response
response = http.request(request)
#Review Response
puts response.body
I feel dumb but figured out the problem.
When generating the different certificates I left some fields blank.
It seems that openssl detected this certificate as "looking" autosigned.
Thus if we use OpenSSL::SSL::VERIFY_PEER the connection fails
So to be sure that the certificate is well generated using the CA you can do :
openssl verify -CAfile ca.crt server.crt
(and same command with client.crt)
if we get
error 18 at 0 depth lookup:self signed certificate
OK
The certificate is detected as autosigned and it fails
if we get
Signature ok
It should work

Socksify and SSL for HTTPS request

For a project I need to make an HTTPS call through a Socks5 proxy so I can access the remote API of a customer.
The original code I'm using is this:
url = "/proxyValidate?service=#{migration_station_verification_url}&ticket=#{service_ticket}"
if Rails.env == 'development'
uri = URI.parse(soe_cas_url + url)
Net::HTTP.SOCKSProxy('127.0.0.1', 1080).start(uri.host, uri.port) do |http|
res = http.get(uri.path)
doc = Nokogiri::XML(res)
doc.remove_namespaces!
doc.xpath('//sessionId').first.content
end
else
http = Net::HTTP.new(soe_cas_url)
http.use_ssl = true
req = Net::HTTP::Get.new(url)
res = http.request(req)
doc = Nokogiri::XML(res)
doc.remove_namespaces!
doc.xpath('//sessionId').first.content
end
The goal is to use the proxy only in development environment. When I make the call using the proxy I receive the error:
wrong status line: "<!DOCTYPE
Googling this shows I need to set the use_ssl flag to true, which works outside the development cycle. When using the socksify there is no such mechanism. If I call this inside the do block, I receive an error that the session is already started. Calling it outside the block returns an exception that no such function exists.
Anyone can tell me how to run SSL/HTTPS through the socksify proxy?
EDIT:
I'm currently playing around with pure sockets to get what I want, using the following code:
socket = Socket::TCPSocket.open('127.0.0.1', 1080)
ssl_context = OpenSSL::SSL::SSLContext.new
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
ssl_context.ca_file = File.join(Rails.root, 'ssl', 'cacert.pem')
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
ssl_socket.sync_close = true
ssl_socket.connect
request = "GET #{url} HTTP/1.1\r\nAccept: */*\r\nHost: #{soe_cas_url}"
ssl_socket.puts(request)
ssl_socket.puts("")
response = ""
while (line = ssl_socket.gets)
response << line
end
This however fails at the ssl_socket.connect with the following error:
ssl_connect syscall returned=5 errno=0 state=sslv2/v3 read server hello a
I've tried using self-signed certificates, no certificates etc but nothing seems to work. I do know the endpoint relies on self-signed certificates on staging environmnet.

Resources