使用瘦身时如何选择证书颁发机构文件?

时间:2013-01-22 23:38:34

标签: ruby ssl thin certificate-authority

我知道我可以选择使用--ssl-verify验证客户端,但是如何指定要使用的CA链?我习惯提供一个文件(比如curl的--cacert或WEBrick的:SSLCACertificateFile),所以我准备好了,但我似乎找不到关于如何将它传递给{的文档{1}}。

1 个答案:

答案 0 :(得分:4)

简短的回答:你不能。

答案很长:你可以,但你必须更新构建ssl连接的EventMachine的C ++扩展,并通过EventMachine和Thin更新调用堆栈以传递证书颁发机构文件。

我是如何发现的:源代码!这一切都在github上

  • 瘦的命令行选项在thin:lib/thin/runner.rb

    中解析
    opts.separator "SSL options:"
    
    opts.on(      "--ssl", "Enables SSL")                                           { @options[:ssl] = true }
    opts.on(      "--ssl-key-file PATH", "Path to private key")                     { |path| @options[:ssl_key_file] = path }
    opts.on(      "--ssl-cert-file PATH", "Path to certificate")                    { |path| @options[:ssl_cert_file] = path }
    opts.on(      "--ssl-verify", "Enables SSL certificate verification")           { @options[:ssl_verify] = true }
    
  • 然后用于创建控制器

    controller = case
    when cluster? then Controllers::Cluster.new(@options)
    when service? then Controllers::Service.new(@options)
    else               Controllers::Controller.new(@options)
    end
    
  • thin:lib/controllers/controller.rb中,ssl选项被撤回以与服务器对象一起存储

    # ssl support
    if @options[:ssl]
      server.ssl = true
      server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => @options[:ssl_verify] }
    end
    
  • 并最终用于初始化与客户端的连接

    def initialize_connection(connection)
      connection.backend                 = self
      connection.app                     = @server.app
      connection.comm_inactivity_timeout = @timeout
      connection.threaded                = @threaded
    
      if @ssl
        connection.start_tls(@ssl_options)
      end
    
  • 此连接是EventMachine::Connection,在eventmachine:lib/em/connection.rb中定义。 EventMachine::Connection#start_tls将参数传递给EventMachine::set_tls_parms

    def start_tls args={}
      priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer)
    
      [priv_key, cert_chain].each do |file|
        next if file.nil? or file.empty?
        raise FileNotFoundException,
        "Could not find #{file} for start_tls" unless File.exists? file
      end 
    
      EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer)
      EventMachine::start_tls @signature
    end 
    
  • EventMachine::set_tls_parms是C ++扩展的一部分,在eventmachine:ext/rubymain.cpp中定义为五个参数C函数t_set_tls_parms

    rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4);
    
  • 同一文件中其他地方定义的t_set_tls_parms只会将ssl选项传递给evma_set_tls_parms

    static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer)
    {
      /* set_tls_parms takes a series of positional arguments for specifying such things
       * as private keys and certificate chains.
       * It's expected that the parameter list will grow as we add more supported features.
       * ALL of these parameters are optional, and can be specified as empty or NULL strings.
       */
      evma_set_tls_parms (NUM2ULONG (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile), (verify_peer == Qtrue ? 1 : 0));
      return Qnil;
    }
    
  • vanilla C函数evma_set_tls_parmseventmachine:ext/cmain.cpp中定义。它将ssl选项传递给EventableDescriptor的{​​{1}}方法:

    SetTlsParms
  • extern "C" void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); if (ed) ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false)); } 实例方法在SetTlsParms中定义,它真正做的就是在某些实例变量中缓存ssl选项。

    eventmachine:ed.cpp
  • 稍后在void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) { #ifdef WITH_SSL if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); if (privkey_filename && *privkey_filename) PrivateKeyFilename = privkey_filename; if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; #endif #ifdef WITHOUT_SSL throw std::runtime_error ("Encryption not available on this event-machine"); #endif } 实例方法(在同一文件中定义)中使用这些实例变量,并将其传递给初始化新的StartTls

    SslBox_t
  • void ConnectionDescriptor::StartTls() { #ifdef WITH_SSL if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding()); _DispatchCiphertext(); #endif 构造函数在SslBox_t中定义,它使用ssl选项初始化新的eventmachine:ext/ssl.cpp

    SslContext_t
  • SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), pSSL (NULL), pbioRead (NULL), pbioWrite (NULL) { /* TODO someday: make it possible to re-use SSL contexts so we don't have to create * a new one every time we come here. */ Context = new SslContext_t (bIsServer, privkeyfile, certchainfile); assert (Context); 构造函数在同一文件中定义,它使用标准OpenSSL C绑定的那些选项:

    SslContext_t

所以现在我们知道如何使用ssl选项。如果修改了调用链以将CA文件名和其他文件名一起传递到此点,比如// The SSL_CTX calls here do NOT allocate memory. int e; if (privkeyfile.length() > 0) e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); else e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); if (certchainfile.length() > 0) e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); else e = SSL_CTX_use_certificate (pCtx, DefaultCertificate); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); ,我们可以使用几个OpenSSL调用来添加权限文件:

const string &certauthfile

提交补丁来做这件事留给了足够积极的练习。