Configuring cURL for SSL
Asked Answered
C

2

6

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

Corfam answered 20/11, 2012 at 6:25 Comment(5)
#6400800Impeachment
@Impeachment it's still not working. I've checked if the exported .crt file from Firefox that is stored in our customer's server is the same as our server's .crt file. They are the same. Our server's .crt file is located at /etc/apache2/ssl/our_site.crt. Do you think I should move it elsewhere... I've been figuring this out for more than 5 hours already.... OH MY...Corfam
Also ask them to export certificate using 2nd option in select (in Firefox) i.sstatic.net/kZpCQ.pngImpeachment
@Impeachment Yep. That's what I did. I have another server that would act as the customer and saved the .crt file exported from firefox. I actually compared it to the .crt file(located in etc/apache2/ssl) on our rest api server and it's the same. I also made the .crt file permissions 777.Corfam
Just set CURLOPT_SSL_VERIFYPEER, 0 and CURLOPT_SSL_VERIFYHOST, 0 to solve this Error, you don't need to add certs. (You don't need to verify if you trust the source).Richia
C
7

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');
Charissecharita answered 18/9, 2013 at 15:9 Comment(0)
S
1

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);
Stockholm answered 15/5, 2019 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.