Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add SSL to a connection

Tags:

ruby

ssl

openssl

How do you secure a socket with SSL in Ruby when you need to communicate over plaintext first?

I can't use OpenSSL::SSL::SSLServer because it's the client's responsibility to request an SSL connection first

To put a long story short, I am attempting to implement RFC3207, where the client sends the keyword "STARTTLS", and then an SSL connection is created.

My question is "How do I create the SSL connection after the server has sent '220 OK'?"

I know I can use OpenSSL::SSL::SSLSocket on the client-side, but I have no idea what to do on the server-side

If you know how to do this in a language other than Ruby, just post the code and I'll translate it, I've been working on this for about 8 hours and I need everything I can get

I have asked in #ruby-lang, but with no avail, and I have tried wrapping Socket objects in SSLSockets on the server and client at the same time, but that isn't working either

In short, I'm very stuck, I need all the help I can get

like image 523
lcarpenter Avatar asked Apr 21 '12 20:04

lcarpenter


2 Answers

I created this gist to illustrate how to set up a minimal TLS server. You may want to leave out lines 62-67, that was to illustrate a new feature on trunk.

But other than that, it's a fully working TLS server, you may build on it to add further functionality.

You may also want to change the server certificate's CN from "localhost" to a real domain if you want to use it seriously :)

You may notice that the largest part of the work is actually setting up the PKI aspects correctly. The core server part is this:

ctx = OpenSSL::SSL::SSLContext.new
ctx.cert = ... # the server certificate
ctx.key = ... # the key associated with this certificate
ctx.ssl_version = :SSLv23
tcps = TCPServer.new('127.0.0.1', 8443)
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
ssls.start_immediately = true
begin
  loop do
    ssl = ssls.accept
    puts "Connected"
    begin
      while line = ssl.gets
        puts "Client says: #{line}"
        ssl.write(line) # simple echo, do something more useful here
      end
    ensure
      ssl.close
    end
  end
ensure
  tcps.close if tcps
end
like image 88
emboss Avatar answered Sep 21 '22 19:09

emboss


You have to set the SSLServer's start_immediately field to false in order to start the SSL server in plain text mode. At any point (ie. when you receive the STARTTLS command from the client), you can call the SSLSocket's accept method to initiate SSL/TLS handshake. The client will of course have to agree to the protocol :)

Here is a sample server I wrote to test this:

#!/usr/bin/ruby

require 'socket';
require 'openssl';

certfile = 'mycert.pem';
port = 9002;

server = TCPServer.new( port );

# Establish an SSL context 
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new( File.open( certfile ) );
sslContext.key = OpenSSL::PKey::RSA.new( File.open( certfile ) );

# Create SSL server
sslServer = OpenSSL::SSL::SSLServer.new( server, sslContext );

# Don't expect an immidate SSL handshake upon connection.
sslServer.start_immediately = false;

sslSocket = sslServer.accept;

sslSocket.puts( "Toast.." );

# Server loop
while line = sslSocket.gets

        line.chomp!;

        if "STARTTLS" == line
                # Starting TLS
                sslSocket.accept;
        end

        sslSocket.puts( "Got '#{line}'" );

end

sslSocket.close;

I'm sure the original poster knows how to test STARTTLS, but the rest of us might need this reminder. Actaually I'm normally using the utils from the GNUTLS package (gnutls-bin in debian/ubuntu) to test starttls, because it allows me to start the handshake whenever I want to:

$ gnutls-cli --starttls --port 9002 --insecure localhost

This connects in plain text TCP socket mode. Type some lines and get them echoed. This traffic is unencrypted. If you send STARTTLS, the sslSocket.accept is called, and the server waits for SSL handshake. Press ctrl-d (EOF) to start handshake from the gnutls client, and watch it establish an encrypted SSL connection. Subsequent lines will be echoed as well, but the traffic is now encrypted.

like image 34
Øystein Steimler Avatar answered Sep 17 '22 19:09

Øystein Steimler