Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring cURL for SSL

Tags:

php

curl

ssl

Our site recently shifted from http to https. It has REST API calls called by our customers which is now not working:

cURL before SSL (working):

$ch = curl_init();

curl_setopt($ch,CURLOPT_URL,$api_call_url);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

$result = curl_exec($ch);

curl_close($ch);

cURL after SSL(not working):

$ch = curl_init();

curl_setopt($ch,CURLOPT_URL,$api_call_url);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, "/customers_path_on_their_server/to/our_cacert_they_exported_via_firefox.crt");   //X.509 Certificate

$result = curl_exec($ch);

curl_close($ch);

Do I need to setup anything on our server other than ask client to add CURLOPT_SSL_VERIFYPEER, CURLOPT_SSL_VERIFYHOST, CURLOPT_CAINFO on their REST integration code?

I'm really a newbie in https and I don't know what exactly is the term I need to search, searched cURL SSL for hours already...

BTW, our site is using amazon ec2 hosting if that information is important...

Here is the returned cURL error:

 error:SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

cURL version: 7.21.6

SSL version: OpenSSL/1.0.0e

like image 564
Woppi Avatar asked Nov 20 '12 06:11

Woppi


2 Answers

Do not turn off peer and host verification as others often suggest. This is an insecure workaround to the real problem. These features exist for good reason: so you can trust, via 3rd party, that the system you're connecting to is the one you expect.

First find if your OS includes a directory of certificates. Certificate authorities will often be included. On Ubuntu, for example, it's often in the directory /etc/ssl/certs, while Red Hat-based distros will include it in /etc/pki/tls/certs. If this directory exists, set the CA path parameter:

curl_setopt($ch, CURLOPT_CAPATH, '/etc/ssl/certs');

Alternatively you can reference a single CA certificate file. Include a cacert.pem file in your project or install it on your server. Download from a trusted source, e.g. cacert.org. For a single file do not set the CAPATH and instead only set CAINFO:

curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem');
like image 59
Matt S Avatar answered Oct 02 '22 12:10

Matt S


To expand on Matt's answer, the two cURL settings CURLOPT_CAPATH and CURLOPT_CAINFO perform different functions and behave quite differently.

CURLOPT_CAINFO refers to a single file, either a certificate bundle or, less commonly, a single certificate. This value can (and should) be set permanently in php.ini using the curl.cainfo directive.

A certificate bundle is just a bunch of certificates concatenated, indicating which CA's issuances will be trusted. If you don't have one, your OS likely provides a package that can be installed. On RHEL, this can be done with yum install ca-certificates, whereas Debian/Ubuntu uses apt install ca-certificates. If your OS doesn't provide a package, the makers of cURL provide a download link to a bundle that's created by Mozilla.

CURLOPT_CAPATH refers to a directory holding individual certificates. A certificate in this directory can be used by cURL if the hostname of your request matches the common name of the certificate. However, it's important to note that this will not happen automatically. If you set the CA path, you must ensure any certificates you want to use in that directory have appropriately named symlinks (see below.)


So, putting it all together, we get the following typical setup.

Point cURL to the system bundle permanently, using something like the following in php.ini:

[curl]
; for RHEL
curl.cainfo=/etc/pki/tls/certs/ca-bundle.crt
; for Debian/Ubuntu
curl.cainfo=/etc/ssl/certs/ca-certificates.crt

This is all you need to do to connect to public servers with certificates issued by trusted CAs, i.e. pretty much everything on the internet. If that's all you need, you're done.

For the remaining cases such as connecting to a device that uses a certificate issued by your company's CA, ensure that you've copied the relevant cert to a directory and set up symlink names properly:

#!/bin/bash
cd /path/to/my/certs/
for c in *.crt; do ln -s "$c" "$(openssl x509 -hash -noout -in "$c").0"; done

Then use this cURL option to set the CA path within your code:

<?php
$ch = curl_init("https://some.device.internal");
curl_setopt($ch, CURLOPT_CAPATH, "/path/to/my/certs/");
$result = curl_init($ch);
like image 25
miken32 Avatar answered Oct 02 '22 12:10

miken32