6

当您需要首先通过明文进行通信时,如何在 Ruby 中使用 SSL 保护套接字?

我不能使用OpenSSL::SSL::SSLServer,因为首先请求 SSL 连接是客户的责任

长话短说,我正在尝试实现 RFC3207,客户端发送关键字“STARTTLS”,然后创建 SSL 连接。

我的问题是“服务器发送‘220 OK’后如何创建 SSL 连接?”

我知道我可以OpenSSL::SSL::SSLSocket在客户端使用,但我不知道在服务器端该怎么做

如果你知道如何用 Ruby 以外的语言做到这一点,只需发布​​代码,我会翻译它,我已经为此工作了大约 8 个小时,我需要我能得到的一切

我在#ruby-lang 中询问过,但无济于事,我尝试同时在服务器和客户端上的 SSLSockets 中包装 Socket 对象,但这也不起作用

简而言之,我很困,我需要我能得到的所有帮助

4

3 回答 3

3

您必须将 SSLServer 的 start_immediately 字段设置为 false 才能以纯文本模式启动 SSL 服务器。在任何时候(即当您从客户端收到 STARTTLS 命令时),您都可以调用 SSLSocket 的 accept 方法来启动 SSL/TLS 握手。客户当然必须同意协议:)

这是我为测试而编写的示例服务器:

#!/usr/bin/ruby

require 'socket';
require 'openssl';

certfile = 'mycert.pem';
port = 9002;

server = TCPServer.new( port );

# Establish an SSL context 
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new( File.open( certfile ) );
sslContext.key = OpenSSL::PKey::RSA.new( File.open( certfile ) );

# Create SSL server
sslServer = OpenSSL::SSL::SSLServer.new( server, sslContext );

# Don't expect an immidate SSL handshake upon connection.
sslServer.start_immediately = false;

sslSocket = sslServer.accept;

sslSocket.puts( "Toast.." );

# Server loop
while line = sslSocket.gets

        line.chomp!;

        if "STARTTLS" == line
                # Starting TLS
                sslSocket.accept;
        end

        sslSocket.puts( "Got '#{line}'" );

end

sslSocket.close;

我确定原始发帖人知道如何测试 STARTTLS,但我们其他人可能需要这个提醒。实际上,我通常使用 GNUTLS 包(debian/ubuntu 中的 gnutls-bin)中的 utils 来测试 starttls,因为它允许我随时开始握手:

$ gnutls-cli --starttls --port 9002 --insecure localhost

这以纯文本 TCP 套接字模式连接。键入一些行并让它们回显。此流量未加密。如果您发送STARTTLSsslSocket.accept则调用 ,并且服务器等待 SSL 握手。按 ctrl-d (EOF) 从 gnutls 客户端开始握手,并观察它建立加密的 SSL 连接。随后的行也将被回显,但流量现在已加密。

于 2012-05-06T20:55:03.753 回答
3

我创建了这个gist来说明如何设置最小的 TLS 服务器。您可能想省略第 62-67 行,这是为了说明中继上的一个新功能。

但除此之外,它是一个功能齐全的 TLS 服务器,您可以在其上构建以添加更多功能。

如果您想认真使用它,您可能还想将服务器证书的 CN 从“localhost”更改为真实域:)

您可能会注意到,工作的最大部分实际上是正确设置 PKI 方面。核心服务器部分是这样的:

ctx = OpenSSL::SSL::SSLContext.new
ctx.cert = ... # the server certificate
ctx.key = ... # the key associated with this certificate
ctx.ssl_version = :SSLv23
tcps = TCPServer.new('127.0.0.1', 8443)
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
ssls.start_immediately = true
begin
  loop do
    ssl = ssls.accept
    puts "Connected"
    begin
      while line = ssl.gets
        puts "Client says: #{line}"
        ssl.write(line) # simple echo, do something more useful here
      end
    ensure
      ssl.close
    end
  end
ensure
  tcps.close if tcps
end
于 2012-06-17T14:52:51.347 回答
0

我在这方面取得了一些进展,保存以备将来使用:

  • 是的,您应该在两端使用 OpenSSL::SSL::SSLSocket
  • 在服务器端,您必须创建一个 OpenSSL::SSL::SSLContext 对象,传入一个带有您希望使用的协议的符号,并在末尾附加“_server”,请参阅 OpenSSL::SSL::SSLContext::METHODS我的意思是,简而言之,对 RFC3207 使用“:TLSv1_server”甚至不需要这样做,在服务器端使用证书创建上下文,然后在套接字上调用#accept 以等待客户端传输
  • 将 SSL 证书传递给 ctx 对象

随意编辑

于 2012-04-21T22:43:42.417 回答