gr3m1in has asked for the wisdom of the Perl Monks concerning the following question:

good day, monks!

my program at my LAN host listens an IO::Socket:INET as HTTPS at port 443 and upgrades connections to IO::Socket::SSL with specified cert/key pair.

i need to detect the SNI hostname to which a client is connecting and generate a dynamic cert/key pair according to the detected hostname.

as the SNI documentation says, the hostname is reachable before actual handshake, but i can't figure out how to get it during upgrade to IO::Socket::SSL and dynamically supply just generated cert/key pair to upgrading object.

thanks for any help!

regards, gr3m1in

  • Comment on Dynamic SNI certificates while upgrading to SSL

Replies are listed 'Best First'.
Re: Dynamic SNI certificates while upgrading to SSL
by noxxi (Pilgrim) on Dec 18, 2014 at 12:24 UTC

    There is no explicit support in IO::Socket::SSL to do this, that is it can only deal with fixed certificates for SNI and has no way to create a certificate on demand for SNI. The way SNI on the server side works is by supplying an tls_ext_servername_callback and set the relevant context there.

    I suggest you have a look at the code for IO::Socket::SSL and look how it uses Net::SSLeay::CTX_set_tlsext_servername_callback. Your code must work in a similar way, but instead of using an already created SSL context you have to create a new context with your own certifcates in this callback.

      thanks for this!

      according to CPAN

      http://search.cpan.org/~mikem/Net-SSLeay-1.66/lib/Net/SSLeay.pod#Low_level_API:_Server_side_Server_Name_Indication_%28SNI%29_support

      the goal can be reached with code like this:

      # create default context my $ctx = Net::SSLeay::CTX_new or die; Net::SSLeay::CTX_set_cipher_list($ctx, 'ALL'); Net::SSLeay::set_cert_and_key($ctx, 'cert.pem','key.pem') or die; # create new context for each new hostname my %hostnames = (); Net::SSLeay::CTX_set_tlsext_servername_callback( $ctx, sub { my $ssl = shift; my $h = Net::SSLeay::get_servername($ssl); unless (exists $hostnames{$h}) { $hostnames{$h}->{ctx} = Net::SSLeay::CTX_new or die; Net::SSLeay::CTX_set_cipher_list($hostnames{$h}->{ctx}, 'A +LL'); $hostnames{$h}->{cert} = ... # generate certificate and $hostnames{$h}->{key} = ... # key based on hostname in $h # and re-use them in future f +rom hash as below Net::SSLeay::set_cert_and_key( $hostnames{$h}->{ctx}, $hostnames{$h}->{cert}, $hostnames{$h}->{key} ) or die; } Net::SSLeay::set_SSL_CTX($ssl, $hostnames{$h}->{ctx}); } );

      but every new cert/key pair generation will use some time.
      in my case, the script is a multiplexing server based on IO::Select, so it reads and writes with short parts of data to prevent any long/heavy transfer to block the others.
      and each time the new pair is generated the script will block for this time.

      is there a way to solve this in non-blocking manner?

      thanks!

        > is there a way to solve this in non-blocking manner?

        I don't think so. To do this in a sensible non-blocking way you would need to have control about the SSL state machine. But OpenSSL only exposes some hooks into this machine with callbacks and I don't think you can have multiple servername callbacks on the same context run in parallel.