ローカルサーバからWebサービスAPIへSSL接続時に出るcurlのエラー対処

僕の場合は、現在限定公開中（ビジネスプランのみのようです）の<a href="http://www.chatwork.com/ja/" target="_blank">ChatWork</a>APIを叩くときに出ました。

ちなみにChatWork APIへの接続方法は、ストックさせていただきましたが、以下の記事を参考にさせていただきました。

ChatWork API を叩く PHP と Python のサンプル
http://qiita.com/wings1685/items/9090f59ab09e73f7c8fb

ただ、ググると、ChatWork関係なく、他のサービス、FacebookやTwitterのAPIでも起きているようですね。

PHPのcurlを使ってSSL（https）のサイトへ接続すると、以下のエラーを吐きます。

<code>"SSL certificate problem, verify that the CA cert is OK.
Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed"</code>

まあ、そりゃそうですよね。ローカルサーバなので、サイト証明書なぞ、ないですもの。

どうにかならないものかと、さらに調べてみると、curl本家サイトに、mozilla.org から自動的にぶっこぬいてきた、サイト証明書をアップしているページを見つけました。

http://curl.haxx.se/ca/

ここからpemファイルをダウンロードして、ローカルのどこかに置いて接続時に使ってやれば、どうにか動作するようにはなりますよ、ということらしい。

```php

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.chatwork.com/v1/me');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-ChatWorkToken: 発行されたAPI用ハッシュ値'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //データを直接出力せずに文字列で返す

//CA証明書
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAINFO, '/var/www/inc/cacert.pem'); //ローカルでの絶対パス指定
//Windowsの場合は、こんな感じ（あくまで絶対パス）↓
//curl_setopt($ch, CURLOPT_CAINFO, 'C:\Users\mhibara\Documents\xampp\htdocs\chatwork\cacert.pem');

$response = curl_exec($ch);

if ( $response == false){
  var_dump(curl_error($ch));
  exit;
}

curl_close($ch);

echo nl2br(print_r(json_decode($response, true), true));
?>

```

うまく動作しました。

ところが、このサイト証明書ですが、定期的に変更されるのだとか。そりゃそうですよね。

とはいえ、ググって出てくるような解決策としては、毎回CRONで取ってくるといった、たいそうな仕組み。
しかもアップロードされている<code>cacert.pem</code>も、サイズが大きくて、毎回取ってくるにはちょっと時間がかかります。

僕としては、それほど頻繁にローカルサーバを起動しているわけでもなく、<strong>開発しているときだけ</strong>使いたい。

そこで、前述のサイト証明書ファイルが更新されていたら、ローカルに引っ張ってきて上書きするという仕組みを、同様にcurlを使ってやってみました。

```php

<?php

$pemfilepath = '/var/www/inc/cacert.pem'; //pemファイルが保存されているローカルファイルパス（絶対パス）
$ch = curl_init('http://curl.haxx.se/ca/cacert.pem');

//出力から本文を削除（要らない）
curl_setopt($ch, CURLOPT_NOBODY, true);
//更新日時を取得しようと試みる
curl_setopt($ch, CURLOPT_FILETIME, true);
$result = curl_exec($ch);

if ($result === false) {
  die(curl_error($ch)); //失敗
}

$timestamp = curl_getinfo($ch, CURLINFO_FILETIME);
if ($timestamp != -1) {
  //更新日時の取得に失敗
}
elseif ( file_exists($pemfilepath) == true){
  $dtPem = filemtime($pemfilepath);
  //ローカルファイルと、ダウンロード先のファイルの更新日時を比較する
  if ( $timestamp > $dtPem){
    //新しい場合なら、CA証明書の更新（丸々上書き）
    $pem_contents = file_get_contents('http://curl.haxx.se/ca/cacert.pem');
    file_put_contents($pemfilepath, $pem_contents);
  }
}
curl_close($ch);

?>

```

たとえば、前述のコードを別ファイル、<code>getca.php</code>とでもしておいて、
必要なPHPファイルにインクルードして読み込めば便利かもしれません。

```php

<?php
//毎回インクルードして読み込む
require_once('getca.php');

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.chatwork.com/v1/me');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-ChatWorkToken: 発行されたAPI用ハッシュ値'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //データを直接出力せずに文字列で返す

//CA証明書
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAINFO, '/var/www/inc/cacert.pem'); //ローカルでの絶対パス指定

$response = curl_exec($ch);

if ( $response == false){
  var_dump(curl_error($ch));
  exit;
}

curl_close($ch);

echo nl2br(print_r(json_decode($response, true), true));

?>

```

とはいえ、mozilla.org から、ぶっこぬいてきたサイト証明書を、しかも、SSLではなく、ただのhttp（平文）でダウンロードしてきて使うっていうのは、セキュリティ上あまり好ましいものではありません。

ローカルサーバで、ちょこっとテストしてみたい、というときだけにお試しください。