PHP CURL issues error "Unable to get local Issuer Certificate"

Asked

Viewed 14,581 times

4

I am trying to get data from an API using the CURL extension of PHP but it is not working. The same code on another server is working.

Here it works: Curl funciona

But it doesn’t work here anymore: Curl não funciona

The code is:

<?php

    $con = curl_init();
    $url = "https://api.moloni.com/v1/companies/getOne/?access_token=123456";
    $my_values = array('company_id' => 0);

    curl_setopt($con, CURLOPT_URL, $url);
    curl_setopt($con, CURLOPT_POST, true);
    curl_setopt($con, CURLOPT_POSTFIELDS, http_build_query($my_values));
    curl_setopt($con, CURLOPT_HEADER, false);
    curl_setopt($con, CURLOPT_RETURNTRANSFER, true);

    $res_curl = curl_exec($con);
    curl_close($con);

    $res_txt = json_decode($res_curl, true);
    if(!isset($res_txt['error'])){
        echo 'Result: '.print_r($res_txt,true).'';
    }else{
        echo 'Erro: '.print_r($res_txt,true).'';
    }

    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);

?>
  • 1

    Which error returns when "does not work"?

  • You are returning this error: Curl error: SSL Certificate problem: Unable to get local Issuer Certificate

  • This is the problem, SSL. You are accessing a secure URL and do not have the certificate

  • What do you mean? I don’t know anything about certificates. Is it a problem on server 2? It is that on server 1 the same code works without problems

  • Every certificate has an "Issuer" or "Certificate Authority (CA)" which is responsible for verifying the certificate’s signature. Your server 1 already has the CA file, so it works. On Linux you find the files on /etc/ssl/certs/, you need to check which is what this talking on server 2. By browser you can check the details of the certificate

  • Enables apache SSL ;), that is, enables the php extension php_openssl. See if this solves

  • php_openssl was already enabled. How do I know which certificate is missing through the browser? Thank you

  • I changed the answer to a better solution, see if it solves your problem.

Show 3 more comments

2 answers

9

For the error reported in the comments, the problem is in SSL.

There are alternatives, not very recommended to solve (or check if this is the problem).

Checking:

Appendage:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
// curl_setopt($ch, CURLOPT_CAINFO, 'local/crt.crt');

Explanation:

The CURLOPT_SSL_VERIFYHOST will verify whether the host that is connecting is the same as the certificate received, simple like this. When you are 2 he will then check the Subject Alternate Name and the Common Name field present in the certificate. Keep it off (0/false) will ignore the check.

The CURLOPT_SSL_VERIFYPEER is a bit more complex. When enabled it will check whether the certificate that was received (by whom you connected) was issued (possessing a CA trusted). In this case Curl will compare certificates whether or not it was issued by someone who trusts, this is recommended. Keep off (0) will cause you not to verify the authenticity of the certificate, leaving exposed even for certificates self-signed and for attacks man-in-the-Middle. The CURLOPT_CAINFO is rightly used when the CURLOPT_SSL_VERIFYPEER.

The CURLOPT_CAINFO lets you choose a location where the certificate is located on your server.

Try turn off everything (as present in the above excerpt) and see if the error persists, if continuing the certificate is not the problem.

Remember NEVER USE THIS IN PRODUCTION, you are turning off the SSL check if you use know that this is extremely vulnerable and unreliable!

Solution:

You have two options:

1. Generic (Several Certification Authority):

Get into https://curl.haxx.se/docs/caextract.html download the latest CA Bundle available for download.

OR

2. Specific certificate (specific Certification Authority):

You can choose to only rely on a single SSL issuer, rather than several as in the way above. That way you need who issued the certificate, so trust him.

Use Mozilla Firefox for this!

  1. Enter the desired site (example: https://www.openssl.org).
  2. Click on the green padlock (next to the website URL).
  3. Click on the arrow next to "Conexão segura" and click on "Mais informações".
  4. Click on "Ver certificado".

This will show all the information, but you want to save one in specific.

  1. Click on the tab "Detalhes".

There is a hierarchy, you need to save what is domain specific.

In my case there are:

Globalsign Root CA

Globalsign Domain Validation CA - SHA256 - G2

.openssl.org

Select the first (GlobalSign Root CA) and click on "Exportar".

Utilise:

Suppose the following structure:

seuphp.php (que usa o cURL)
seucertifiado.crt (que acabou de salvar)

Modify the code to:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, 'caminho/para/seucertificado.crt');

That will solve the problem.

The paths must be absolute!


Is that safe? Yes, it can be more.

If you only want to rely on a specific certificate, for much more security, you should use the CURLOPT_PINNEDPUBLICKEY, for this you need to have CURL in the version superior to 7.39.0 and have PHP in the version 7.0.7, because using an obsolete version of PHP and wanting security is a bit incoherent.

To do so get the certificate from the website (if you don’t have):

openssl s_client -servername www.example.com -connect www.example.com:443 < /dev/null | sed -n "/-----BEGIN/,/-----END/p" > /caminho/para/arquivo.pem

After that add this to CURL:

curl_setopt($ch, CURLOPT_PINNEDPUBLICKEY, 'caminho/para/seucertificado.pem');

This will check if the certificate informed to CURL is the same certificate that CURL got when connecting, if it is different the connection will be canceled.

This is very useful if you’re communicating with a payment website, such as Paypal, Moip, Pagseguro, Mercadopago, this makes it even more difficult for there to be a fake certificate that uses a trusted CA.

  • Inkeliz thank you so much for your reply! I followed your tutorial but still could not, now I get the following error: Curl error: error Setting Certificate Verify Locations: Cafile: seucertificado.crt Capath: None

  • Make sure that Cafile has the same name. If naming to 'biscoito.crt' should change in the code too, check the directory. If it can’t be the absence of ca-Certificates, try to reinstall using yum reinstall ca-certificates or similar.

  • I managed to solve, thank you very much Inkeliz!

  • You helped me too, thank you!

1

First of all, thank you for the detailed answer, @Inkeliz. I solved my problem with your answer, but I had to make a small change to work in my case ...

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, 'caminho/para/seucertificado.crt');

this path gave the following error: "error Setting Certificate Verify Locations: Cafile ...". So I changed your code a little bit:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 'caminho/para/seucertificado.crt');
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 'caminho/para/seucertificado.crt');

So it worked. I hope it helps for those who have similar problem ...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.