有効な証明書なのにcertificate has expiredになる件
サーバー上のPHPからcURLで外部APIを叩いた時に起こりました。
HTTPのテスト系APIで動作確認した後、HTTPSの本番系APIに切り替えると以下のエラーが返ってきました。
curl: (60) The certificate issuer's certificate has expired. Check your system date and time.
クライアントのWebブラウザから、HTTPSの本番系に正常に接続できていることは確認済みです。
つまりSSL証明書が有効なので、certificate has expired=証明書の有効期限切れではないことは明白です。
試しにサーバー内に入って直接curlコマンドを打ってみます。
[vagrant@dev ~]$ curl https://something.com/
curl: (60) The certificate issuer's certificate has expired. Check your system date and time.
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
ここで確認しなければならないことは「サーバーの時刻が同期されているか?」です。
(エラーメッセージにも書いてあります)
当たり前ですが、証明書の有効期限切れの判定にはシステム時刻を使用しています。
そのためサーバーの時刻を合わせる必要があります。
NTPが設定されているかどうかを確認し、されていなければ設定しましょう。
しかし、私のパターンではサーバーの時刻も合っていました。
ルートCA証明書が失効している
以下のコマンドでca-certificatesパッケージを更新することで解決できました。
sudo yum update ca-certificates
更新前
[vagrant@dev ~]$ yum list installed | grep ca-certificates
Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
ca-certificates.noarch 2015.2.4-70.0.el7_1 @updates
更新後
[vagrant@dev ~]$ yum list installed | grep ca-certificates
ca-certificates.noarch 2021.2.50-72.el7_9 @updates
2015年のパッケージから更新されていなかったようですね。
ちなみにyum updateすら証明書エラーになってしまう場合は、別な手段でca-certificatesパッケージを取得してインストールする必要があります。
なぜ解決したのか?
時は遡ること2021年9月30日。
この日にLet’s Encryptが使用しているDST Root CA X3という証明書の有効期限が切れました。
この影響で古い環境では証明書エラーが発生してしまいます。
Let’s EncryptのルートCA証明書は新しい証明書に更新されていますが、それに対応した証明書を端末にインストールしておかなければ、端末は新しい証明書を認識できません。
こんなルート証明書知らん!
と認識してしまうのですね。
そしてAPIのSSL証明書も、このLet’s Encryptを使っていました。
クライアントであれば、Windows UpdateなどのOSアップデートにあたる更新を行っていれば問題ありません。
サーバーでも同じで、今回のケースでもyum updateを行っていれば問題は起きていませんでした。
しかし長らくyum updateしていなかったため、ルートCA証明書が入っているca-certificatesパッケージが古いままになっていたのです。
余談
PHPプログラムにはJavaScriptのXMLHttpRequestでアクセスしており、エラーが発生した場合はcURLのステータスコードをJSONで返すようにしていました。
そして返ってきたcURLのステータスコードはなぜか0。
お前誰やねん。100番台のステータスコードすら見たことないのに
ちょっとハマりかけましたが、cURLのエラーメッセージを吐き出すようにしたら冒頭のメッセージが出現。
状況的にルートCA証明書絡みなのかなぁと思ったらビンゴでした。