Попытка создать простой сервер Ruby через SSL - PullRequest
14 голосов
/ 03 мая 2011

Я пытаюсь создать простой клиент и сервер 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 с сертификатом и закрытым ключом в файле).Документация очень скудная, и в этой области, похоже, не так много информации в Интернете.

Есть идеи?

Ответы [ 2 ]

15 голосов
/ 03 мая 2011

Я понял это благодаря этой ссылке на некоторую приличную документацию.

С одной стороны, 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
0 голосов
/ 04 мая 2017

, если вам нужно предоставить промежуточные сертификаты, установите sslContext.extra_chain_cert с ними.

Если вы используете сертификаты letsencrypt, используйте ваш fullchain.pem для sslContext.cert, privkey.pem для sslContext.key и ["chain.pem "] для sslContext.extra_chain_cert.Таким образом, вы также можете проверить соединение с сервером через браузер с полной проверкой цепочки.

Примечание. Это дополняет ответ автора https://stackoverflow.com/a/5873796/533510, но он был отклонен пользователем @joe,потому что он понимает, что это не дополнение к вопросу.Если вы согласны с ним, попробуйте использовать этот ответ в качестве решения вопроса!

...