Попытка создать простой сервер Ruby через SSL
Я пытаюсь создать простой SSL-клиент и сервер в Ruby. Но я получаю загадочное сообщение об ошибке, и документация не помогает.
Вот мой код сервера:
#!/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
Клиентский код похож:
#!/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
Когда я подключаюсь, я получаю эту ошибку как на сервере, так и на клиенте:
in `connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol (OpenSSL::SSL::SSLError)
Проблема может заключаться в том, что он не доверяет сертификату (самоподписанному). Я не уверен, как сообщить клиенту доверять этому сертификату. Выше, я поставил серверный сертификат в контексте, но это был просто выстрел в темноте. Я даже не уверен, что мой сертификат находится в приемлемом формате (он находится в base64 с сертификатом и закрытым ключом в файле). Документация очень скудная, и в этой области, похоже, не так много в Интернете.
Любые идеи?
Ответы
Ответ 1
Я понял, благодаря этой ссылке на приличную документацию.
С одной стороны, SSLSocket.connect() предназначен только для вызова на клиенте.
Но главная проблема заключается в том, что я пытаюсь использовать сокет GServer и обновлять его до SSL. Вместо этого я должен использовать OpenSSL:: SSL:: SSLServer.
Кроме того, я разделил свой сертификат и закрытый ключ на два файла.
Вот рабочий сервер:
#!/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
И клиент:
#!/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
если вам нужно предоставить промежуточные сертификаты, установите с ними sslContext.extra_chain_cert.
Если вы используете allowencrypt сертификаты, используйте fullchain.pem для sslContext.cert, privkey.pem для sslContext.key и [ "chain.pem" ] для sslContext.extra_chain_cert. При этом вы также можете проверить подключение к серверу из своего браузера с полной проверкой цепи.
Примечание: это дополняет ответ автора fooobar.com/info/368061/..., но он был отклонен пользователем @joe, потому что он понимает, что это не дополнение к вопросу. Если вы согласны с ним, попробуйте использовать этот ответ в качестве решения вопроса!