将SSL添加到连接

时间:2012-04-21 20:23:09

标签: ruby ssl openssl

如果需要先通过纯文本进行通信,如何在Ruby中使用SSL保护套接字?

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

总而言之,我试图实现RFC3207,客户端发送关键字“STARTTLS”,然后创建SSL连接。

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

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

如果您知道如何使用Ruby以外的其他语言进行此操作,只需发布​​代码并将其翻译,我已经在这方面工作了大约8个小时,我需要我能得到的一切

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

简而言之,我非常困难,我需要得到所有帮助

3 个答案:

答案 0 :(得分: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包中的utils(debian / ubuntu中的gnutls-bin)来测试starttls,因为它允许我随时启动握手:

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

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

答案 1 :(得分:3)

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

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

如果您想认真使用服务器证书的CN,可能还需要将服务器证书的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

答案 2 :(得分:0)

我已经取得了一些进展,保存以备将来使用:

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

随意编辑