LoginSignup
15
14

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-12-09

僕の場合は、現在限定公開中(ビジネスプランのみのようです)のChatWorkAPIを叩くときに出ました。

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

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

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

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

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

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

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

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


<?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で取ってくるといった、たいそうな仕組み。
しかもアップロードされているcacert.pemも、サイズが大きくて、毎回取ってくるにはちょっと時間がかかります。

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

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


<?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);

?>

たとえば、前述のコードを別ファイル、getca.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(平文)でダウンロードしてきて使うっていうのは、セキュリティ上あまり好ましいものではありません。

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

15
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14