要件
ルート証明書を使用して、とあるホストにアクセスする用事があった。
ゴールは、PHP の cURL 関数でアクセスできること。
環境
- CentOS 7
- php7.3
- cURL 関数を使用
事象
php の cURL 関数でエラーが発生
証明書のパスを指定して cURL 関数を実行してみたところ、
以下のようなエラーが出てしまった。
# オプション定義(抜粋)
$ch = curl_init( "https://xxxx" );
curl_setopt( $ch, CURLOPT_SSLCERT, "./aaa.pem" );
curl_exec( $ch );
# 実行結果
cURL の戻り値:58
curl_error のメッセージ:
unable to load client key: -8178 (SEC_ERROR_BAD_KEY)
調査
CUI の curl でアクセスしてみる
では、同じ URL に対して CUI の curl で試したらどうなるか確認。
# コマンド
curl -v https://xxxx --cacert ./aaa.pem
# 結果
< HTTP/1.1 200 OK
~省略~
* CAfile: ./aaa.pem
CApath: none
~省略~
curl: (58) unable to load client key: -8178 (SEC_ERROR_BAD_KEY)
同じエラーが出てしまった。
エラーメッセージを見てみる
curl のエラーの中に
CApath: none
という文言があった。
一つ上にある CAfile
には引数の --cacert
の値が入っているので、
別の引数で指定しないといけない?
調べてみると、--capath
という引数で
CApath
に証明書のディレクトリを指定できるらしい。
早速試してみる。
解決編
curl に capath オプションを指定してみる
# コマンド
curl -v https://xxxx --capath <aaa.pem が存在するディレクトリのパス>
# 結果
< HTTP/1.1 200 OK
~省略~
* CAfile: /etc/...省略.../ca-bundle.crt
CApath: <aaa.pem が存在するディレクトリのパス>
~省略~
URL のデータ
取得成功!
ちなみに、ディレクトリは絶対パスでも相対パスでも指定可。
PHP の cURL 関数でも試してみる
PHP の cURL 関数では CURLOPT_CAPATH
というオプションで
証明書のパスを指定できるらしい。
# オプション定義
curl_setopt( $ch, CURLOPT_CAPATH, "<aaa.pem が存在するディレクトリのパス>" );
# 実行結果
成功(CUI と同じ)
ファイル名ではなくディレクトリの指定が必要だったんですね。
追記
OS の証明書ストアに登録する方法
独自の証明書をOSの証明書ストアに登録すれば、
CApath
の指定が無くても読み込んでくれます。
※この方法が普通みたいですね;
# cp aaa.pem /usr/share/pki/ca-trust-source/anchors/
# update-ca-trust
ただし、この場合は独自証明書が OS 全体に適用されてしまうため、
ユースケースによっては、前述の様に証明書パスを指定した方が良いかもしれません。
ユースケースの例としては・・・
- 独自証明書をサービス固有で定義したい場合
- サービス毎にディレクトリを作っており、
他のサービスに影響を与えたくない場合など - OS 自体にインストールしないので、
Ansible などでの配布時に OS にほぼ影響を及ぼさない
- サービス毎にディレクトリを作っており、
- 証明書を cron などで自動的に取得 & 配置 している場合
- 外部サービスの証明書が頻繁に更新される場合、
証明書ストアを更新するよりも自動化の手間が省ける
- 外部サービスの証明書が頻繁に更新される場合、
証明書ファイルを明示的に指定
CURLOPT_CAPATH
を指定していれば、証明書ファイル名を指定しなくても
ディレクトリ内にあるファイルから自動的?に証明書を見つけてくれますが、
CURLOPT_CAINFO
で明示的に証明書パスを指定することもできるようです。
※その場合でも CURLOPT_CAPATH
は指定必須
# オプション例
curl_setopt( $ch, CURLOPT_CAINFO , "./aaa.pem" );
curl_setopt( $ch, CURLOPT_CAPATH, "<aaa.pem が存在するディレクトリのパス>" );
業務で長く使っていて色々なサーバの証明書が混在していたり、
有効期限切れのものが混ざっていたりしたら
この方が安全かもしれません。