Question

Background:

  • Ruby 1.9.3
  • Rails 3.2.16
  • Windows 7 x64

Issue

I'm trying to fix the infamous

SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

error by including my certificates in each HTTP request I perform. To do this I monkey patch Net::HTTP#use_ssl=:

# lib/gem_ext/net_http.rb
require 'open-uri'
require 'net/https'

module Net
  class HTTP
    alias_method :original_use_ssl=, :use_ssl=

    def use_ssl=(flag)
      store = OpenSSL::X509::Store.new
      store.set_default_paths # Auto-include the system CAs.

      Dir[Rails.root + 'config/certificates/*'].each do |cert|
        puts "Adding cert: #{cert}"
        store.add_cert(OpenSSL::X509::Certificate.new(File.read(cert)))
      end

      self.cert_store = store
      self.verify_mode = OpenSSL::SSL::VERIFY_PEER
      self.original_use_ssl = flag
    end
  end
end

Now, requests performed using Net::HTTP, for example:

> uri = URI.parse('https://internal-app/secure_url.json?foo=bar')
> Net::HTTP.start(uri.host, uri.port, :read_timeout => 10.minutes, :use_ssl => uri.scheme == 'https') do |http|
>  http.request Net::HTTP::Get.new(uri.request_uri)
> end
Adding cert: config/certificates/cert1.cer
Adding cert: config/certificates/cert2.cer
=> #<Net::HTTPOK 200 OK readbody=true>

Work perfectly.

However, when i try to use OpenURI, which i thought was just a wrapper around Net::HTTP (and other IO operations), such as:

> require 'open-uri'    
> open('https://our-all/secure_url.json?foo=bar', 'r', :read_timeout => 10.minutes)
Adding cert: config/certificates/cert1.cer
Adding cert: config/certificates/cert2.cer
#<Class:0x870e2e0>: SSL_connect returned=1 errno=0 state=SSLv3 read server certi
  from D:/Ruby/Ruby193/lib/ruby/1.9.1/net/http.rb:800:in `connect'
  from D:/Ruby/Ruby193/lib/ruby/1.9.1/net/http.rb:800:in `block in connect

So I can see my monkey patched method is getting hit ("Adding cert.."), but still I get the error. It seems as though something else is overriding it. Any ideas?

Thanks

Was it helpful?

Solution

I managed to come up with a solution (a while back now, so I hope it is still applicable).

According to the comments in my code, I needed to monkey patch cert_store= as well "as use_ssl= as Net::HTTP and OpenURI.open_httpcall these methods in different orders, so we need to ensure certificates are consistently added."

So, here is my solution:

module Net
  class HTTP

    alias_method :original_use_ssl=, :use_ssl=
    def use_ssl=(flag)
      store = OpenSSL::X509::Store.new
      store.set_default_paths # Auto-include the system CAs.
      self.cert_store = store # Now include internal certificates.
      self.verify_mode = OpenSSL::SSL::VERIFY_PEER # Force verification.
      self.original_use_ssl = flag
    end

    alias_method :original_cert_store=, :cert_store=
    def cert_store=(store)
      Dir[Rails.root + 'config/certificates/*'].each do |cert|
        store.add_cert(OpenSSL::X509::Certificate.new(File.read(cert)))
      end

      self.original_cert_store = store
    end
  end
end

Hope that helps

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top