Search code examples
perlsslpaypallwplwp-useragent

Perl, LWP "certificate verify failed" with paypal.com


Not 100% sure this is a Perl issue, but it seems to be. I have an IPN script that connects with PayPal to verify transactions. It was working fine until yesterday, when I installed LWP::Protocol::https. Since then, it's been failing with the error:

Can't connect to www.paypal.com:443 (certificate verify failed)

LWP::Protocol::https::Socket: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed at /usr/local/share/perl5/LWP/Protocol/http.pm line 47.

Running GET https://www.paypal.com from bash (which uses LWP) yields the same error message. OTOH, running GET https://www.gmail.com is successful. Running openssl s_client -host paypal.com -port 443 returns (among other things) Verify return code: 0 (ok). curl "https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate" successfully receives a response from PayPal. So it does seem to be Perl-specific.

Module versions:

LWP 6.13
LWP::Protocol::https 6.06
IO::Socket::SSL 2.015
Mozilla::CA 20141217 (note: I've tried the script both using Mozilla::CA and without it... results have been the same)

Please let me know if there are other relevant modules. Perl version is 5.10.1. Server OS is RHEL 6.


Solution

  • Mozilla::CA 20141217 (note: I've tried the script both using Mozilla::CA and without it... results have been the same)

    In short: I don't know what "without it" means for RHEL6 but please try again with Mozilla::CA 20130114 or with the "older ca-bundle" linked from http://curl.haxx.se/docs/caextract.html.

    Details: The certificate chain you get from www.paypal.com

    [0] www.paypal.com
    [1] Symantec Class 3 EV SSL CA - G2
    [2] VeriSign Class 3 Public Primary Certification Authority - G5
    

    The last certificate in the chain is signed by the 1024 certificate

    /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
    

    Since 1024 bit certificates where removed by Mozilla end of last year you will not find them in the current Mozilla::CA any longer. But browsers don't need the old certificate because the create the trust chain based on certificates [0] and [1] already because they use a built-in certificate instead of the certificate [2] send by the server.

    While this newer built-in certificate is also included in Mozilla::CA it will not be used because of a long-standing bug in how OpenSSL validates certificates: it will always try to validate the longest chain and not check if a shorter chain is possible.

    For more details about this problem see

    The problem can be resolved by using the flag X509_V_FLAG_TRUSTED_FIRST which was introduced with OpenSSL 1.02 (released 4 month ago and probably not in RHEL yet) or by using an even newer and not yet released version of OpenSSL where they finally fixed the problem (see https://rt.openssl.org/Ticket/Display.html?id=3637&user=guest&pass=guest).

    The problem can be worked around by having the older 1024 bit CA certificates still available, i.e either using an older Mozilla::CA or CA bundle or using the system CA store which usually includes these older CA. See also:

    • A current bug report against IO::Socket::SSL to use the X509_V_FLAG_TRUSTED_FIRST by default (if available). This flag gets set with 2.016 (not yet released) but needs a version of Net::SSLeay which exports this flag (not yet released) and OpenSSL 1.02 (not included in RHEL).
    • A pull request against LWP to use the default CA on the system instead of Mozilla::CA. This would probably save the problem for you too. Note that Debian/Ubuntu have a similar patch included. I don't know about the version of LWP shipped with RHEL.