指定した対象サーバーの TLS(SSL)証明書チェーンを動的に解析し、エンド証明書から上位の証明書(中間CA、ルート証明書)へと信頼チェーン(Chain of Trust)が正しく繋がっているかを検証するシェルスクリプトです。
- 動的なチェーン追跡: サーバーから返される証明書の階層数(Depth)を動的に判定し、最上位まで途切れなく
Issuer(発行者)とSubject(所有者)が一致するかを検証します。 - 主体者DNの表示: 各 Depth の証明書の主体者DN(Subject DN)を一覧表示し、チェーンの構成を一目で確認できます。
- ホスト名(SAN/CN)の検証: 証明書の発行対象が指定したサーバー名と一致するかを検証します(ブラウザの
ERR_CERT_COMMON_NAME_INVALID相当のエラーを事前に検知できます)。 - 有効期限のチェック: チェーン内の各証明書の有効期限(
notAfter)を表示し、期限切れはエラー、30日以内の失効は警告として通知します。中間証明書の期限切れも検知できます。 - ローカルストア補完の警告: サーバーが中間証明書を送信しておらず、検証マシンのローカル証明書ストアで補完された場合に警告します(手元では成功してもブラウザ等ではエラーになるケースの検知)。
- 直感的なエラーメッセージ: チェーン切れ(設定漏れ)、ホスト名不一致、期限切れなど、エラーの種類に応じた原因や解決策のヒントを日本語で分かりやすく出力します。
- スマートな引数補完: FQDN(ドメイン名)を1つ指定するだけで、ポート番号やサーバー名を自動で補完して検証を実行します。
- 外部依存なし:
openssl,awk,grepなど、標準的なコマンド群のみで動作します。
bashopenssl(s_clientコマンドを使用)- 標準のテキスト処理ツール (
awk,grep,cutなど)
補足:
- ホスト名検証には
-verify_hostnameオプションに対応した openssl が必要です。未対応の環境(古い openssl や一部の LibreSSL)では、警告を表示した上でホスト名検証のみスキップされます。timeout(またはgtimeout)コマンドが存在する環境では、接続に15秒のタイムアウトが適用されます。無い環境でもそのまま動作します。- チェーン検証の結果は検証マシンのローカル証明書ストアの内容に依存します。ブラウザは独自の信頼ストアや AIA フェッチ(欠落中間証明書の自動取得)を持つため、挙動が完全に一致するとは限りません。
スクリプトに実行権限を付与してから使用してください。
chmod +x validate_chain.shドメイン名を1つ指定するだけで、自動的に ポート443 と ServerName (SNI) が補完されて実行されます。
./validate_chain.sh certs.nii.ac.jp(内部的に certs.nii.ac.jp:443 に対して ServerName: certs.nii.ac.jp として接続します)
明示的にポート番号を指定したい場合は以下のように実行できます。
./validate_chain.sh example.com:8443引数を省略した場合は、スクリプトにデフォルトで組み込まれているホスト(certs.nii.ac.jp)に対して検証を行います。動作確認に便利です。
./validate_chain.sh接続先のホスト(IPアドレスや別ポート)と、SNIとして送信するサーバー名を明確に分けたい場合に使用します。
./validate_chain.sh 192.168.1.100:443 example.comチェーンが正常にルート証明書または自己署名証明書まで辿れた場合は、各階層の主体者DN・有効期限・一致状況と [SUCCESS] が表示されます。
接続中: certs.nii.ac.jp:443 (ServerName: certs.nii.ac.jp) ...
証明書チェーン:
Depth 0 主体者DN: C=JP, ST=Tokyo, L=Chiyoda-ku, O=National Institute of Informatics, CN=certs.nii.ac.jp
Depth 1 主体者DN: C=JP, O=SECOM Trust Systems CO.,LTD., CN=NII Open Domain CA - G7 RSA
Depth 2 主体者DN: C=JP, O=SECOM Trust Systems CO.,LTD., OU=Security Communication RootCA2 (ローカル証明書ストアのルート証明書)
証明書の有効期限:
Depth 0 有効期限(notAfter): Aug 15 04:23:23 2026 GMT [OK]
Depth 1 有効期限(notAfter): May 29 05:00:39 2029 GMT [OK]
[OK] Depth 0 の発行者DNと Depth 1 の主体者DNが一致しました。
[OK] Depth 1 の発行者DNと Depth 2 の主体者DNが一致しました。
[SUCCESS] 証明書チェーンはエンド証明書からルート証明書まで正しく繋がっています。(最大Depth: 2)
最上位のルート証明書は、サーバーから送信されず検証マシンの証明書ストアにあるのが正常な構成のため、(ローカル証明書ストアのルート証明書) と注記されます。
サーバー側の設定ミス(中間証明書のインストール漏れなど)によりチェーンが途切れている場合、以下のようなどこで途切れたかの情報と共にエラーが表示されます。
接続中: www.example.com:443 (ServerName: www.example.com) ...
[ERROR] 証明書の信頼チェーン検証に失敗しました。(ルートまで辿れません)
詳細: unable to get local issuer certificate (エラーコード: 20)
原因: サーバー側に中間証明書が正しくインストールされていないか、必要なクロスルート証明書が不足しているため、チェーンが途切れています。
証明書の SAN/CN が指定したサーバー名と一致しない場合は、以下のようなエラーが表示されます。
接続中: www.example.com:443 (ServerName: www.example.com) ...
[ERROR] 証明書のホスト名検証に失敗しました。(SAN/CN の不一致)
詳細: hostname mismatch (エラーコード: 62)
原因: 証明書の SAN/CN が、指定したサーバー名(www.example.com)と一致していません。証明書の発行対象(コモンネーム / サブジェクト代替名)を確認してください。
チェーン内の証明書の有効期限が切れている場合は、以下のようなエラーが表示されます。
接続中: www.example.com:443 (ServerName: www.example.com) ...
[ERROR] 証明書の有効期限検証に失敗しました。(期限切れ)
詳細: certificate has expired (エラーコード: 10)
原因: チェーン内の証明書の有効期限が切れています。証明書を更新してください。
また、有効期限が30日以内に迫っている証明書がある場合は、検証は成功しつつ Certificate expiration: セクションに [WARN] が表示されます。
サーバーが中間証明書を送信しておらず、検証マシンのローカル証明書ストアによってチェーンが補完された場合は、以下のような警告が表示されます。この状態は、手元の検証では成功しても、初回アクセスのブラウザや他のクライアントでは検証エラーになる恐れがあります。
証明書チェーン:
Depth 0 主体者DN: CN=www.example.com
Depth 1 主体者DN: C=US, O=Example CA, CN=Example Intermediate CA [WARN] サーバー未送信(ローカル証明書ストアで補完)
...
[WARN] チェーンの一部がローカルの証明書ストアで補完されています。サーバーが中間証明書を送信していない可能性が高く、この環境では検証に成功しても、初回アクセスのブラウザや他のクライアントでは検証エラーになる恐れがあります。
解決のヒント: このエラーが出た場合、検証対象のウェブサーバー(Apache, Nginxなど)において、「サーバー証明書」単体だけでなく、「中間CA証明書」「クロスルート証明書」も含んだ正しい順序で設定・結合されているかを確認してください。
詳しい設定手順については、以下のマニュアルをご参照ください: https://nii-auth.atlassian.net/wiki/spaces/UPKIManual/pages/43877668/Apache+IIS+Nginx
このプロジェクトは MIT License の下で公開されています。
詳細については、プロジェクトのルートディレクトリにある LICENSE ファイルをご覧ください。