Hello fellow programmers,
I'm making an API with a https connection. I want to secure the API call with a certificate check. Now i've downloaded the cacert.pem from curl and placed this in my folder, but it doesn't work and gives me this error:
SSL certificate problem: unable to get local issuer certificate
Well i'm really sure that the cacert.pem file is located correctly but for you guys some code that i've been writing to get it working:
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST ,1);
curl_setopt($ch, CURLOPT_USERPWD, $this->api_key . ":" . $this->api_secret);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . "/cacert.pem");
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_POSTFIELDS ,$post_vars);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION ,0);
curl_setopt($ch,CURLOPT_HTTPHEADER,array('Accept: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,1); // RETURN THE CONTENTS OF THE CALL
$data = curl_exec($ch);
I hope you some of you guys have an answer for my question because I really don't know much about SSL connections.
Thanks in advance!
After having made sure that your certificate bundle is readable by the script you're running, the next step is to investigate the server itself.
If this is your own server, it's likely that you forgot to install the intermediate certificates, i.e. the ones in between the issued certificate and one of the certificate authorities that comes with the bundle.
If you're working with a self-signed certificate, your cacert.pem should contain the public certificate of the server you're connecting to; this can be generated from the private certificate, outlined here, referred to as the .crt file.
Related
I try to send curl request with my correct APP_ID, APP_SECRET etc. to the
https://oauth.vk.com/access_token?client_id=APP_ID&client_secret=APP_SECRET&code=7a6fa4dff77a228eeda56603b8f53806c883f011c40b72630bb50df056f6479e52a&redirect_uri=REDIRECT_URI
I need to get access_token from it, but get a FALSE and curl_error() print next message otherwise:
60: SSL certificate problem: self signed certificate in certificate chain
My code is:
// create curl resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, $url);
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string
$output = curl_exec($ch);
if ( ! $output) {
print curl_errno($ch) .': '. curl_error($ch);
}
// close curl resource to free up system resources
curl_close($ch);
return $output;
When I move manually to the link above, I get access_token well. Why it doesn't work with curl? Help, please.
Answers suggesting to disable CURLOPT_SSL_VERIFYPEER should not be accepted. The question is "Why doesn't it work with cURL", and as correctly pointed out by Martijn Hols, it is dangerous.
The error is probably caused by not having an up-to-date bundle of CA root certificates. This is typically a text file with a bunch of cryptographic signatures that curl uses to verify a host’s SSL certificate.
You need to make sure that your installation of PHP has one of these files, and that it’s up to date (otherwise download one here: http://curl.haxx.se/docs/caextract.html).
Then set in php.ini:
curl.cainfo = <absolute_path_to> cacert.pem
If you are setting it at runtime, use (where $ch = curl_init();):
curl_setopt ($ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");
This workaround is dangerous and not recommended:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
It's not a good idea to disable SSL peer verification. Doing so might expose your requests to MITM attackers.
In fact, you just need an up-to-date CA root certificate bundle. Installing an updated one is as easy as:
Downloading up-to-date cacert.pem file from cURL website and
Setting a path to it in your php.ini file, e.g. on Windows:
curl.cainfo=c:\php\cacert.pem
That's it!
Stay safe and secure.
If the SSL certificates are not properly installed in your system, you may get this error:
cURL error 60: SSL certificate problem: unable to get local issuer
certificate.
You can solve this issue as follows:
Download a file with the updated list of certificates from https://curl.haxx.se/ca/cacert.pem
Move the downloaded cacert.pem file to some safe location in your system
Update your php.ini file and configure the path to that file:
Important: This issue drove me crazy for a couple days and I couldn't figure out what was going on with my curl & openssl installations. I finally figured out that it was my intermediate certificate (in my case, GoDaddy) which was out of date. I went back to my godaddy SSL admin panel, downloaded the new intermediate certificate, and the issue disappeared.
I'm sure this is the issue for some of you.
Apparently, GoDaddy had changed their intermediate certificate at some point, due to scurity issues, as they now display this warning:
"Please be sure to use the new SHA-2 intermediate certificates included in your downloaded bundle."
Hope this helps some of you, because I was going nuts and this cleaned up the issue on ALL my servers.
To add a more specific answer, I ran into this when using Guzzle v7, the PHP HTTP request package. Guzzle allows you to bypass this like so:
use GuzzleHttp\Client;
$this->client = new Client([
'verify' => false,
]);
Original source comment: https://github.com/guzzle/guzzle/issues/1490#issuecomment-375667460
Error: SSL certificate problem: self signed certificate in certificate
chain
Solution:
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
I know there are several posts about this topic but I've tried a lot and nothing has worked.
curl_setopt($ch, CURLOPT_URL, "https://127.0.0.1:2288");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pRequest);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_CAINFO, "/etc/ssl/certs/cacert.pem");
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '2');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
When I execute it on Ubuntu 20.04 machine, i get this error:
Fatal error: Uncaught RuntimeException: Unable to connect to https://127.0.0.1:2288/ Error: SSL certificate problem: self signed certificate
The certificate is the current version from curl.se/docs/caextract.html
php.ini:
extension=curl
extension=php_curl.dll
curl.cainfo="/etc/ssl/certs/cacert.pem"
What else can I do without prohibiting curl ssl connections?
If you are using self-signed certificates you either have to add it to your trusted certificates or you have to turn validation off.
For the second case just switch the CURLOPT_SSL_VERIFYPEER to false.
You are connecting securely to localhost on Ubuntu and, as your error indicates, your server is responding with a self-signed certificate. Your code specifies that curl should attempt to verify the host it's connecting to (localhost, in your case):
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '2');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
According to the docs, that value of 2 in your code for CURLOPT_SSL_VERIFYHOST is documented thusly:
2 to verify that a Common Name field or a Subject Alternate Name field in the SSL peer certificate matches the provided hostname. 0 to not check the names. 1 should not be used. In production environments the value of this option should be kept at 2 (default value).
If you want to strictly follow the documentation's recommendation, you'd have to map some domain onto localhost and install a cert for that domain on your server. AFAIK, you cannot get a cert for 127.0.0.1 because that special IP address always refers to localhost...sort of a long story but it makes no sense for anyone to sign such a cert.
Your other option is to tell curl not to verify the ssl connection CURLOPT_SSL_VERIFYHOST to zero:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// you may also need to set this?
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
That tells curl not to bother validating the certificate. This should be fairly safe if your are in control of your own server and if your server hasn't been compromised somehow.
EDIT
I would add that downloading the cert extract from curl.se just grabs a bundle of widely trusted signatures/certificates that happens to be the bundle used by Firefox. This cert bundle is used to check any domain that you might visit by comparing the signature on that domain's certificate to some big/important certificates that have been signed by supposedly trustworthy organizations. It will never be helpful in validating a self-signed certificate. For more information about this elaborate concept, try reading about Web of Trust.
I should also mention that you might be able to use a few commands to grab the certificate that your local machine coughs up and configure your curl code to use that certificate, HOWEVER, the CURLOPT_SSL_VERIFYHOST setting of 2 instructs curl to check the Common Name field of this certificate against the IP address or domain to which you are connecting. On my ubuntu workstation, the certificate's Common Name is 'Ubuntu' -- and that won't match either localhost or 127.0.0.1. For this approach to work, you'll either have to generate a new cert for your machine with a Common Name that matches the address to which you connect (localhost or 127.0.0.1) OR you'll need to set up something to name your machine 'ubuntu' on your local network.
I'm attempting to log into a bank's website via PHP/curl (PHP 5.5.3) to download transactions. I'm new to curl, so I'm trying to figure out how to just log in to the site first.
CURRENT CODE:
$postinfo = 'username=' . $username . '&password=' . $password;
$curl = curl_init();
curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_NOBODY, false);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($curl, CURLOPT_CAINFO, getcwd() . '\certificateCA.cer');
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postinfo);
$content = curl_exec($curl);
if(false == $content){
echo curl_error($curl);
}
curl_close($curl);
print $content;
ISSUE
I'm getting the following error:
"SSL certificate problem: unable to get local issuer certificate"
WHAT I'VE TRIED
So far, I've tried getting the certificate of the remote site per the answer on this SO question. This seems to have helped, because before I was getting a different error ("error setting certificate verify locations"). The certificateCA.cer referenced in my code is the certificate I downloaded from the remote site (the bank), by choosing to export the "Base-64 encoded X.509 (.CER)" option.
As it seems my current issue is related to the local cert not the remote cert, I've tried getting the latest from here: https://curl.haxx.se/ca/cacert.pem per this SO answer.
I added the following lines to my php.ini file and restarted Apache per this SO answer:
curl.cainfo = "C:\xampp\php\extras\ssl\cacert.pem"
openssl.cafile = "C:\xampp\php\extras\ssl\cacert.pem"
Per this SO response, I also tried changing the file name and the reference to this:
curl.cainfo = "C:\xampp\php\extras\ssl\ssl.txt"
openssl.cafile = "C:\xampp\php\extras\ssl\ssl.txt"
HOW YOU CAN HELP ME
Please help me understand CURLOPT_CAINFO. Is this expecting the certificate of the remote site, or the local machine? It seems that it's looking for the remote site certificate based on the error going away after adding that certificate, but it's not clear from the documentation exactly what it's expecting.
A solution, or even a method by which I can triage this issue and find a resolution myself. It seems like what's worked for others on the linked SO questions isn't working for me, so I'm doing something wrong.
I try to send curl request with my correct APP_ID, APP_SECRET etc. to the
https://oauth.vk.com/access_token?client_id=APP_ID&client_secret=APP_SECRET&code=7a6fa4dff77a228eeda56603b8f53806c883f011c40b72630bb50df056f6479e52a&redirect_uri=REDIRECT_URI
I need to get access_token from it, but get a FALSE and curl_error() print next message otherwise:
60: SSL certificate problem: self signed certificate in certificate chain
My code is:
// create curl resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, $url);
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string
$output = curl_exec($ch);
if ( ! $output) {
print curl_errno($ch) .': '. curl_error($ch);
}
// close curl resource to free up system resources
curl_close($ch);
return $output;
When I move manually to the link above, I get access_token well. Why it doesn't work with curl? Help, please.
Answers suggesting to disable CURLOPT_SSL_VERIFYPEER should not be accepted. The question is "Why doesn't it work with cURL", and as correctly pointed out by Martijn Hols, it is dangerous.
The error is probably caused by not having an up-to-date bundle of CA root certificates. This is typically a text file with a bunch of cryptographic signatures that curl uses to verify a host’s SSL certificate.
You need to make sure that your installation of PHP has one of these files, and that it’s up to date (otherwise download one here: http://curl.haxx.se/docs/caextract.html).
Then set in php.ini:
curl.cainfo = <absolute_path_to> cacert.pem
If you are setting it at runtime, use (where $ch = curl_init();):
curl_setopt ($ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");
This workaround is dangerous and not recommended:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
It's not a good idea to disable SSL peer verification. Doing so might expose your requests to MITM attackers.
In fact, you just need an up-to-date CA root certificate bundle. Installing an updated one is as easy as:
Downloading up-to-date cacert.pem file from cURL website and
Setting a path to it in your php.ini file, e.g. on Windows:
curl.cainfo=c:\php\cacert.pem
That's it!
Stay safe and secure.
If the SSL certificates are not properly installed in your system, you may get this error:
cURL error 60: SSL certificate problem: unable to get local issuer
certificate.
You can solve this issue as follows:
Download a file with the updated list of certificates from https://curl.haxx.se/ca/cacert.pem
Move the downloaded cacert.pem file to some safe location in your system
Update your php.ini file and configure the path to that file:
Important: This issue drove me crazy for a couple days and I couldn't figure out what was going on with my curl & openssl installations. I finally figured out that it was my intermediate certificate (in my case, GoDaddy) which was out of date. I went back to my godaddy SSL admin panel, downloaded the new intermediate certificate, and the issue disappeared.
I'm sure this is the issue for some of you.
Apparently, GoDaddy had changed their intermediate certificate at some point, due to scurity issues, as they now display this warning:
"Please be sure to use the new SHA-2 intermediate certificates included in your downloaded bundle."
Hope this helps some of you, because I was going nuts and this cleaned up the issue on ALL my servers.
To add a more specific answer, I ran into this when using Guzzle v7, the PHP HTTP request package. Guzzle allows you to bypass this like so:
use GuzzleHttp\Client;
$this->client = new Client([
'verify' => false,
]);
Original source comment: https://github.com/guzzle/guzzle/issues/1490#issuecomment-375667460
Error: SSL certificate problem: self signed certificate in certificate
chain
Solution:
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
I'm trying to write a PHP script using cURL that can authorize a user through a page that uses an SSL certificate, in addition to username and password, and I can't seem to get past the SSL cert stage.
In this case, curl_setopt($handle, CURLOPT_VERIFYPEER, 0) unfortunately isn't an option. The certificate is a required part of authentication, otherwise I get the error mentioned in this other similar SO post.
I've tried a few command-line runs with cURL:
> curl --url https://website
This returns the (60) SLL certificate problem error. If I adjust the command to include the --cacert option:
> curl --url https://website --cacert /path/to/servercert.cer
It works just fine; the auth website is returned.
However, I've tried the following PHP code:
$handle = curl_init();
$options = array(
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_SSL_VERIFYHOST => '0',
CURLOPT_SSL_VERIFYPEER => '1',
CURLOPT_CAINFO => '/path/to/servercert.cer',
CURLOPT_USERAGENT => 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)',
CURLOPT_VERBOSE => true,
CURLOPT_URL => 'https://website'
);
curl_setopt_array($handle, $options);
curl_exec($handle);
if (curl_errno($handle)) {
echo 'Error: ' . curl_error($handle);
}
curl_close($handle);
I would have thought the code was essentially analogous to the shell commands, but instead I'm greeted with the following error message:
Error: error setting certificate verify locations: CAfile: /path/to/servercert.cer CApath: none
I've read all the literature I can find (particularly on php.net and curl.haxx) and can't seem to find anything that fixes this problem. Any suggestions?
I have tried chmod 777 servercert.cer with no success. However, in executing the PHP script with the above code from the command line instead of the browser via php test.php, it works perfectly. Any explanation for why it doesn't work in the browser?
Because things work via the command line but not via php using curl then I would pursue curl being the problem.
According to this URL, http://curl.haxx.se/docs/sslcerts.html, which was reference in an SO post you cited above ( reading SSL page with CURL (php) )...
"Until 7.18.0, curl bundled a severely outdated ca bundle file that was
installed by default. These days, the curl archives include no ca certs at
all. You need to get them elsewhere. See below for example.
If the remote server uses a self-signed certificate, if you don't install a CA
cert bundle, if the server uses a certificate signed by a CA that isn't
included in the bundle you use or if the remote host is an impostor
impersonating your favorite site, and you want to transfer files from this
server, do one of the following:"
It then goes on to list a number of steps that you can try.
Since your 7.16.3 version of curl is prior to 7.18.0, if you haven't already, I would recommend updating your curl and openssl components and then working through the list referenced above.
To elaborate and sum this up:
if you have a PHP file using PHP curl and place the ca certificate for your systems in the same directory, the below code will give you a jumpstart
$url = "https://myserver.mydomain.local/get_somedata.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
//These next lines are for the magic "good cert confirmation"
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_VERBOSE, true);
//for local domains:
//you need to get the pem cert file for the root ca or intermediate CA that you signed all the domain certificates with so that PHP curl can use it...sorry batteries not included
//place the pem or crt ca certificate file in the same directory as the php file for this code to work
curl_setopt($ch, CURLOPT_CAINFO, __DIR__.'/cafile.pem');
curl_setopt($ch, CURLOPT_CAPATH, __DIR__.'/cafile.pem');
//DEBUG: remove slashes on the next line to prove "SSL verify" is the cause
//curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//Error handling and return result
$data = curl_exec($ch);
if ($data === false) {
$result = curl_error($ch);
} else {
$result = $data;
}
// Close handle
curl_close($ch);
return $result;
After 6 years of the question, I meet the same problem on a shared-host, and apparently there is no satisfactory answer. I found for myself a solution, hope it useful for everybody.
You can try this config:
curl_setopt($config,CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($config,CURLOPT_SSL_VERIFYPEER,1);
curl_setopt($config,CURLOPT_CAINFO,'ca-bundle.crt');
curl_setopt($config,CURLOPT_CAPATH,'ca-bundle.crt');
I met same error with #Magsol: Error: error setting certificate verify locations: CAfile: /path/to/servercert.cer CApath: none; so I added the 4th line to set CAPath.
It's work with me. But note, the CA file must be place in accessable dir (with chmod 755 or 777) and it will better if CA file is in the same dir with PHP file.
Now that this question is vey old, but maybe could be useful for some users looking for an answer currently.
I have a similar problem about an API with SSL, having problems with CURL (not with the browsers) my problem was that I just put the certificate but not the ceritifcates chain/bundle. Then I put that and things started working. So that's important in order to avoid problems.
Hope this can be useful for someone.
You can try this if it works for you:
curl_setopt($ch, CURLOPT_URL, "https://test.example.com/v1/authenticate.json?api_key=123456");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_CAINFO,'cert.embedapp.20191004.pem');
curl_setopt($ch,CURLOPT_CAPATH,'./cert.embedapp.20191004.pem');
Comment these lines and add this:
//curl_setopt($ch,CURLOPT_CAINFO,'cert.embedapp.20191004.pem');
//curl_setopt($ch,CURLOPT_CAPATH,'./cert.embedapp.20191004.pem');
curl_setopt($ch, CURLOPT_SSLCERT,'cert.embedapp.20191004.pem');