By Joe


2014-10-01 19:04:50 8 Comments

I’ve been trying to access this particular REST service from a PHP page I’ve created on our server. I narrowed the problem down to these two lines. So my PHP page looks like this:

<?php
$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json");

echo $response; ?>

The page dies on line 2 with the following errors:

  • Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in ...php on line 2
    • Warning: file_get_contents(): Failed to enable crypto in ...php on line 2
    • Warning: file_get_contents(https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json): failed to open stream: operation failed in ...php on line 2

We’re using a Gentoo server. We recently upgraded to PHP version 5.6. It was after the upgrade when this problem appeared.

I found when I replace the REST service with an address like https://www.google.com; my page works just fine.

In an earlier attempt I set “verify_peer”=>false, and passed that in as an argument to file_get_contents, as described here: file_get_contents ignoring verify_peer=>false? But like the writer noted; it made no difference.

I’ve asked one of our server administrators if these lines in our php.ini file exist:

  • extension=php_openssl.dll
  • allow_url_fopen = On

He told me that since we’re on Gentoo, openssl is compiled when we build; and it’s not set in the php.ini file.

I also confirmed that allow_url_fopen is working. Due to the specialized nature of this problem; I’m not finding a lot of information for help. Have any of you come across something like this? Thanks.

16 comments

@Dipti 2018-12-04 10:40:34

Working for me, I am using PHP 5.6. openssl extension should be enabled and while calling google map api verify_peer make false Below code is working for me.

<?php
$arrContextOptions=array(
    "ssl"=>array(
         "verify_peer"=>false,
         "verify_peer_name"=>false,
    ),
);  
$url = "https://maps.googleapis.com/maps/api/geocode/json?latlng="
      . $latitude
      . ","
      . $longitude
      . "&sensor=false&key="
      . Yii::$app->params['GOOGLE_API_KEY'];

$data = file_get_contents($url, false, stream_context_create($arrContextOptions));

echo $data;
?>

@Aleks 2017-05-12 00:46:15

Regarding errors similar to

[11-May-2017 19:19:13 America/Chicago] PHP Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed

Have you checked the permissions of the cert and directories referenced by openssl?

You can do this

var_dump(openssl_get_cert_locations());

To get something similar to this

array(8) {
  ["default_cert_file"]=>
  string(21) "/usr/lib/ssl/cert.pem"
  ["default_cert_file_env"]=>
  string(13) "SSL_CERT_FILE"
  ["default_cert_dir"]=>
  string(18) "/usr/lib/ssl/certs"
  ["default_cert_dir_env"]=>
  string(12) "SSL_CERT_DIR"
  ["default_private_dir"]=>
  string(20) "/usr/lib/ssl/private"
  ["default_default_cert_area"]=>
  string(12) "/usr/lib/ssl"
  ["ini_cafile"]=>
  string(0) ""
  ["ini_capath"]=>
  string(0) ""
}

This issue frustrated me for a while, until I realized that my "certs" folder had 700 permissions, when it should have had 755 permissions. Remember, this is not the folder for keys but certificates. I recommend reading this this link on ssl permissions.

Once I did

chmod 755 certs

The problem was fixed, at least for me anyway.

@RA. 2017-08-18 12:02:14

Yay! CHMOD was my issue as well ;-) Thanks

@Edward DiGirolamo 2018-01-12 15:33:39

After falling victim to this problem on centOS after updating php to php5.6 I found a solution that worked for me.

Get the correct directory for your certs to be placed by default with this

php -r 'print_r(openssl_get_cert_locations()["default_cert_file"]);'

Then use this to get the cert and put it in the default location found from the code above

wget http://curl.haxx.se/ca/cacert.pem -O <default location>

@Joe 2014-10-01 22:56:00

This was an enormously helpful link to find:

http://php.net/manual/en/migration56.openssl.php

An official document describing the changes made to open ssl in PHP 5.6 From here I learned of one more parameter I should have set to false: "verify_peer_name"=>false

Note: This has very significant security implications. Disabling verification potentially permits a MITM attacker to use an invalid certificate to eavesdrop on the requests. While it may be useful to do this in local development, other approaches should be used in production.

So my working code looks like this:

<?php
$arrContextOptions=array(
    "ssl"=>array(
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ),
);  

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

echo $response; ?>

@hypery2k 2014-12-05 20:37:06

this breaks SSL certification and is a security hole

@Joe 2015-01-27 15:01:30

How would you do it then?

@elitechief21 2015-02-24 17:01:17

As @hypery2k pointed out, you shouldn't just disable verification. See my answer for an alternative solution that doesn't defeat the purpose of using ssl.

@Jasper 2015-03-10 17:15:30

This really shouldn't be done. Rather than side-stepping the issue you should actually fix it. See the better solution from @elitechief21.

@HappyCoder 2015-05-30 21:52:30

This can be frustrating if you are testing from a local environment against an api. In this case I normally nuke the verification.

@laurent 2015-09-02 09:49:00

Since I always come here when I try do this, here is the code to easily copy and paste: file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));

@neelabh 2015-10-03 16:32:58

exactly what I wanted. all other options were with curl_exec and I didn't wanted to mess to much with third party libraries. Also I prefer to nuke the verification. I have a socket.io & localhost communicating with each other over ssl. So self signed certificates cause a royal pain. I just used dev env to avoid these options in production

@Yusef Mohamadi 2016-07-19 19:00:51

Well done, work for me

@prateek31 2016-10-12 13:50:04

Worked for me, though I understand that this breaks SSL certification and is a security hole

@Beyaz 2018-04-13 12:46:36

not worked for me

@site 2019-04-11 18:29:18

Another thing to try is to re-install ca-certificates as detailed here.

# yum reinstall ca-certificates
...
# update-ca-trust force-enable 
# update-ca-trust extract

And another thing to try is to explicitly allow the one site's certificate in question as described here (especially if the one site is your own server and you already have the .pem in reach).

# cp /your/site.pem /etc/pki/ca-trust/source/anchors/
# update-ca-trust extract

I was running into this exact SO error after upgrading to PHP 5.6 on CentOS 6 trying to access the server itself which has a cheapsslsecurity certificate which maybe it needed to be updated, but instead I installed a letsencrypt certificate and with these two steps above it did the trick. I don't know why the second step was necessary.


Useful Commands

View openssl version:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

View PHP cli ssl current settings:

# php -i | grep ssl
openssl
Openssl default config => /etc/pki/tls/openssl.cnf
openssl.cafile => no value => no value
openssl.capath => no value => no value

@shakee93 2018-04-06 17:02:47

following below steps will fix this issue,

  1. Download the CA Certificate from this link: https://curl.haxx.se/ca/cacert.pem
  2. Find and open php.ini
  3. Look for curl.cainfo and paste the absolute path where you have download the Certificate. curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Restart WAMP/XAMPP (apache server).
  5. It works!

hope that helps !!

@swisswiss 2018-05-17 12:24:04

is this safe? i love easy solutions but im missing some information here..

@Geomorillo 2018-05-30 23:42:22

for testing is ok

@ARUNBALAN NV 2016-02-03 18:02:54

If your PHP version is 5, try installing cURL by typing the following command in the terminal:

sudo apt-get install php5-curl

@Andreas 2016-05-15 18:55:25

This has absolutely nothing to do with cURL.

@Charles-Antoine Fournel 2016-08-01 12:56:04

that worked for me.

@hailong 2016-09-23 00:45:24

Installing the php curl should be the correct answer! For my Mac system, I'm using port, and the command is: sudo port install php70-curl

@Entretoize 2017-05-30 13:03:05

worked for me too, thanks

@Aubs 2017-12-31 13:31:56

I had the same issue for another secure page when using wget or file_get_contents. A lot of research (including some of the responses on this question) resulted in a simple solution - installing Curl and PHP-Curl - If I've understood correctly, Curl has the Root CA for Comodo which resolved the issue

Install Curl and PHP-Curl addon, then restart Apache

sudo apt-get install curl
sudo apt-get install php-curl
sudo /etc/init.d/apache2 reload

All now working.

@n.r. 2017-10-14 11:56:26

Had the same error with PHP 7 on XAMPP and OSX.

The above mentioned answer in https://stackoverflow.com/ is good, but it did not completely solve the problem for me. I had to provide the complete certificate chain to make file_get_contents() work again. That's how I did it:

Get root / intermediate certificate

First of all I had to figure out what's the root and the intermediate certificate.

The most convenient way is maybe an online cert-tool like the ssl-shopper

There I found three certificates, one server-certificate and two chain-certificates (one is the root, the other one apparantly the intermediate).

All I need to do is just search the internet for both of them. In my case, this is the root:

thawte DV SSL SHA256 CA

And it leads to his url thawte.com. So I just put this cert into a textfile and did the same for the intermediate. Done.

Get the host certificate

Next thing I had to to is to download my server cert. On Linux or OS X it can be done with openssl:

openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert

Now bring them all together

Now just merge all of them into one file. (Maybe it's good to just put them into one folder, I just merged them into one file). You can do it like this:

cat /tmp/thawteRoot.crt > /tmp/chain.crt
cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt

tell PHP where to find the chain

There is this handy function openssl_get_cert_locations() that'll tell you, where PHP is looking for cert files. And there is this parameter, that will tell file_get_contents() where to look for cert files. Maybe both ways will work. I preferred the parameter way. (Compared to the solution mentioned above).

So this is now my PHP-Code

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));

That's all. file_get_contents() is working again. Without CURL and hopefully without security flaws.

@Dr.X 2018-05-11 13:24:41

What's the file chain.pem? Is this chain.crt?

@n.r. 2018-05-12 14:24:20

It is not the chain.crt, that's for the actual certificate. It's the list if intermediate certificates, aka certificate chain. You don't necessarily need it. Use a SSL cert checker to find out if you need it. If so, you may search for the name of your cert issuer + the term "chain" or "intermediate" to find the correct file.

@elitechief21 2015-02-24 17:00:30

You shouldn't just turn off verification. Rather you should download a certificate bundle, perhaps the curl bundle will do?

Then you just need to put it on your web server, giving the user that runs php permission to read the file. Then this code should work for you:

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/path/to/bundle/cacert.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

Hopefully, the root certificate of the site you are trying to access is in the curl bundle. If it isn't, this still won't work until you get the root certificate of the site and put it into your certificate file.

@Paul Phillips 2016-04-18 15:24:49

This should be the accepted answer.

@vee 2016-05-23 11:25:59

Where is .crt file come from? There is no link in curl website you provide it have only .pem.

@elitechief21 2016-05-23 16:27:20

The pem file should be the same. At the time this was posted, they had two versions of the certificate bundle on their site, a pem and a crt and I just used the crt file for my example (naturally that's the one they would remove). For future reference, crt files are usually renamed pem files, that are renamed so that windows recognizes the file as a certificate file. This won't always be the case, but it is in this case. I'll update my example so that it uses the pem file currently on the curl site

@Ken Koch 2016-08-17 16:02:15

theres also a helpful function stream_context_set_default which can be used so you dont have to pass it into file_get_contents everytime

@zmippie 2017-03-25 19:02:49

Did anyone ever get this to work? I tried with the cacert.pem bundle linked to in this answer, and also with the Comodo CA Root certificate (which is the CA root for the certificate I want to have verified), but it will always fail.

@elitechief21 2017-04-13 16:00:11

If you are having issues getting this to work, you could try adding "capture_peer_cert_chain" => true to $arrContextOptions. Then the call to file_get_contents will also include the certificate chain of the host that you are trying to connect to (base64 encoded). I believe the last certificate listed in the output is the root certificate and that certificate must be in the cert file you are using, in order for this to work. Note that, there may be some other issue with the host or client side that's causing it to fail (i.e. expired host cert, client time is incorrect, etc)

@IncredibleHat 2017-10-10 23:05:36

I found this extremely helpful. Better answer than the accepted. Our server has a cert from RapidSSL where its not present for php on any machine we try to connect to the server with. So we have to squirrel the rapidssl2017.pem away and let php know (thanks to this answer). So being able to tell file_get_contents() where to look for it... is much nicer than using curl for a grab.

@IncredibleHat 2017-10-30 18:06:38

Sadly, after I got everything working in my prior comment on oct 10th... today I come back from vacation, and its all stopped working. No amount of linking or grabbing a pem file is working. Had to temporarily resort to disabling verify :( Will be posting a brand new question sometime here about our specific issue with these crappy RapidSSL certs.

@SaidbakR 2017-11-23 18:41:10

How could set permissions for the pem file?

@zeeshan 2018-03-01 15:05:19

Out of everything that I have tried for over 2 days, after updating to http24 and php7, this is the only solution which worked. However I am not sure about the security concerns. I created a new key using openssl and used that instead of the link provided for file_get_contents. I am not sure why things stopped working earlier and why it is working now. I only hope that there are no security holes. At least it is working now.

@Thomas Valadez 2018-05-21 21:59:22

In case anyone needs additional information on file_get_contents SSL context. php.net/manual/en/context.ssl.php

@OMA 2018-09-24 18:46:58

It works but I wnat to know: Does this "cacert.pem" file downloaded from curl.haxx.se/ca/cacert.pem ever expire? Do I need to download another copy in a few months time or will it work indefinitely?

@OMA 2018-09-24 18:52:11

Well, I've checked the expiration of the "cacert.pem" file with the "openssl" command line and it says it expires on 2028. I used this: openssl x509 -enddate -noout -in cacert.pem

@Josef Glatz 2018-09-25 10:08:51

From a security perspective: next to the usage of the cafile stream context option, it could be very important to also define the allowed ciphers in the ciphers stream context option and forbid those SSL versions which are known as vulnerable. Also setting the stream context option disable_compression to true is recommended to mitigate the CRIME attack vector.

@Muhammad Omer Aslam 2019-08-24 10:34:37

i am getting the same exception but with exif_imagetype which has only 1 parameter , i am on local host with self signed certificate for my virtual host using xampp, any ideas ?

@hangerer 2017-01-12 15:21:41

Had the same ssl-problem on my developer machine (php 7, xampp on windows) with a self signed certificate trying to fopen a "https://localhost/..."-file. Obviously the root-certificate-assembly (cacert.pem) didn't work. I just copied manually the code from the apache server.crt-File in the downloaded cacert.pem and did the openssl.cafile=path/to/cacert.pem entry in php.ini

@Kshitij Mittal 2016-01-05 17:22:36

You basically have to set the environment variable SSL_CERT_FILE to the path of the PEM file of the ssl-certificate downloaded from the following link : http://curl.haxx.se/ca/cacert.pem.

It took me a lot of time to figure this out.

@Daniel P 2016-06-27 16:34:07

The link didn't work, but I found this that is similar: akrabat.com/ssl-certificate-verification-on-php-5-6

@Ben Shoval 2016-10-28 18:29:12

You can get around this problem by writing a custom function that uses curl, as in:

function file_get_contents_curl( $url ) {

  $ch = curl_init();

  curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
  curl_setopt( $ch, CURLOPT_HEADER, 0 );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  curl_setopt( $ch, CURLOPT_URL, $url );
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );

  $data = curl_exec( $ch );
  curl_close( $ch );

  return $data;

}

Then just use file_get_contents_curl instead of file_get_contents whenever you're calling a url that begins with https.

@andlin 2016-10-01 13:33:00

I fixed this by making sure that that OpenSSL was installed on my machine and then adding this to my php.ini:

openssl.cafile=/usr/local/etc/openssl/cert.pem

@andlin 2016-10-31 09:38:45

@Akhi You should be able to find some tutorials if you Google it

@David Refoua 2017-02-24 11:13:31

I used the same method using PHP 7 on IIS, downloaded the cert.pem file and set the php.ini like this, and it worked: openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem

@Chandra Shekhar Goka 2018-03-10 02:06:42

Its worked for me. Thanks.

@Jake 2018-06-12 17:24:16

I downloaded the PEM file from curl.haxx.se/docs/caextract.html - fixed the problem for me on Windows with a particular gstatic.com URL.

@Arvind K. 2019-02-14 14:36:15

Downloading the PEM file from curl.haxx.se/docs/caextract.html didn't work for me on Centos 7. I created a bundle certificate by concatenating main certificate and pkcs7 and place it on server and then specify openssl.cafile path. +1 for right answer and direction.

@Arvind K. 2019-02-14 14:38:49

When creating a bundle don't forget to convert pkcs to pem file before concating it to main certification

@HWD 2019-03-06 22:51:36

This fixed my errors in a localhost environment using a self-signed certificate.

@cweiske 2016-09-06 21:34:02

Reason for this error is that PHP does not have a list of trusted certificate authorities.

PHP 5.6 and later try to load the CAs trusted by the system automatically. Issues with that can be fixed. See http://php.net/manual/en/migration56.openssl.php for more information.

PHP 5.5 and earlier are really hard to setup correctly since you manually have to specify the CA bundle in each request context, a thing you do not want to sprinkle around your code. So I decided for my code that for PHP versions < 5.6, SSL verification simply gets disabled:

$req = new HTTP_Request2($url);
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
    //correct ssl validation on php 5.5 is a pain, so disable
    $req->setConfig('ssl_verify_host', false);
    $req->setConfig('ssl_verify_peer', false);
}

@Joe Lipson 2018-03-05 05:56:35

This helped me find my problem. Even though I'm on PHP 5.6 I was using an outdated api client library that manually specified an old CA file using the cafile context option as per your link above. Removing that from the api client library fixed it for me. Presumably PHP started using OpenSSLs trusted bundle

@SlickTheNick 2016-02-26 20:40:31

Just wanted to add to this since I ran into the same problem and nothing I could find anywhere would work (e.g downloading the cacert.pem file, setting cafile in php.ini etc.)

If you are using NGINX and your SSL certificate comes with an "intermediate certificate", you need to combine the intermediate cert file with your main "mydomain.com.crt" file and it should work. Apache has a setting specific for intermediate certs, but NGINX does not so it must be within same file as your regular cert.

Related Questions

Sponsored Content

8 Answered Questions

[SOLVED] REST HTTP status codes for failed validation or invalid duplicate

0 Answered Questions

file_get_contents() ssl operation failed from my own server only

  • 2018-11-15 14:02:49
  • Shahin
  • 655 View
  • 0 Score
  • 0 Answer
  • Tags:   php https

2 Answered Questions

file_get_contents HTTP request failed! HTTP/1.0 503 Service Unavailable

  • 2014-08-08 14:53:57
  • Norvert John Abella
  • 4467 View
  • -3 Score
  • 2 Answer
  • Tags:   php file-get-contents

2 Answered Questions

[SOLVED] file_get_contents(): SSL operation failed with code 1 (certificate verify failed)

  • 2016-08-12 16:20:25
  • BrightonBoy
  • 14236 View
  • 4 Score
  • 2 Answer
  • Tags:   php apache wamp

2 Answered Questions

[SOLVED] reCaptcha file_get_contents(): SSL operation failed

2 Answered Questions

PHP get_contents() Error

  • 2016-05-07 03:43:36
  • Viyog
  • 698 View
  • 0 Score
  • 2 Answer
  • Tags:   php

1 Answered Questions

Can't send sms using twilio error occured

1 Answered Questions

SSL operation failed with code 1. OpenSSL Error messages:

2 Answered Questions

Sponsored Content