Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this Ruby SSL-enabled server/client test working?

Tags:

ruby

ssl

I'm working through creating an SSL-enabled server in Ruby, along with a corresponding Ruby client to use with the server. In order to test, I created my own Root CA certificate with the following commands.

$:~/devel/ssl-test/ssl/CA$ openssl genrsa -out TestCA.key 2048
Generating RSA private key, 2048 bit long modulus
............+++
...........................+++
e is 65537 (0x10001)

$:~/devel/ssl-test/ssl/CA$ openssl req -new -key TestCA.key -out TestCA.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

$:~/devel/ssl-test/ssl/CA$ openssl x509 -req -days 365 -in TestCA.csr -out TestCA.crt -signkey TestCA.key 
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key

I then generated an SSL certificate for my server:

$:~/devel/ssl-test/ssl/keys$ openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.+++
............................................+++
e is 65537 (0x10001)

$:~/devel/ssl-test/ssl/keys$ cd ../csrs/
$:~/devel/ssl-test/ssl/csrs$ openssl req -new -key ../keys/server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:my.secure.test
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$:~/devel/ssl-test/ssl/csrs$ cd ../certs/
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
I am unable to access the ./demoCA/newcerts directory
./demoCA/newcerts: No such file or directory
$:~/devel/ssl-test/ssl/certs$ mkdir -p demoCA/newcerts
$:~/devel/ssl-test/ssl/certs$ touch demoCA/index.txt
$:~/devel/ssl-test/ssl/certs$ echo "01" > demoCA/serial
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Oct 25 16:25:05 2011 GMT
            Not After : Oct 24 16:25:05 2012 GMT
        Subject:
            countryName               = AU
            stateOrProvinceName       = Some-State
            organizationName          = Internet Widgits Pty Ltd
            commonName                = my.secure.test
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                48:50:B5:04:11:02:F1:40:97:58:BF:5F:8B:27:50:10:C0:3F:EE:D9
            X509v3 Authority Key Identifier: 
                DirName:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
                serial:81:44:16:06:5C:EB:5E:71

Certificate is to be certified until Oct 24 16:25:05 2012 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

After that, I created a simplistic SSL-enabled server to use the SSL certificate I just created.

require 'socket'
require 'openssl'
require 'thread'

server  = TCPServer.new(1337)
context = OpenSSL::SSL::SSLContext.new

context.cert = OpenSSL::X509::Certificate.new(File.open('ssl/certs/server.crt'))
context.key  = OpenSSL::PKey::RSA.new(File.open('ssl/keys/server.key'))

secure = OpenSSL::SSL::SSLServer.new(server, context)

puts 'Listening securely on port 1337...'

loop do
  Thread.new(secure.accept) do |conn|
    begin
      while request = conn.gets
        $stdout.puts '=> ' + request
        response = "You said: #{request}"
        $stdout.puts '<= ' + response
        conn.puts response
      end
    rescue
      $stderr.puts $!
    end
  end
end

When started, it seems to work fine...

$:~/devel/ssl-test$ ruby server.rb 
Listening securely on port 1337...

I then created a non-SSL capable client just to ensure it was denied connectivity.

require 'socket'
require 'thread'

client = TCPSocket.new('127.0.0.1', 1337)

Thread.new do
  begin
    while response = client.gets
      $stdout.puts response
    end
  rescue
    $stderr.puts "Error from client: #{$!}"
  end
end

while request = $stdin.gets
  request = request.chomp
  client.puts request
end

When I run this via the following:

$:~/devel/ssl-test$ ruby client.rb
hello
Error from client: Connection reset by peer

Correspondingly, I get the following from the server:

$:~/devel/ssl-test$ ruby server.rb
Listening securely on port 1337...
/usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept': SSL_accept returned=1 errno=0 state=SSLv2/v3 read client hello A: unknown protocol (OpenSSL::SSL::SSLError)
    from /usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept'
    from server.rb:16:in `block in <main>'
    from server.rb:15:in `loop'
    from server.rb:15:in `<main>'

This was all expected. Next, I modified the client code to use an SSL context.

require 'socket'
require 'openssl'
require 'thread'

client  = TCPSocket.new('127.0.0.1', 1337)
context = OpenSSL::SSL::SSLContext.new

secure = OpenSSL::SSL::SSLSocket.new(client, context)
secure.sync_close = true
secure.connect

Thread.new do
  begin
    while response = secure.gets
      $stdout.puts response
    end
  rescue
    $stderr.puts "Error from client: #{$!}"
  end
end

while request = $stdin.gets
  request = request.chomp
  secure.puts request
end

I fully expected this to fail as well during the handshake process, but it did not... I got the following result:

$:~/devel/ssl-test$ ruby client.rb
hello
You Said: hello

Why did this work? I was assuming it would fail because I didn't think the client would have any idea about the Root CA I created and signed the server SSL certificate with, and therefore wouldn't be able to verify the server's certificate. What am I missing? When I created and signed the server's certificate and it was "committed", did this somehow make it available to the OpenSSL library? I was expecting to have to somehow tell the SSL context in the client where to look for the Root CA I created for testing purposes...

As a follow-up test, I copied my client code over to a different machine that definitely knows nothing about the Root CA I created for this test, changed the IP address the client connects to, and ran the test again. This test produced the same results - the client was able to communicate with the server when I assumed it wouldn't be able to. Any ideas?

like image 754
Bryan Avatar asked Oct 25 '11 17:10

Bryan


People also ask

Could not verify the SSL certificate for Ruby?

Why am I seeing certificate verify failed ? This error happens when your computer is missing a file that it needs to verify that the server behind RubyGems.org is the correct one. The latest version of RubyGems should fix this problem, so we recommend updating to the current version.

What is Ssl_cert_file?

Strictly, SSL_CERT_FILE is an OpenSSL environment variable. Requests doesn't use it because it does not ever check for it being set, and never trusts that OpenSSL will locate certs.


1 Answers

Depending on the version of Ruby you are using, the default verify mode for the SSLContext object may not be enforcing certificate verification. You can force the verify mode with:

context = OpenSSL::SSL::SSLContext.new
context.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT

This should cause the client's connection attempt to fail, as expected.

like image 131
Ian Eccles Avatar answered Dec 11 '22 03:12

Ian Eccles