What exactly goes into CURLOPT_CAINFO/CAPATH? - php

Trying to make CURL in PHP work with a self signed certificate. I've made a copy of the cert file available to the client code, and I specify the path to the cert file both in CURLOPT_CAINFO and CURLOPT_CAPATH. Still, I'm getting error 60: SSL certificate problem: unable to get local issuer certificate.
Here are the repro steps. All on Linux (Debian Stretch in my case). Replace example.com with a relevant hostname.
First, I'd generate a private key:
openssl genrsa -out key.pem 2048
Compose a config file:
[req]
prompt=no
distinguished_name=dn
req_extensions=ext
x509_extensions=ext
[dn]
emailAddress=seva#example.com
CN=example.com
O=Seva Alekseyev
L=Chicago
ST=IL
C=US
[ext]
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=#alt
[alt]
DNS=example.com
Save as req.txt, generate a self signed cert:
openssl req -x509 -new -config req.txt -days 3650 -key key.pem -out example.cer
Install example.cer and key.pem in Apache under hostname example.com. Browse to make sure the basic setup works (modulo the scary security message).
Now, the client. Placed a copy of example.cer under $path. The PHP code goes:
$cu = curl_init("https://example.com/");
curl_setopt_array($cu, array(
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CAINFO => "$path/example.cer",
CURLOPT_CAPATH => "$path/example.cer"
));
$r = curl_exec($cu);
$c = curl_errno($cu);
$s = curl_error($cu);
curl_close($cu);
echo "$c $s";
Then the error message.
What am I missing here? Some guides suggest the value of CURLOPT_CAINFO/CAPATH should a folder instead, with serial-based symlinks pointing to cert files. Tried that too, same error. The document at https://curl.haxx.se/docs/sslcerts.html says:
Get a CA certificate that can verify the remote server and use the proper option to point out this CA cert for verification when connecting.
But there's no CA there, no cert chain. The signing cert is itself. Should I somehow transform the cert so that CURL sees it as a CA one? Should I generate a fake CA cert first, and sign the SSL cert with that one?
Command line curl, as in curl --cacert example.cer https://example.com/, pops the same message.
Related question here, but I'd rather not mess with systemwide settings.

The certificates pointed to by CURLOPT_CAINFO/CAPATH are expected to be CA certificates - at least when OpenSSL is used. This means that your self-signed certificate need also to be a CA certificate, i.e. it should not only be for serverAuth but also have basic constraints CA:true.

The keyUsage line under [ext] must include keyCertSign, like this:
keyUsage=digitalSignature,keyEncipherment,keyCertSign
Otherwise, it's not a CA cert as far as OpenSSL is concerned.
OBTW, the basicConstraints=CA:true line under [ext], suggested by Steffen, is not necessary, I've checked. At least with CURL 7.52.1 and OpenSSL 1.0.2r it's not.
In the client code, CURLOPT_CAPATH is not necessary, either. CURL supports two alternative ways of specifying the root CA cert bundle. CURLOPT_CAINFO makes CURL read and parse a single file, potentially with multiple certificates in it. CURLOPT_CAPATH makes CURL scan a directory with certificate files identified by their serial numbers - or symlinks to those, as generated by c_rehash. Since in my scenario the effective root CA cert bundle has exactly one cert, the one file approach is sufficient.
Doesn't work under Windows, at least with command line CURL 7.55.1. The Windows version of CURL uses the built-in Schannel library for its SSL implementation, and ignores the --cacert option, instead relying on Windows' built-in trusted CA store. See here.
It might be possible to rebuild CURL for Windows against a different SSL implementation, but the trouble is hardly worth it. Windows comes with its own fleet of HTTP(S) clients.

Related

How to fix Curl error 60 without downloading cert

I'm using Rackspace API in PHP, and it just stopped working (everything worked fine 3 days ago). It uses guzzle, who uses curl. And curl just stopped working.
[Thu Jun 21 14:55:36 2018] [error] [client xxx.xx.xxx.xx] PHP Fatal error: Uncaught exception 'Guzzle\\Http\\Exception\\CurlException' with message '[curl] 60: [url] https://identity.api.rackspacecloud.com/v2.0/tokens' in
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php:359\nStack trace:\n#0
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php(292): Guzzle\\Http\\Curl\\CurlMulti->isCurlException(Object(Guzzle\\Http\\Message\\EntityEnclosingRequest), Object(Guzzle\\Http\\Curl\\CurlHandle), Array)\n#1
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php(257): Guzzle\\Http\\Curl\\CurlMulti->processResponse(Object(Guzzle\\Http\\Message\\EntityEnclosingRequest),
Object(Guzzle\\Http\\Curl\\CurlHandle), Array)\n#2
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php(240): Guzzle\\Http\\Curl\\CurlMulti->processMessages()\n#3
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php(224): Guzzle\\Http\\Curl\\CurlMulti->executeHandles()\n#4
/var/www/passline.com/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php(111)
The important part from the error is the following:
[curl] 60: [url] https://identity.api.rackspacecloud.com/v2.0/tokens
I get an error 60 from Curl, who means is an SSL cert error. Most answer say's the solution to this problem is: deactivate ssl or download a new cert.
curl: (60) SSL certificate : unable to get local issuer certificate
https://es.stackoverflow.com/questions/174276/curl-60-ssl-certificate-problem-unable-to-get-local-issuer-certificate-url-h
I won't deactivate SSL, I can't use http instead of https and I want to avoid having to get into the machine and downloading a new cert.
If someday I have an old cert again, my site is going to stop working. What is the correct way to fix this?
This server has CenOs 6, We're using PHP 5.3.3 and curl 7.19.7
---- Edit ----
So, my problem is because of the change in the certificates of curl. From https://curl.haxx.se/docs/caextract.html
This bundle was generated at Wed Jun 20 03:12:06 2018 GMT .
There is a tool on linux called update-ca-certificates who solves this problem, also, the curl site say's you can run
curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem
But, I don't know, someday I'll see the system stop working properly, I gonna get into the machine an run this command, and, that's all?, What doest the other people do?, set a cron with this command? or what?
Older versions of Guzzle made use of their own CA file that was bundled with the Guzzle library. It would use that file instead of system's (/etc/pki/tls/certs).
If you can get things working with cURL from the command line but get this error in Guzzle that is likely the culprit.
Things were changed to use the system CA bundle by default in late 2014.
https://github.com/guzzle/guzzle/issues/623
https://github.com/guzzle/guzzle/pull/800
The behaviour of newer (> 3.0 ?) versions is described here (see verify configuration flag):
Check if openssl.cafile is set in your php.ini file.
Check if curl.cainfo is set in your php.ini file.
Check if /etc/pki/tls/certs/ca-bundle.crt exists (Red Hat, CentOS, Fedora; provided by the ca-certificates package)
Check if /etc/ssl/certs/ca-certificates.crt exists (Ubuntu, Debian; provided by the ca-certificates package)
Check if /usr/local/share/certs/ca-root-nss.crt exists (FreeBSD; provided by the ca_root_nss package)
Check if /usr/local/etc/openssl/cert.pem (OS X; provided by homebrew)
Check if C:\windows\system32\curl-ca-bundle.crt exists (Windows)
Check if C:\windows\curl-ca-bundle.crt exists (Windows)
If someday I have an old cert again, my site is going to stop working. Curl should download a new cert by himself? isn't it?.
The concept of TLS is that the server sends its certificate to the client, shows a prove that it actually owns the private key belonging to the certificate and then the client checks if the certificate is considered trusted. Trusted means among others that the certificate was issued by a locally trusted CA (certificate authority).
Typically the client has a set of CA it trusts, i.e. CA like Let's Encrypt. If the certificate was issued by such an already trusted CA no changes to the client are needed whenever the certificate is changed as long as the issuer CA is still trusted and the server is configured properly to provide all intermediate CA certificates which are needed to build the trust path.
If instead you have a self-signed certificate or a certificate signed by some private CA the client has no kind of trust anchor it can use to verify the certificate. In this you need to provide the necessary trust anchor to the client. In case of a private CA it is sufficient to setup the client once with this private CA and it will accept also later certificates issued by this CA. But in case of a self-signed certificate this means that you need to update the expected certificate at the client whenever you update the certificate at the server. There is no automatic way to do it - because how should the client verify that it gets the correct new certificate without having an established trust to the party providing the new certificate?
This issue was caused by the change in the certificates of curl. From https://curl.haxx.se/docs/caextract.html
This bundle was generated at Wed Jun 20 03:12:06 2018 GMT .
There is a tool on linux called update-ca-certificates who solves this problem, also, the curl site say's you can run
curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem
Just consider any of these commands may be needed to run again in the future if the certificates are renovated again.

curl SSL certificate error

I'm trying to install Vaprobash via CURL but I get this message everytime I try to download something through CURL. I'm using a Mac.
curl -L http://bit.ly/vaprobash > Vagrantfile
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Append option in curl call:
-k/--insecure
The curl will be not trying to verify SSL cert.
The link is redirecting to a secure HTTP (SSL) server, and curl doesn't have access to the proper chain of trusted certificates to confirm that the site says it is who it is.
If you're sure the site is legitimate, and this is a once-off effort, you can use the --insecure option (it will ignore any certificate errors). Opening the link in a browser directs to GitHub with no issues, and curl fetches it fine on my Ubuntu 12.04 box.
It's likely a missing certificate in your SSL installation. Run with the -v option to get a detailed output if you need to confirm this.

Google PHP API Client: CA cert error

I'm attempting to interface with the Google PHP API client and I am having issues with the certificate provided by Google:
Google error:
SSL certificate problem, verify that the CA cert is OK.
Retrying with the CA cert bundle from google-api-php-client.
PHP cURL error:
SSL certificate problem: unable to get local issuer certificate
I had no problems whatsoever on a Linux box. These errors are occuring on a Windows box.
I've tried a couple of different solutions:
https://code.google.com/
http://richardwarrender.com/
but to no avail.
PS:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
won't be acceptable ...
Courtesy of rmckay at webaware dot com dot au:
Please everyone, stop setting CURLOPT_SSL_VERIFYPEER to false or 0. If your PHP installation doesn't have an up-to-date CA root certificate bundle, download the one at the curl website and save it on your server:
http://curl.haxx.se/docs/caextract.html
Then set a path to it in your php.ini file, e.g. on Windows:
curl.cainfo=c:\php\cacert.pem
Turning off CURLOPT_SSL_VERIFYPEER allows man in the middle (MITM) attacks, which you don't want!
\Google_Client::$io->setOptions(array(CURLOPT_SSL_VERIFYPEER => FALSE));
#sKophek is correct and I appreciate the help as I was struggling with this. For those that prefer a touch more detail, here it is: (this is true, at least, for the 0.6.x version of the google-api-php-client)
1) \google-api-php-client\src\io\Google_CurlIO.php
2)
private $curlParams = array (
...
CURLOPT_SSL_VERIFYPEER => false,
... );

SSL peer certificate or SSH remote key was not OK

I'm testing an API that uses curl_exec php function and a CA certificate but something is going wrong and I'm a little lost.
I have configured SSL on my apache VirtualHost and looks ok ( opening https:://[myVHost]... works ).
However the API curl call give me back this message:
SSL peer certificate or SSH remote key was not OK
I'm not very experienced with SSL so I have few ideas about the cause of that.
UPDATE:
This is the code I'm using in my cURL request, I have commented 2 lines and changes their value (look at 'TODO' line ) and in this way it is working, however this is just a work arround ...
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_CONNECTTIMEOUT] = 50;
$opts[CURLOPT_TIMEOUT] = 100;
$headers = array(
'Accept: application/json',
"User-Agent: APIXXX-PHP-Client");
$opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_USERPWD] = $env->getApiKey() . ':';
if (certificatePresent()) {
// $opts[CURLOPT_SSL_VERIFYPEER] = true;
// $opts[CURLOPT_SSL_VERIFYHOST] = 2;
// TODO: SET IT BACK
$opts[CURLOPT_SSL_VERIFYPEER] = 0;
$opts[CURLOPT_SSL_VERIFYHOST] = 0;
$opts[CURLOPT_CAINFO] = $path
}
curl_setopt_array($curl, $opts);
$response = curl_exec($curl);
You are probably using self-signed SSL certifiacate, which will not pass when the CURLOPT_SSL_VERIFYPEER options is set.
There are two solutions:
Set up valid SSL certificate.
Disable SSL verification in Curl. (add --insecure option)
If you disable verification, you can't be sure if you are really communicating with your host.
So it depends on level of security you need.
Beside CURLOPT_SSL_VERIFYPEER there are two other settings which might be changed to false/0:
CURLOPT_SSL_VERIFYHOST
CURLOPT_SSL_VERIFYSTATUS
Beware that you should fix your SSL certificates & settings instead of disable security!
Although I am answering an old post, I think it will help the new viewers-
You can check the problem by adding
$opts[CURLOPT_VERBOSE] = 1
For self signed certificate your client may connect with the server using IP address, because the host name is not available in DNS cache. In that case the COMMON NAME(CN) of your server certificate needs to match with the Server IP (put IP address as common name when generating the server certificate). When you do it correctly, you can see this message:
common name: 192.168.0.1 (matched)
Here 192.168.0.1 is an example.
You're right to want to enable SSL_VERIFYPEER if you are worried about man-in-the-middle attacks.
Is your $path set to point to the certificate (or certificate bundle) provided by the API owner? Is that certificate readable by the web server user? If so, have you verified that the certificate(s) is the same as when you visit the https address manually in a browser and inspect the certificate?
If you can't get it to work, and the API you are connecting to has a SSL certificate that works in your normal browser without warnings, you should be able to set $path to your CA root bundle on your server.
You can build a valid SSL certificate and ensure that it is stored in the trusted folder.
Valid SSL certificate can be created by including the following command in the developer command prompt of VS2012. (This can be obtained by typing developer in the start)
The following command creates a self-signed certificate that can be used to test a web application that uses Secure Sockets Layer (SSL) on a web server whose URL is www.example.com. The OID defined by the -eku option identifies that certificate as an SSL server certificate. The certificate is stored in the my store and is available at the machine (rather than user) level. The certificate's private key is exportable, and the certificate is valid from May 10, 2010 through December 22, 2011.
Makecert -r -pe -n CN="www.example.com" -b 05/10/2010 -e 12/22/2011 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
For more on how to create the SSL certificate
Now make sure that this certificate is trusted, this can be done by typing CERTMGR in the cmd..
now the cert created is in the PERSONAL folder.. copy it and paste it to the TRUSTED PEOPLE FOLDER.
This should do the trick. Let me know if that doesn't work.
I had the same issue. I follow the instruction here: http://siteber.com/download-failed-ssl-peer-certificate-or-ssh-remote-key-was-not-ok/ and it fixed mine.
basically I went to /etc/resolv.conf
and Replace any
OpenDNS server:
208.67.222.222
208.67.220.220
With
Google’s public DNS servers:
nameserver 8.8.8.8
nameserver 8.8.4.4
This error can also occur if you update packages on a linux server that has a self-signed certificate.
Solution:
Stop your existing Apache/nginx server.
Run certbot (if you are using lets encrypt)
Restart your Apache/nginx server.
Note: If you're using Springboot, add System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3"); to your application.properties file
Voila!
I spent almost all day for this error, and problem was in using ipv6, and called api server does not support ipv6.
Solution:
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_VERSION_IPV4);

Not hacking CurlException: 60 (cURL SSL Certificate Verification)

The error that alot of people get with Facebook authentication is:
CurlException: 60: SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
And the only information I can find about it suggest to add the following lines of code to curl:
$opts[CURLOPT_SSL_VERIFYPEER] = false;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
I know this works, but what is going on here?
Isn't there any server settings/configuraton that can be changed instead of hacking up facebook.php.
What It Does & Meaning:
The following code tells the cURL to NOT verify that security certificates are correct. Hence, the error disappears.
$opts[CURLOPT_SSL_VERIFYPEER] = false;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
When you connect to a remote server with SSL, their certificate might be invalid, expired, or not signed by a recognized CA. The cURL normally checks it.
CURLOPT_SSL_VERIFYHOST:
1: to check the existence of a common name in the SSL peer certificate.
2: to check the existence of a common name and also verify that it matches the hostname provided.
CURLOPT_SSL_VERIFYPEER: FALSE to stop CURL from verifying the peer's certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option. CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2).
How to Enable & Verify Correctly:
To verify correctly, we need to to verify the certificate being presented to us is good for real. We do this by comparing it against a certificate we reasonable* trust.
If the remote resource is protected by a certificate issued by one of the main CA's like Verisign, GeoTrust et al, you can safely compare against Mozilla's CA certificate bundle which you can get from http://curl.haxx.se/docs/caextract.html
Save the file cacert.pem somewhere in your server and set the following options in your script.
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_setopt ($ch, CURLOPT_CAINFO, "pathto/cacert.pem");
If you are connecting to a resource protected by a self-signed certificate, all you need to do is obtain a copy of the certificate in PEM format and append it to the cacert.pem of the above paragraph.
In my case, I could not use curl_setopt, because I could not edit Facebook API classes ( conditions of project I was working in ).
I solved the problem by adding path to cacert.pem downloaded from http://curl.haxx.se/docs/caextract.html to my php.ini
[curl]
curl.cainfo = "c:\wamp\cacert.pem"
I just had the same problem, and disabling peer verification is not acceptable in my case.
I updated the fa_ca_chain_bundle.crt file (from facebook's gitbub) and it works now.
Regards,
Marek

Categories