5

I'm currently testing an API on a website with a certificate by executing a php script that uses curl, in command line on my local windows machine. But the script never manages to reach the server. I have looked up solutions for this apparently frequent error, and tried the following, without success:

  • Made sure the certificate is still valid.
  • Made sure that the openssl php extension is enabled in php.ini, and that the .dll is indeed there.
  • Downloaded the latest certificate bundle from https://curl.haxx.se/docs/caextract.html, as explained in the answer to this question and added it to php.ini. I have also checked that the certification authority for the website I'm trying to contact is indeed in that bundle (in my case, Comodo).
  • Downloaded an older certificate bundle that's closer to the beginning of the website's certificate validity period.
  • Downloaded the certificate directly from the certification authority via this link, and added the .crt to my php.ini file.
  • Converted the .crt file retrieved above to .pem using this tool, then adding it to php.ini (replacing the one above).

Here is the script I'm using for testing (I'm executing it using php in command line only):

$data = 'username=myuser&password=mypassword';
$ch = curl_init('https://my.website.com/auth');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_VERBOSE, true);
//curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
//curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_exec($ch);
curl_close($ch);

If I uncomment the two lines in there to bypass certificate validation entirely, it works perfectly, but I want to avoid that.

In all cases, I have tried path with both slashes and backslashes, I am certain that the correct php.ini is used, and that php does have access to the .pem file location. This is confirmed by the output of the php script (I have replaced the actual url I'm using):

> *   Trying 192.168.200.11...
> * TCP_NODELAY set
> * Connected to my.website.com (192.168.200.11) port 443 (#0)
> * ALPN, offering http/1.1
> * successfully set certificate verify locations:   
> CAfile: C:\Development\examples\COMODORSADomainValidationSecureServerCA.pem  
> CApath: none
> * NPN, negotiated HTTP1.1
> * SSL certificate problem: unable to get issuer certificate
> * stopped the pause stream!
> * Closing connection 0

I'm currently out of ideas for checking what I did wrong and how to fix it.

Michaël Vreux
  • 316
  • 1
  • 2
  • 14
  • "unable to get issuer certificate" always mean that you receive from remote end a certificate for which locally you can not find a certificate signing it. You will first need to see exactly what certificate you got, and who issued it. Then you will need to make sure to add this issuer certificste in your relevant truststore. – Patrick Mevzek Mar 14 '19 at 23:17
  • The difficult part is really to know how one is supposed to actually retrieve the certificate needed by curl. And I can now thank firefox for having the tools for doing just that. – Michaël Vreux Mar 15 '19 at 10:14
  • Update 2020: firefox is no longer able to provide the functionality I needed, see my answer below for a more stable way to do this. – Michaël Vreux Apr 21 '20 at 14:37

2 Answers2

4

So, in the end, here is how I finally managed to do it: as the error suggests, and couple of answers also points out to, the Certification Authority's certificate chain is missing from my configuration, and is not in the pack downloaded from https://curl.haxx.se/docs/caextract.html.

Indeed, to successfully connect in HTTPS to a website, on top of your own website's certificate, you also need additional certificates related your Certification Authority. This can be checked in the certification path after exporting the website certificate and visualizing it with a tool meant for this. Windows has such a tool that will be used by default with .cer files - not a text editor.

Usually, those additional certificates are already there by default, but in my case, they were not. My certification path looks like this:

Sectigo
   |_____Sectigo RSA Domain Validation Secure Server CA
            |_____*.example.com 

I had to retrieve one certificate for each of the 3 parts of my certification path (3 certificates in total).

So in the end, I found this question that pointed me in the right direction: I had to export my website's certifitate in .cer format (same format as PEM, just another extension), open my certificate in windows, and export each portion of the certification path separately to obtain all 3 certificates.

I then copied all 3 certificates into the end of the file downloaded from https://curl.haxx.se/docs/caextract.html , which I located at C:/Development/examples/cacert.pem (you probably want to put that file in a place closer to your php installation folder).

Then, I changed my php.ini configuration to

curl.cainfo=C:/Development/examples/cacert.pem openssl.cafile=C:/Development/examples/cacert.pem

And that did the trick! I can now connect to the website without issue.

Patrick Mevzek
  • 10,995
  • 16
  • 38
  • 54
Michaël Vreux
  • 316
  • 1
  • 2
  • 14
  • Hint: when exporting the certificate, choose Base-64 encoded X.509 (.CER) format, or you cannot copy it into a `.pem` file – Jing He Nov 13 '20 at 14:24
0

Are you able to connect to the server from the command line with curl?

curl -vs https://my.website.com/auth --cacert C:\Development\examples\COMODORSADomainValidationSecureServerCA.pem 

Probably there is a mismatch between the server certificate and the bundle you've downloaded, i.e. the certificate is not in the bundle.

If you have openssl installed you can extract the CA cert from your server with

openssl s_client -showcerts -servername server -connect server:443 > cacert.pem

The description of a similar problem can be found here

da-sha1
  • 706
  • 5
  • 7
  • Unfortunately, I don't have a linux environment or cygwin to test this, but it did give the important clue that what is wrong is possibly the whole certificate chain fully or partially missing, which was indeed the case! – Michaël Vreux Mar 15 '19 at 10:12