Search code examples
perlsslsoapsoaplite

SOAP::Lite connection to SSL Server


I am completely new to Perl. I have written a web service in C# running on a Windows box and am calling it from a Perl script on a Linux box using SOAP::Lite. My code works fine using the http:// handler. I'm now trying to get it to work with https://

The code is:

    use SOAP::Lite;
    use POSIX;

    #Tell Perl to trust the testserver's cert
    #$ENV{HTTPS_CA_DIR} = '/usr/local/share/ca-certificates';
    $ENV{HTTPS_CA_FILE} = '/home/myusername/TestServer_CA_Cert.cer';
    #$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;


    my $uri = 'http://Automation/';
    my $proxy = 'https://TestServer/ToolsAutomation/AutomationInterface.asmx';
    my $method = SOAP::Data->name('SubmitAutomationJob')->attr({xmlns => $uri});

    #-Code that sets us @params deleted for readability-

    my $myServer = SOAP::Lite
        -> on_action(sub {join '', $uri, $_[1]})
        -> proxy($proxy)
    #        -> proxy($proxy, ssl_opts => [ SSL_verify_mode => 0 ] )
    #        -> proxy($proxy, ssl_opts => [SSL_ca_file => $ServerCACert])
            ;

    my $myCALL = $myServer #This is line 36 in the original script
                -> call($method => @params);

    print 'Result: ' . $myCALL->result;

When I run the code as posted, I get error "500 Can't connect to TestServer:443 at line 36." A network trace on the server side shows the client sends a SYN, server sends a SYN/ACK, client sends and ACK, then the client immediately sends a FIN/ACK.

I have tried various things to get this to work. I have tried:

  • Uncommenting the line #$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; results in "500 Can't connect to TestServer:443 (certificate verify failed). Network trace shows that at least part of HTTPS negotiation is working.
  • Uncommenting the line proxy($proxy, ssl_opts => [ SSL_verify_mode => 0 ] ) works, but of course this doesn't verify the server's identity.
  • Uncommenting the line -> proxy($proxy, ssl_opts => [SSL_ca_file => $ServerCACert]) also gives a "certificate verify failed" error.
  • Changing the path to $ENV{HTTPS_CA_FILE} gives an error "SSL_ca_file /home/myusername/TestServer_CA_cert.ceeer does not exist" at the line 36. I take this as a good sign; the code is finding the certificate I tell it to use for server auth.
  • Uncommenting both the $ENV{HTTPS_CA_DIR} and the $ENV{HTTPS_CA_FILE} lines gives the error "only SSL_ca_path or SSL_ca_file should be given."

I have imported my CA's cert into Firefox on the Linux machine and have successfully browsed to my testserver without getting an SSL warning, and then removed the cert from Firefox and browsed back and received the SSL warning, so I'm reasonably confident that the cert file I have is good.

Does anyone have any ideas on how to fix this code? Is there some attribute of the CA's cert itself that I should check? I find it puzzling that Firefox has no problem trusting a server whose SSL cert is signed with this CA's cert, but I can't convince Perl to do the same.

Any feedback is appreciated. And do remember: I'm an absolute Perl n00b. Before I Googled how to call a web service from Perl, the only think I knew about Perl was "Larry Wall."

Thanks!

UPDATE:

At the request of Steffen Ullrich below, I ran the following:

perl -MIO::Socket::SSL=debug4 program.pl

The output I received is:

me@myMachine:~$ perl -MIO::Socket::SSL=debug4 ./program.pl 
DEBUG: .../IO/Socket/SSL.pm:402: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:404: socket connected
DEBUG: .../IO/Socket/SSL.pm:422: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:1388: SSL structure creation failed

Solution

  • When I run the code as posted, I get error "500 Can't connect to TestServer:443 at line 36." A network trace on the server side shows the client sends a SYN, server sends a SYN/ACK, client sends and ACK, then the client immediately sends a FIN/ACK.

    If this is really like you describe it then no data gets exchanged which means th client does not even attempt to do an SSL handshake. This means that probably the way you called it is wrong:

    #        -> proxy($proxy, ssl_opts => [ SSL_verify_mode => 0 ] )
    #        -> proxy($proxy, ssl_opts => [SSL_ca_file => $ServerCACert])
    

    I'm pretty sure that ssl_opts does not need an array ([...]) but a hash ({...}).

    EDIT: It looks like you need an array and not a hash because ssl_opts are handled in a special way by SOAP::Lite, i.e. they are not directly given to the constructor of LWP::UserAgent as the documentation would imply. I've filed a bug about it.

    DEBUG: .../IO/Socket/SSL.pm:1388: SSL structure creation failed
    

    Based on the code I got from you I could reproduce the problem, but only if the given CA file was corrupt. I expect that this is the case for you. Note that the file must look like this:

    -----BEGIN CERTIFICATE-----
    MIIDKDCCApGgAwIBAgIJAOetiwdVihcnMA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNV
    .... more base64 encoded stuff ...
    M0LUhtV8z6OcURDEfnQBsgLCPODc9Mg4FEViDGVaZ1i233ZeAlLzXSZeXL8=
    -----END CERTIFICATE-----