Search code examples
perlmojolicious

Mojo::UserAgent TLS/SSL certificate authentication


I am new to Mojolicious and I am trying to get a Mojo::UserAgent script to use both a TLS certificate authority and a TLS certificate file but certificate file is password protected. I am having issues passing the password so the client certificate can be opened.

I have the following code:


#!/usr/bin/env perl

use Modern::Perl;
use Mojo::UserAgent;

IO::Socket::SSL::set_defaults(SSL_passwd_cb => sub {return "password";});
my $ua = Mojo::UserAgent->new;
my $base_dir = '/path/to/certs/';
$ua->ca($base_dir . 'ca-cert.crt');
$ua->cert($base_dir . 'clientcert.crt');
my $tx = $ua->build_tx(POST => '/POST HTTP/1.1');
$tx->req->url->parse('https://example.com:12345');
$ua->start($tx);

if(my $res = $tx->success) {
  say $res->body;
  print Dumper($tx);
} else {
  my ($err, $code) = $tx->error;
  say $code ? "$code response: $err" : "Connection error: $err";
}

I have verified the certificate with the following:

openssl s_client -connect host:port -CApath /path/to/cert - CAfile ca-cert.crt -cert clientcert.crt

I get prompted with:

Enter pass phrase for clientcert.crt:

I enter the password and it authenticates correctly.

So, how do I get the password to IO::Socket::SSL?


Solution

  • I found a solution (and reported the issue), so for now you can solve this with the following.

    In Mojo::IOLoop::Client changed the following:

    SSL_key       => $args->{tls_key},
    #SSL_key_file       => $args->{tls_key},
    

    Then, you can authenticate with the following code:

    #!/usr/bin/env perl
    
    use Modern::Perl;
    use Mojo::UserAgent;
    
    my $ua = Mojo::UserAgent->new;
    my $base_dir = '/path/to/certs/';
    $ua->ca($base_dir . 'ca-cert.crt');
    $ua->cert($base_dir . 'clientcert.crt');
    
    my $bio = Net::SSLeay::BIO_new_file($base_dir . 'clientcert.crt', 'r');
    my $privkey = Net::SSLeay::PEM_read_bio_PrivateKey($bio, undef, 'password');
    $ua->key($privkey);
    
    my $tx = $ua->post('https://example.com:12345');
    
    if(my $res = $tx->success) {
      say $res->body;
      print Dumper($tx);
    } else {
      my ($err, $code) = $tx->error;
      say $code ? "$code response: $err" : "Connection error: $err";
    }