I am trying to create a simple SSL client and server in Ruby. But I'm getting a cryptic error message and the documentation is of no help.
Here is my server code:
#!/usr/bin/ruby
require "gserver"
require "openssl"
listeningPort = Integer(ARGV[0])
class Server < GServer
def initialize(listeningPort)
@sslContext = OpenSSL::SSL::SSLContext.new
@sslContext.cert = OpenSSL::X509::Certificate.new(File.open("MyCert.pem"))
super(listeningPort, "0.0.0.0")
end
def serve(io)
begin
ssl = OpenSSL::SSL::SSLSocket.new(io, @sslContext)
ssl.sync_close = true
ssl.connect
while (lineIn = ssl.gets)
lineIn = lineIn.chomp
$stdout.puts "=> " + lineIn
lineOut = "You said: " + lineIn
$stdout.puts "<= " + lineOut
ssl.puts lineOut
end
rescue
$stderr.puts $!
end
end
end
server = Server.new(listeningPort)
server.start
server.join
The client code is similar:
#!/usr/bin/ruby
require "socket"
require "thread"
require "openssl"
host = ARGV[0]
port = Integer(ARGV[1])
socket = TCPSocket.new(host, port)
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new(File.open("MyCert.pem"))
ssl = OpenSSL::SSL::SSLSocket.new(socket, sslContext)
ssl.sync_close = true
ssl.connect
puts ssl.peer_cert # this is nil
Thread.new {
begin
while lineIn = ssl.gets
lineIn = lineIn.chomp
$stdout.puts lineIn
end
rescue
$stderr.puts "Error in input loop: " + $!
end
}
while (lineOut = $stdin.gets)
lineOut = lineOut.chomp
ssl.puts lineOut
end
When I connect, I get this error on both the server and the client:
in `connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol (OpenSSL::SSL::SSLError)
The problem could be that it doesn't trust the certificate (self-signed). I'm not sure how to tell the client to trust that certificate. Above, I have put the server's cert in the context, but that was just a shot in the dark. I'm not even sure my certificate is in an acceptable format (it is in base64 with the cert and the private key in the file). The documentation is very scant and there doesn't seem to be much on the web in this area either.
Any ideas?
To serve a Ruby on Rails application via HTTPS, there are three steps that you need to follow: Obtain an SSL certificate. Configure the web server to use the SSL certificate. Configure the Ruby on Rails application for HTTPS.
SSL certificates are what enable websites to move from HTTP to HTTPS, which is more secure. An SSL certificate is a data file hosted in a website's origin server. SSL certificates make SSL/TLS encryption possible, and they contain the website's public key and the website's identity, along with related information.
I figured it out, thanks to that link to some decent documentation.
For one thing, SSLSocket.connect() is only meant to be called on the client.
But the main problem is that I'm trying to take a GServer socket and upgrade it to SSL. Instead, I should use OpenSSL::SSL::SSLServer.
Also, I separated my certificate and private key into two files.
Here is the working server:
#!/usr/bin/ruby
require "socket"
require "openssl"
require "thread"
listeningPort = Integer(ARGV[0])
server = TCPServer.new(listeningPort)
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new(File.open("cert.pem"))
sslContext.key = OpenSSL::PKey::RSA.new(File.open("priv.pem"))
sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
puts "Listening on port #{listeningPort}"
loop do
connection = sslServer.accept
Thread.new {
begin
while (lineIn = connection.gets)
lineIn = lineIn.chomp
$stdout.puts "=> " + lineIn
lineOut = "You said: " + lineIn
$stdout.puts "<= " + lineOut
connection.puts lineOut
end
rescue
$stderr.puts $!
end
}
end
And client:
#!/usr/bin/ruby
require "socket"
require "thread"
require "openssl"
host = ARGV[0]
port = Integer(ARGV[1])
socket = TCPSocket.new(host, port)
expectedCert = OpenSSL::X509::Certificate.new(File.open("cert.pem"))
ssl = OpenSSL::SSL::SSLSocket.new(socket)
ssl.sync_close = true
ssl.connect
if ssl.peer_cert.to_s != expectedCert.to_s
stderrr.puts "Unexpected certificate"
exit(1)
end
Thread.new {
begin
while lineIn = ssl.gets
lineIn = lineIn.chomp
$stdout.puts lineIn
end
rescue
$stderr.puts "Error in input loop: " + $!
end
}
while (lineOut = $stdin.gets)
lineOut = lineOut.chomp
ssl.puts lineOut
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With