5

As referenced in OpenSSL::X509::Certificate Showing Certificate for Wrong Domain, I need to use TLSv1 or above and Server Name Indication extension.

Even with ssl_version and servername_cb set via the SSLContext, I'm still getting the wrong certificate for myproair.com.

  begin 
    timeout(1) do
      tcp_client = TCPSocket.new("#{instance["domain"]}", 443)
      ssl_context = OpenSSL::SSL::SSLContext.new()
      ssl_context.ssl_version = :TLSv1
      ssl_context.servername_cb = "https://#{instance["domain"]}"
      ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client, ssl_context)
      ssl_client.connect
      cert = OpenSSL::X509::Certificate.new(ssl_client.peer_cert)
      ssl_client.sysclose
      tcp_client.close
      #http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/X509/Certificate.html
      date = Date.parse((cert.not_after).to_s)
      row.push("#{date.strftime('%F')} #{cert.signature_algorithm} #{cert.subject.to_a.select{|name, _, _| name == 'CN' }.first[1]}".downcase.ljust(57))
    end
  rescue SocketError
    row.push("down".ljust(57))
  rescue Errno::ECONNREFUSED
    row.push("connection refused".ljust(57))
  rescue Errno::ECONNRESET
    row.push("connection reset".ljust(57))
  rescue Timeout::Error
    row.push("no 443 listener".ljust(57))
  rescue OpenSSL::SSL::SSLError
    row.push("bad certificate - ssl error".ljust(57))
  rescue Exception => ex
    row.push("error: #{ex.class} #{ex.message}".ljust(57))
  end

How do I set the server name in Ruby 2.0 on OS X?


$ ruby --version
ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin14]

$ openssl version
OpenSSL 0.9.8zc 15 Oct 2014
Community
  • 1
  • 1
Seth Reeser
  • 195
  • 2
  • 2
  • 13
  • `servername_cb` is for the server side. When the server gets a connection, it invokes the callback to allow the server to select a *SNI* related certificate :) On the client, you are looking for the Ruby equivalent to `SSL_set_tlsext_host_name`. You can see a C-based example of it at OpenSSL's [SSL/TLS Client](https://wiki.openssl.org/index.php/SSL/TLS_Client). – jww May 14 '15 at 19:34
  • Also see [How to set TLS context options in Ruby (like OpenSSL::SSL::SSL_OP_NO_SSLv2)](http://stackoverflow.com/q/22550213). It shows you how to get "TLS 1.0 and above" in Ruby. – jww May 14 '15 at 19:55
  • 1
    @jww thank you for all of your insights - I'm reaching into edge case territory but it's fun :) still digging around to find the Ruby equivalent... – Seth Reeser May 14 '15 at 22:33
  • Ruby is very frustrating to me. I find its too damn difficult to do basic things securely, like establish a secure channel. (I have my own definition of "secure" when it comes to secure channels). I eventually gave up on it and stopped trying to learn it. – jww May 15 '15 at 17:54
  • After immersing myself in Ruby for the past few months, I've concluded the same thing - I'm debating rewriting in Python. What's your preference? – Seth Reeser Jul 15 '15 at 23:40
  • I need more info regarding this, I have a multi tenant app coded with rails , and each client will be uses their own domain. My problem is setting up SSL for my clients to access my app with their domain names. From my research, all this happens at the server level. So i am a bit confused when i see SNI at the rails level. So its possible to setup SNI for rails using the OpenSSL::SSL::SSLContext#servername_rb ? – Ndeto Feb 21 '19 at 14:04

1 Answers1

6

Naturally you use the undocumented 'hostname' method for OpenSSL::SSLSocket!

tcp_client = TCPSocket.new("#{instance["domain"]}", 443)
ssl_context = OpenSSL::SSL::SSLContext.new()
ssl_context.ssl_version = :TLSv1
ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client, ssl_context)
ssl_client.hostname = instance["domain"]
ssl_client.connect
cert = OpenSSL::X509::Certificate.new(ssl_client.peer_cert)
ssl_client.sysclose
tcp_client.close

I discovered that here while writing code with a similar goal.

dkam
  • 3,876
  • 2
  • 32
  • 24