php - cURL error 60: SSL certificate: unable to get local issuer certificate


Translate

I use WAMP on a local development environment and am trying to charge a credit card but get the error message:

cURL error 60: SSL certificate problem: unable to get local issuer certificate

I searched a lot on Google and lots of people are suggesting that I download this file: cacert.pem, put it somewhere and reference it in my php.ini. This is the part in my php.ini:

curl.cainfo = "C:\Windows\cacert.pem"

Yet, even after restarting my server several times and changing the path, I get the same error message.

I use WAMP from the Apache Modules and have the ssl_module enabled. And from the PGP extensions I have php_curl enabled.

Still the same error message. Why is that happening?

Now I am following this fix: How to fix PHP CURL Error 60 SSL

Which suggests that I add these lines to my cURL options:

curl_setopt($process, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
curl_setopt($process, CURLOPT_SSL_VERIFYPEER, true);

Where do I add options to my cURL? Apparently not through the command line, since my CLI doesn't find the command "curl_setopt"

EDIT

This is the code I am running:

public function chargeStripe()
{
    $stripe = new Stripe;
    $stripe = Stripe::make(env('STRIPE_PUBLIC_KEY'));

    $charge = $stripe->charges()->create([
        'amount'   => 2900,
        'customer' => Input::get('stripeEmail'),
        'currency' => 'EUR',
    ]);

    dd($charge);

    // echo $charge[Input::get('stripeToken')];


    return Redirect::route('step1');
}

All Answers
  • Translate

    working solution:

    • Assuming On windows

    XAMPP server

    similar for other environment - download and extract for cacert.pem here (a clean file format/data)

    https://curl.haxx.se/docs/caextract.html

    • put it here

    C:\xampp\php\extras\ssl\cacert.pem

    • in your php.ini put this line in this section ("c:\xampp\php\php.ini"):
    ;;;;;;;;;;;;;;;;;;;;
    ; php.ini Options  ;
    ;;;;;;;;;;;;;;;;;;;;
    
    curl.cainfo = "C:\xampp\php\extras\ssl\cacert.pem"
    
    • restart your webserver/apache

    Problem solved!

    (source: https://laracasts.com/discuss/channels/general-discussion/curl-error-60-ssl-certificate-problem-unable-to-get-local-issuer-certificate)


  • Translate

    If you are using PHP 5.6 with Guzzle, Guzzle has switched to using the PHP libraries autodetect for certificates rather than it's process (ref). PHP outlines the changes here.

    Finding out Where PHP/Guzzle is Looking for Certificates

    You can to dump where PHP is looking using:

     var_dump(openssl_get_cert_locations());
    

    Getting a Certificate Bundle

    For OS X testing, you can use homebrew to install openssl brew install openssl and then use openssl.cafile=/usr/local/etc/openssl/cert.pem in your php.ini or Zend Server settings (under OpenSSL).

    A certificate bundle is also available from curl/Mozilla on the curl website: https://curl.haxx.se/docs/caextract.html

    Telling PHP Where the Certificates Are

    Once you have a bundle, either place it where PHP is already looking (which you found out above) or update openssl.cafile in php.ini. (Generally, /etc/php.ini or /etc/php/7.0/cli/php.ini or /etc/php/php.ini on Unix.)


  • Translate

    Attention Wamp/Wordpress/windows users. I had this issue for hours and not even the correct answer was doing it for me, because i was editing the wrong php.ini file because the question was answered to XAMPP and not for WAMP users, even though the question was for WAMP.

    here's what i did

    Download the certificate bundle.

    Put it inside of C:\wamp64\bin\php\your php version\extras\ssl

    Make sure the file mod_ssl.so is inside of C:\wamp64\bin\apache\apache(version)\modules

    Enable mod_ssl in httpd.conf inside of Apache directory C:\wamp64\bin\apache\apache2.4.27\conf

    Enable php_openssl.dll in php.ini. Be aware my problem was that I had two php.ini files and I need to do this in both of them. First one can be located inside of your WAMP taskbar icon here.

    enter image description here

    and the other one is located in C:\wamp64\bin\php\php(Version)

    find the location for both of the php.ini files and find the line curl.cainfo = and give it a path like this

    curl.cainfo = "C:\wamp64\bin\php\php(Version)\extras\ssl\cacert.pem"

    Now save the files and restart your server and you should be good to go


  • Translate

    Guzzle, which is used by cartalyst/stripe, will do the following to find a proper certificate archive to check a server certificate against:

    1. Check if openssl.cafile is set in your php.ini file.
    2. Check if curl.cainfo is set in your php.ini file.
    3. Check if /etc/pki/tls/certs/ca-bundle.crt exists (Red Hat, CentOS, Fedora; provided by the ca-certificates package)
    4. Check if /etc/ssl/certs/ca-certificates.crt exists (Ubuntu, Debian; provided by the ca-certificates package)
    5. Check if /usr/local/share/certs/ca-root-nss.crt exists (FreeBSD; provided by the ca_root_nss package)
    6. Check if /usr/local/etc/openssl/cert.pem (OS X; provided by homebrew)
    7. Check if C:\windows\system32\curl-ca-bundle.crt exists (Windows)
    8. Check if C:\windows\curl-ca-bundle.crt exists (Windows)

    You will want to make sure that the values for the first two settings are properly defined by doing a simple test:

    echo "openssl.cafile: ", ini_get('openssl.cafile'), "\n";
    echo "curl.cainfo: ", ini_get('curl.cainfo'), "\n";
    

    Alternatively, try to write the file into the locations indicated by #7 or #8.


  • Translate

    If you're unable to change php.ini you could also point to the cacert.pem file from code like this:

    $http = new GuzzleHttp\Client(['verify' => '/path/to/cacert.pem']);
    $client = new Google_Client();
    $client->setHttpClient($http);
    

  • Translate

    What i did was use var_dump(openssl_get_cert_locations()); die; in any php script, which gave me the information about defaults that my local php was using:

    array (size=8)
      'default_cert_file' => string 'c:/openssl-1.0.1c/ssl/cert.pem' (length=30)
      'default_cert_file_env' => string 'SSL_CERT_FILE' (length=13)
      'default_cert_dir' => string 'c:/openssl-1.0.1c/ssl/certs' (length=27)
      'default_cert_dir_env' => string 'SSL_CERT_DIR' (length=12)
      'default_private_dir' => string 'c:/openssl-1.0.1c/ssl/private' (length=29)
      'default_default_cert_area' => string 'c:/openssl-1.0.1c/ssl' (length=21)
      'ini_cafile' => string 'E:\xampp\php\extras\ssl\cacert.pem' (length=34)
      'ini_capath' => string '' (length=0)
    

    As you can notice, i have set the ini_cafile or the ini option curl.cainfo. But in my case, curl would try to use the "default_cert_file" which did not exist.

    I copied the file from https://curl.haxx.se/ca/cacert.pem into the location for "default_cert_file" (c:/openssl-1.0.1c/ssl/cert.pem) and i was able to get it to work.

    This was the only solution for me.


  • Translate

    I found a solution that worked for me. I downgraded from the latest guzzle to version ~4.0 and it worked.

    In composer.json add "guzzlehttp/guzzle": "~4.0"

    Hope it helps someone


  • Translate

    I had this problem appear out-of-the-blue one day, when a Guzzle(5) script was attempting to connect to a host over SSL. Sure, I could disable the VERIFY option in Guzzle/Curl, but that's clearly not the correct way to go.

    I tried everything listed here and in similar threads, then eventually went to terminal with openssl to test against the domain with which I was trying to connect:

    openssl s_client -connect example.com:443 
    

    ... and received first few lines indicating:

    CONNECTED(00000003)
    depth=0 CN = example.com
    verify error:num=20:unable to get local issuer certificate
    verify return:1
    depth=0 CN = example.com
    verify error:num=21:unable to verify the first certificate
    verify return:1 
    

    ... while everything worked fine when trying other destinations (ie: google.com, etc)

    This prompted me to contact the domain I had been trying to connect to, and indeed, they had a problem on THEIR END that had crept up. It was resolved and my script went back to working.

    So... if you're pulling your hair out, give openssl a shot and see if there's anything up with the response from the location you are attempting to connect. Maybe the issue isn't so 'local' after all sometimes.


  • Translate

    Have you tried..

    curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
    

    If you are consuming a trusted source arguably you dont need to verify the SSL cert.


  • Translate

    Be sure that you open the php.ini file directly by your Window Explorer. (in my case: C:\DevPrograms\wamp64\bin\php\php5.6.25).

    Don't use the shortcut to php.ini in the Wamp/Xamp icon's menu in the System Tray. This shortcut doesn't work in this case.

    Then edit that php.ini :

    curl.cainfo ="C:/DevPrograms/wamp64/bin/php/cacert.pem" 
    

    and

    openssl.cafile="C:/DevPrograms/wamp64/bin/php/cacert.pem"
    

    After saving php.ini you don't need to "Restart All Services" in Wamp icon or close/re-open CMD.


  • Translate

    I just experienced this same problem with the Laravel 4 php framework which uses the guzzlehttp/guzzle composer package. For some reason, the SSL certificate for mailgun stopped validating suddenly and I got that same "error 60" message.

    If, like me, you are on a shared hosting without access to php.ini, the other solutions are not possible. In any case, Guzzle has this client initializing code that would most likely nullify the php.ini effects:

    // vendor/guzzlehttp/guzzle/src/Client.php
        $settings = [
            'allow_redirects' => true,
            'exceptions'      => true,
            'decode_content'  => true,
            'verify'          => __DIR__ . '/cacert.pem'
        ];
    

    Here Guzzle forces usage of its own internal cacert.pem file, which is probably now out of date, instead of using the one provided by cURL's environment. Changing this line (on Linux at least) configures Guzzle to use cURL's default SSL verification logic and fixed my problem:

            'verify'          => true
    

    You can also set this to false if you don't care about the security of your SSL connection, but that's not a good solution.

    Since the files in vendor are not meant to be tampered with, a better solution would be to configure the Guzzle client on usage, but this was just too difficult to do in Laravel 4.

    Hope this saves someone else a couple hours of debugging...


  • Translate

    This might be an edge case, but in my case the problem was not the client conf (I already had curl.cainfo configured in php.ini), but rather the remote server not being configured properly:

    It did not send any intermediate certs in the chain. There was no error browsing the site using Chrome, but with PHP I got following error.

    cURL error 60

    After including the Intermediate Certs in the remote webserver configuration it worked.

    You can use this site to check the SSL configuration of your server:

    https://whatsmychaincert.com/


  • Translate

    I spent too much time to figure out this problem for me.

    I had PHP version 5.5 and I needed to upgrade to 5.6.

    In versions < 5.6 Guzzle will use it's own cacert.pem file, but in higher versions of PHP it will use system's cacert.pem file.

    I also downloaded file from here https://curl.haxx.se/docs/caextract.html and set it in php.ini.

    Answer found in Guzzles StreamHandler.php file https://github.com/guzzle/guzzle/blob/0773d442aa96baf19d7195f14ba6e9c2da11f8ed/src/Handler/StreamHandler.php#L437

            // PHP 5.6 or greater will find the system cert by default. When
            // < 5.6, use the Guzzle bundled cacert.
    

  • Translate

    All of the answers are correct ; but the most important thing is You have to find the right php.ini file. check this command in cmd " php --ini " is not the right answer for finding the right php.ini file.

    if you edit

    curl.cainfo ="PATH/cacert.pem"
    

    and check

    var_dump(openssl_get_cert_locations()); 
    

    then curl.cainfo should have a value. if not then that's not right php.ini file;

    *I recommend you to search *.ini in wamp/bin or xxamp/bin or any server you use and change them one by one and check it. *


  • Translate

    when I run 'var_dump(php_ini_loaded_file());' I get this output on my page 'C:\Development\bin\apache\apache2.4.33\bin\php.ini' (length=50)'

    and to get php to load my cert file I had to edit the php.ini in this path 'C:\Development\bin\apache\apache2.4.33\bin\php.ini' and add openssl.cafile="C:/Development/bin/php/php7.2.4/extras/ssl/cacert.pem" where I had downloaded and place my cert file from https://curl.haxx.se/docs/caextract.html

    am on windows 10, using drupal 8, wamp and php7.2.4


  • Translate

    As you are using Windows, I think your path separator is '\' (and '/' on Linux). Try using the constant DIRECTORY_SEPARATOR. Your code will be more portable.

    Try:

    curl_setopt($process, CURLOPT_CAINFO, dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cacert.pem');
    

    EDIT: and write the full path. I had some issues with relative paths (perhaps curl is executed from another base directory?)


  • Translate

    if you use WAMP you should also add the certificate line in php.ini for Apache (besides the default php.ini file):

    [curl]
    curl.cainfo = C:\your_location\cacert.pem
    

    works for php5.3+