ブラウザがHTTPSサイトに接続するとき、証明書を単純に信頼するわけではありません。証明書チェーンを検証します。これは、サイトの証明書から事前に信頼されたルート認証局(CA)まで連なる一連の証明書です。
3つのレベル
┌─────────────────────────────────┐
│ ROOT CA │ ブラウザ/OSにプリインストール
│ (ISRG Root X1 for LE) │ 自己署名、長い有効期間(20年以上)
└───────────────┬─────────────────┘
│ signs
▼
┌─────────────────────────────────┐
│ INTERMEDIATE CA │ ルートが署名
│ (Let's Encrypt R3/R10) │ 中程度の有効期間(5-10年)
└───────────────┬─────────────────┘
│ signs
▼
┌─────────────────────────────────┐
│ YOUR CERTIFICATE │ 中間CAが署名
│ (example.com) │ 短い有効期間(LEでは90日)
└─────────────────────────────────┘
ルートCA — ブラウザとOSに信頼されています。デバイスの証明書ストアに保存されています。自己署名(ルートの署名者はいません。ルートが信頼の基点です)。ルート鍵はハードウェアセキュリティモジュールでオフライン保管されます。
中間CA — ルートが署名しています。実際にエンドエンティティ証明書を発行します。中間CAが侵害された場合、中間CAのみを失効させればよく、ルートは安全なままです。
エンドエンティティ証明書 — あなたの証明書です。中間CAが署名しています。これがサーバーからブラウザに送信されるものです。
検証の仕組み
ブラウザが証明書を受け取ると:
- 証明書を読む — 発行者名を抽出
- 中間証明書を見つける — サーバーのレスポンスまたはブラウザのキャッシュから
- 中間証明書の署名を検証 — 信頼されたルートが署名しているか確認
- 証明書の署名を検証 — 中間CAが署名しているか確認
- 有効期限を確認 — どちらの証明書も期限切れでないこと
- ドメインの一致を確認 — 証明書のSAN/CNがURLと一致すること
- 失効を確認 — 証明書が失効していないこと(OCSP/CRL)
いずれかのステップが失敗すると、ブラウザはセキュリティ警告を表示します。
中間証明書が重要な理由
本番環境で最も一般的なSSLエラーは中間証明書の欠落です。サーバーが中間証明書なしで自分の証明書だけを送信すると、ブラウザは証明書チェーンを構築できません。
Nginxはfullchain.pem(証明書 + 中間証明書を結合)を期待します:
ssl_certificate /etc/ssl/fullchain.pem; # cert + intermediate
Apacheは別々のファイルを使用します:
SSLCertificateFile /etc/ssl/cert.pem # your cert
SSLCertificateChainFile /etc/ssl/chain.pem # intermediate
GetHTTPSはfullchain.pemとcert.pem + chain.pemの両方を提供するため、どのサーバーにも対応できます。
Let’s Encryptのチェーン
Let’s Encryptの現在のチェーン:
ISRG Root X1 (root, in all trust stores)
└── Let's Encrypt R10 (intermediate, signs your cert)
└── yourdomain.com (your certificate)
古いデバイスとの互換性のためにIdenTrustのDST Root CA X3を経由するクロス署名パスもありますが、ほとんどのユースケースではもう必要ありません。
よくある質問
「証明書が信頼されていない」エラーを修正するには?
ほぼ常に中間証明書の欠落が原因です。fullchain.pem(Nginx)を使用するか、SSLCertificateChainFile chain.pem(Apache)を追加してください。以下でテストできます:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
「verify return:1」(正常)と「verify return:0」(チェーンの問題)を探してください。
ブラウザでチェーンを確認できますか?
はい。パドロックをクリック → 「証明書」(または「この接続は保護されています」→「証明書は有効です」)。チェーンが表示されます:あなたの証明書 → 中間CA → ルートCA。
ルートCAが侵害されたらどうなりますか?
ブラウザ/OSの更新を通じてルートがトラストストアから削除されます。そのルートの下で発行されたすべての証明書が信頼されなくなります。これは極めてまれです。ルート鍵は物理的なアクセス制御を持つオフラインのハードウェアセキュリティモジュールに保管されています。
Let’s Encryptはなぜルートで直接署名せず中間証明書を使うのですか?
セキュリティの分離です。中間鍵が侵害された場合、中間CAのみを失効させればよく、ルートは安全なままです。ルート鍵はオフラインで保管され、中間証明書の署名にのみ使用されます。これはすべての主要な認証局の標準的な慣行です。
サーバーにルート証明書をインストールする必要がありますか?
いいえ。ルート証明書はブラウザとOSにプリインストールされています。サーバーは証明書 + 中間証明書のみを送信する必要があります。ブラウザは既にルートを持っており、チェーンの検証に使用します。
チェーンの問題をデバッグする
本番環境で最も一般的なSSLエラーは不完全な証明書チェーンです。体系的な診断方法を紹介します:
ステップ1: サーバーが送信するチェーンを確認する
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null
出力を確認します:
Certificate chain
0 s:CN = example.com
i:C = US, O = Let's Encrypt, CN = R10
1 s:C = US, O = Let's Encrypt, CN = R10
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
---
Verify return code: 0 (ok)
- Depth 0 = あなたの証明書
- Depth 1 = 中間証明書
- 「Verify return code: 0 (ok)」 = チェーンが完全
depth 0のみ(depth 1がない)場合、中間証明書が欠落しています。
ステップ2: チェーンを修正する
Nginx: fullchain.pem(証明書 + 中間証明書を結合)を使用:
ssl_certificate /etc/ssl/fullchain.pem; # NOT cert.pem
Apache: 中間証明書を明示的に追加:
SSLCertificateChainFile /etc/ssl/chain.pem
chain.pemを紛失した場合: Let’s Encryptの中間証明書をletsencrypt.org/certificatesからダウンロードするか、GetHTTPSで証明書を再発行してください。
ステップ3: 修正を確認する
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | grep "Verify return code"
# 表示されるべき: Verify return code: 0 (ok)