ID: 49419 User updated by: Jacek at jacekk dot info Reported By: Jacek at jacekk dot info Status: Open Bug Type: OpenSSL related Operating System: Ubuntu PHP Version: 5.3.0 New Comment:
> what version have you linked against with your PHP? I've tested it on PHP linked against OpenSSL 0.9.8g (Ubuntu LTS) and 0.9.8k (Gentoo) I understand how certificate checking works and I've provided wrong chain - my bad, however with the file you linked PHP still shows warnings form the first message. One more question: must OpenSSL check whole path, if one of intermediate certificates exists in cafile? Previous Comments: ------------------------------------------------------------------------ [2009-09-04 06:59:44] ryan+phpbugs at sleevi dot com I was unable to reproduce the "good" OpenSSL output that you described, using OpenSSL FIPS 1.2. For documentation sake (and because everything I'm about to explain is relative to that, which is equivalent to 0.9.8f code more or less), what version have you linked against with your PHP? Running openssl s_client -connect www.verisign.com:443 -CAfile chain.pem I get CONNECTED(00000003) depth=3 /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority verify error:num=19:self signed certificate in certificate chain verify return:0 --- Certificate chain 0 s:/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/2.5.4.15=V1.0, Clause 5.(b)/serialNumber=2497886/C=US/postalCode=94043/ST=California/L=Mountain View/streetAddress=487 East Middlefield Road/O=VeriSign, Inc./OU=Production Security Services/CN=www.verisign.com i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)06/CN=VeriSign Class 3 Extended Validation SSL SGC CA 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)06/CN=VeriSign Class 3 Extended Validation SSL SGC CA i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority 3 s:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority --- Using the chain file at http://pastebin.com/f16f72c6e and I am able to use the code without the issues you describe (There is an HTTP 500 error with the PayPal site, but that demonstrates the connection is successful). The certificate in this file corresponds to the last certificate in the chain as supplied by both Verisign and Paypal. The explanation of "why" this works follows, and is based on the 0.9.8/OpenSSL FIPS Module 1.2 code. While I cannot be certain that this explanation fully explains your problem, based on those missing pieces of information, it may shed light on the issues and limitations of the 'cafile' and 'capath' arguments. I do not believe that what you want to work will work the way you've described, which is due to OpenSSL limitations. In the chain file you provided, the two certificates you provided are: subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 issuer= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 and subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)06/CN=VeriSign Class 3 Extended Validation SSL CA issuer= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5 The first certificate in the supplied chain file corresponds to being the issuer of the certificate at index 1 in the above output from OpenSSL. Thus, one would expect the chain to go from index 0 (www.verisign.com) -> index 1 (EV SSL SGC CA) -> chain file 0 (Public Primary CA - G5). As chain file 0 is both a self-signed certificate and in the trusted list, one would presume the connection is now trusted/verified. However, OpenSSL's verify code is set to respect the ordering supplied by the remote server over the preference of a chain file when used for certificate path building. The untrusted list of certs receives precedence when building the chain, with the capath, cafile, and any other lookups only being consulted to complete any missing parts of the chain. This can be found in the OpenSSL sources in crypto/x509/x509_vfy.c X509_verify_cert. The comment in the code states "if we were passed a cert chain, use it first", in reference to the 'untrusted' chain. The Verisign server, above, is supplying a chain that actually terminates at "Class 3 Public Primary Certification Authority" (Index 3) This is the certificate that would need to be contained in chain.pem for verification to happen properly, as this is the certificate that OpenSSL (ergo, by proxy, PHP) expects to find in the trusted store (created by the cafile/capath options). If the Verisign server were instead supplying the (intermediate) certificate "CN=VeriSign Class 3 Public Primary Certification Authority - G5" (Index: 2) as the last certificate, then the chain.pem you supplied would still not work, because the Verisign server is supplying a version who says its issuer is the "Class 3 Public Primary Certification Authority". As such, that is the certificate that would be looked up, rather than the self-signed version that you have in your chain.pem. If the Verisign server was supplying the last certificate as "CN=VeriSign Class 3 Extended Validation SSL SGC CA" (Index 1), then the code you supplied *WOULD* work for Verisign, because OpenSSL would have an incomplete chain, and then examine chain.pem to inject the trusted (self-signed) version of the G5 key. For the PayPal example, the chain I receive from openssl s_client -connect www.paypal.com:443 is Certificate chain 0 s:/C=US/ST=California/L=San Jose/O=PayPal, Inc./OU=Information Systems/CN=api-3t.paypal.com i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)05/CN=VeriSign Class 3 Secure Server CA 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)05/CN=VeriSign Class 3 Secure Server CA i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority Here, as best I can tell, none of the certificates in your chain.pem match certificates in this list. However, using the chain.pem I supplied above, which contains the "Class 3 Public Primary Certification Authority", the code executes as intended. ------------------------------------------------------------------------ [2009-08-30 18:46:59] Jacek at jacekk dot info Description: ------------ PHP cannot validate some (VeriSign's?) certificate chains correctly. openssl s_client works fine with the same input. Verification of thawte chain works well. chain.pem is available at http://pastebin.com/f4ab25a9a OpenSSL: $ openssl s_client -connect www.verisign.com:443 -CAfile chain.pem (...) Verify return code: 0 (ok) (...) Reproduce code: --------------- <?php $ssl = array( 'verify_peer' => TRUE, 'verify_depth' => 5, 'allow_self_signed' => FALSE, 'cafile' => 'chain.pem', 'capture_peer_cert' => TRUE, 'capture_peer_chain' => TRUE, ); $context = stream_context_create(array( 'ssl' => $ssl, )); file_get_contents('https://api-3t.paypal.com/', NULL, $context); file_get_contents('https://www.verisign.com/', NULL, $context); ?> Expected result: ---------------- Nothing Actual result: -------------- Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /home/me/test/test.php on line 14 Warning: file_get_contents(): Failed to enable crypto in /home/me/test/test.php on line 14 Warning: file_get_contents(https://api-3t.paypal.com/): failed to open stream: operation failed in /home/me/test/test.php on line 14 Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /home/me/test/test.php on line 15 Warning: file_get_contents(): Failed to enable crypto in /home/me/test/test.php on line 15 Warning: file_get_contents(https://www.verisign.com/): failed to open stream: operation failed in /home/me/test/test.php on line 15 ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=49419&edit=1