为连接添加SSL

6
当你需要先通过明文通信时,如何在Ruby中使用SSL保护套接字?
我不能使用OpenSSL::SSL::SSLServer,因为客户端需要首先请求SSL连接。
长话短说,我正在尝试实现RFC3207,其中客户端发送关键词“STARTTLS”,然后创建SSL连接。
我的问题是:“在服务器发送'220 OK'之后,如何创建SSL连接?”
我知道可以在客户端使用OpenSSL::SSL::SSLSocket,但我不知道该怎么在服务器端操作。
如果您知道如何在除Ruby以外的其他语言中执行此操作,请发布代码,我已经花了约8个小时在这上面,我需要尽可能多的帮助。

我已经在 #ruby-lang 提问了,但没有得到帮助。我也尝试在服务器和客户端同时将 Socket 对象包装在 SSLSockets 中,但这也没有起作用。

简而言之,我非常困惑,需要尽可能多的帮助。

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)中的工具来测试starttls,因为它允许我在想要的时候启动握手:

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

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


我拆开了OpenSSL SSLServer的实现,作为编写我今天使用的代码的参考,除了#sync_close之外,它看起来几乎相同,但仍然会抛出错误 :/ - lcarpenter

3
我创建了这个 代码片段 来演示如何设置一个简单的TLS服务器。您可能想要省略行62-67,它们是为了说明trunk上的一个新功能。
除此之外,这是一个完全可工作的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

我能削减多少证书生成的内容?我在考虑将其放入脚本中,生成一年的证书并将证书存储在源代码控制中。你上面提供的简单示例(不考虑上下文重用)是我现在正在使用的,但偶尔会出现奇怪的错误,我想你已经见过了 :) - lcarpenter

0

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

  • 是的,你应该在两端都使用OpenSSL::SSL::SSLSocket
  • 在服务器端,您必须创建一个OpenSSL::SSL::SSLContext对象,传递一个带有您希望使用的协议和"_server"附加到末尾的符号,参见OpenSSL::SSL::SSLContext::METHODS,简而言之,对于RFC3207,请使用":TLSv1_server"甚至不需要这样做,在服务器端创建具有证书的上下文,然后调用#accept等待客户端传输
  • 将SSL证书传递给ctx对象

根据您的需要进行编辑


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接