2011-05-03 19 views
14

Estoy intentando crear un servidor SSL simple y servidor en Ruby. Pero recibo un mensaje de error críptico y la documentación no es de ayuda.Intentando crear un servidor simple de Ruby sobre SSL

Aquí está mi código de servidor:

#!/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 

El código de cliente es 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 

Cuando conecto, consigo este error en el servidor y el cliente:

in `connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol (OpenSSL::SSL::SSLError) 

El problema podría ser que no confía en el certificado (autofirmado). No estoy seguro de cómo decirle al cliente que confíe en ese certificado. Arriba, he puesto el certificado del servidor en el contexto, pero eso fue solo una oportunidad en la oscuridad. Ni siquiera estoy seguro de que mi certificado esté en un formato aceptable (está en base64 con el certificado y la clave privada en el archivo). La documentación es muy escasa y tampoco parece haber mucho en la web en esta área.

¿Alguna idea?

+0

Ahh, esto ayuda: http://stackoverflow.com/questions/4730544/ruby-openssl-documentation – Fantius

Respuesta

15

Lo he descubierto, gracias a ese enlace a una documentación decente.

Por un lado, SSLSocket.connect() solo debe invocarse en el cliente.

Pero el problema principal es que estoy tratando de tomar un socket GServer y actualizarlo a SSL. En cambio, debería usar OpenSSL :: SSL :: SSLServer.

Además, separé mi certificado y clave privada en dos archivos.

Aquí es el servidor de trabajo:

#!/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 

y el cliente:

#!/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 
+2

¿Podría publicar los comandos para generar cert.pem y priv.pem? – adamwong246

+1

http://www.cryptosys.net/pki/rsakeyformats.html – Fantius

+0

No puedo editar: 'stderrr' debe ser' $ stderr' – ribamar

0

si es necesario para proporcionar los certificados intermedios, establecidos sslContext.extra_chain_cert con ellos.

Si el uso tiene certificados letprypt, use su fullchain.pem para sslContext.cert, privkey.pem para sslContext.key y ["chain.pem"] para sslContext.extra_chain_cert. Al hacerlo, también puede probar la conexión al servidor desde su navegador, con la verificación de cadena completa.

Nota: esto complementa la respuesta del autor https://stackoverflow.com/a/5873796/533510, pero el usuario @joe la rechazó porque entiende que esto no es un complemento para la pregunta. Si está de acuerdo con él, intente utilizar esta respuesta como solución a la pregunta.