5

The following script works on PHP 5.6.23:

$options = [
    CURLOPT_POST => 1,
    CURLOPT_URL => 'https://uat.dwolla.com/oauth/rest/offsitegateway/checkouts',
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_POSTFIELDS => json_encode(['name'=>'value']),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_CAINFO => '/path/to/certs/GoDaddyRootCertificateAuthority-G2.crt',
];
$ch = curl_init();

curl_setopt_array($ch, $options);
if( ! $result = curl_exec($ch)) $err = curl_error($ch);
else $err = null;

curl_close($ch);

if($err) echo $err;
else print_r(json_decode($result,true));

I get the expected response from Dwolla's payment API. To make my script more dynamic, I tried to change it to refer to the directory that hosts the certs I want cURL to trust. So I changed the last option (CURLOPT_CAINFO) to:

CURLOPT_CAPATH => '/path/to/certs'

This breaks the script however and no connection is made; the error is:

SSL certificate problem: unable to get local issuer certificate

I know the directory is correct and the cert file is valid since the original script refers to the cert in that same directory. I expected cURL to scan the files in the directory and find the cert it needs but this isn't happening. Why is this?

BeetleJuice
  • 39,516
  • 19
  • 105
  • 165
  • Rename the `crt` to `test.crt` and try again. – Hackerman Aug 26 '16 at 21:10
  • @Hackerman made no difference; it works the first way (with `CURLOPT_CAINFO`). Same error the 2nd way (with `CURLOPT_CAPATH`). – BeetleJuice Aug 26 '16 at 21:19
  • Try with this one: https://certs.godaddy.com/repository/gdroot-g2.crt – Hackerman Aug 26 '16 at 21:21
  • @Hackerman I think if the cert was the problem, the original script in the OP wouldn't work (since it verifies the remote server against the cert I was already using). Still I tried the cert you sent, and the results are the same. Original script works; modified script yields the same error – BeetleJuice Aug 26 '16 at 21:27
  • Just tried (with php.ini configuration) using `curl.capath` (as well as `openssl.capath`) - both have no effect. But with `openssl.cainfo` it works. I'm not sure why `capath` doesn't work. – Charlotte Dunois Aug 26 '16 at 23:09

1 Answers1

6

It does not work if you just point CApath to some directory and put the certificates into this directory. To make the finding of the correct CA certificate efficient the files in this directory need to have names derived from the subject of the certificate. For example you'll might find the following in /etc/ssl/certs:

   ff783690.0 -> UTN_USERFirst_Hardware_Root_CA.pem
   ff588423.0 -> ComSign_CA.pem
   ...

Here the filenames are based on the hashes of the certificate's subject and point to the real certificate. For information on how to create the necessary filename see How to calculate the hash value used by CA file names .

See also the man page for openssl verify:

-CApath directory
A directory of trusted certificates. The certificates should have names of the form: hash.0 or have symbolic links to them of this form ("hash" is the hashed certificate subject name: see the -hash option of the x509 utility). Under Unix the c_rehash script will automatically create symbolic links to a directory of certificates.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172