简体   繁体   English

尝试在 SSL 上创建一个简单的 Ruby 服务器

[英]Trying to create a simple Ruby server over SSL

I am trying to create a simple SSL client and server in Ruby.我正在尝试在 Ruby 中创建一个简单的 SSL 客户端和服务器。 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).我什至不确定我的证书是否采用可接受的格式(它在 base64 中,证书和私钥在文件中)。 The documentation is very scant and there doesn't seem to be much on the web in this area either.文档非常少,而且该区域的 web 似乎也没有太多内容。

Any ideas?有任何想法吗?

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.一方面, SSLSocket.connect() 仅用于在客户端上调用。

But the main problem is that I'm trying to take a GServer socket and upgrade it to SSL.但主要问题是我正在尝试使用 GServer 套接字并将其升级到 SSL。 Instead, I should use OpenSSL::SSL::SSLServer.相反,我应该使用 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 need to provide intermediate certificates, set sslContext.extra_chain_cert with them.如果您需要提供中间证书,请使用它们设置 sslContext.extra_chain_cert。

If use have letsencrypt certificates, use your fullchain.pem for sslContext.cert, privkey.pem for sslContext.key and ["chain.pem"] for sslContext.extra_chain_cert.如果使用有 Letsencrypt 证书,请将您的 fullchain.pem 用于 sslContext.cert,将 privkey.pem 用于 sslContext.key,将 ["chain.pem"] 用于 sslContext.extra_chain_cert。 Doing so, you can also test the connection to the server from your browser, with full chain verification.这样做,您还可以通过全链验证测试从浏览器到服务器的连接。

Note: this complements the author's answer https://stackoverflow.com/a/5873796/533510 , but it was rejected by user @joe, because he understands that this is not a complement for the question.注意:这是对作者的回答https://stackoverflow.com/a/5873796/533510的补充,但被用户@joe 拒绝,因为他明白这不是问题的补充。 If you agree to him, try using this answer as solution to the question!如果您同意他的观点,请尝试使用此答案作为问题的解决方案!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM