公開鍵暗号だけでは足りない理由
AがBへ秘密の文章を送りたいとします。AはBの公開鍵で文章を暗号化し、Bは自分の秘密鍵で復号します。この形だけを見ると安全そうですが、ひとつ大きな穴があります。
それは、Aが受け取った公開鍵が本当にBの公開鍵なのか、という問題です。公開鍵は秘密にするものではないためネットワーク越しに配れます。しかし、途中に攻撃者Mがいると、Bの公開鍵の代わりにM自身の公開鍵をAへ渡せてしまいます。
sequenceDiagram participant A as A participant M as 攻撃者M participant B as B B--xM: Bの公開鍵Kb M-->>A: Mの公開鍵Kmを「Bの鍵」として渡す A->>M: Kmで暗号化した秘密文章 M->>M: Mの秘密鍵で復号して読む M->>B: Kbで再暗号化して転送
Aから見ると「公開鍵で暗号化して送った」ように見えます。Bから見ても「自分の秘密鍵で復号できた」ように見えます。それでも、途中のMに内容を読まれている可能性があります。これが中間者攻撃の典型です。
必要なのは公開鍵の認証
公開鍵暗号が保証するのは、「この公開鍵に対応する秘密鍵を持つ人しか読めない」という性質です。一方で、「この公開鍵の持ち主が本当にBである」ことは別問題です。
そこでWebのHTTPSでは、単に公開鍵を渡すのではなく、サーバ証明書を渡します。サーバ証明書には、概念的には次のような情報が入っています。
| 項目 | 意味 |
|---|---|
| 対象ドメイン | www.example.com など、この証明書が使える名前 |
| 公開鍵 | サーバーの秘密鍵に対応する公開鍵 |
| 発行者 | 証明書へ署名した中間CA |
| 有効期間 | 証明書が有効な開始日時と終了日時 |
| 署名 | 発行者の秘密鍵で作られた改ざん検出用の値 |
重要なのは署名です。攻撃者は「対象ドメインは www.example.com、公開鍵はKmです」というデータを勝手に作れます。しかし、信頼されたCAの秘密鍵でそのデータへ署名することはできません。
証明書チェーンの全体像
ブラウザは、サーバ証明書を単独で信じません。サーバ証明書を署名した中間CA、その中間CAを認めたルートCAへと署名をたどります。このつながりを証明書チェーンと呼びます。
www.example.com のサーバ証明書
内容: www.example.com の公開鍵は Kb
署名: 中間CAの秘密鍵で署名
中間CA証明書
内容: この中間CAの公開鍵は Ki
署名: ルートCAの秘密鍵で署名
ルートCA証明書
内容: このルートCAの公開鍵は Kr
信頼: OSやブラウザにあらかじめ登録済み
検証の向きは、サーバーから届いた証明書を起点に上へ向かいます。まずサーバ証明書の署名を中間CAの公開鍵で検証し、次に中間CA証明書の署名をルートCAの公開鍵で検証します。最後に、そのルートCAが端末の信頼ストアに入っているかを確認します。
検証はどの順番で進むのか
ブラウザが https://www.example.com へ接続したときの流れを、もう少し細かく見ると次のようになります。
sequenceDiagram
autonumber
participant Browser as ブラウザ
participant Server as www.example.com
participant Store as 信頼ストア
Browser->>Server: HTTPS接続を開始
Server-->>Browser: サーバ証明書と中間CA証明書を送信
Browser->>Browser: 証明書の対象ドメインを確認
Browser->>Browser: 中間CAの公開鍵でサーバ証明書の署名を検証
Browser->>Browser: ルートCAの公開鍵で中間CA証明書の署名を検証
Browser->>Store: ルートCAが信頼済みか確認
Store-->>Browser: 信頼済み
Browser->>Browser: サーバーの公開鍵を信用する
署名検証がすべて成功し、アクセス先のホスト名と証明書のSANが一致し、期限などの条件も問題なければ、ブラウザは「この公開鍵は www.example.com のものだ」と判断できます。
SANには何が書かれているのか
SANはSubject Alternative Nameの略で、証明書がどの名前に対して有効なのかを列挙する領域です。現在のブラウザは、昔ながらのCommon Nameではなく、基本的にSANを見てアクセス先のホスト名と証明書が一致しているかを確認します。
たとえば https://www.example.com にアクセスした場合、ブラウザは証明書のSANに DNS:www.example.com が含まれているかを確認します。署名チェーンが正しくても、SANにアクセス先の名前が入っていなければ、その証明書は別サイト用なので拒否されます。
証明書A
SAN:
DNS:example.com
DNS:www.example.com
https://www.example.com で使える
https://api.example.com では使えない
証明書B
SAN:
DNS:*.example.com
https://api.example.com で使える
https://www.example.com で使える
https://example.com では使えない
ワイルドカードの *.example.com は、基本的に1階層下の名前にだけ一致します。つまり api.example.com や www.example.com には一致しますが、親ドメインの example.com には一致しません。そのため実務では、example.com と *.example.com の両方をSANに入れる構成がよく使われます。
| SANの種類 | 用途 |
|---|---|
DNS | WebサイトやAPIのホスト名。HTTPSのサーバ証明書で最もよく使う |
IP Address | IPアドレスで直接アクセスする証明書に使う。DNS名とは別扱い |
URI | サービス識別子など、用途が限定された名前を入れる場合がある |
Email | S/MIMEなどメール証明書で使われることがある |
ここで大切なのは、CAの署名は「このSANに書かれた名前と、この公開鍵の組み合わせを認める」という意味を持つことです。攻撃者が正規CAから www.evil.com 用の証明書を取れても、そのSANには www.example.com が入っていないため、www.example.com のなりすましには使えません。
ルートCAはなぜ特別なのか
中間CAの公開鍵は、さらに上のルートCAによって保証されます。では、そのルートCAの公開鍵は誰が保証するのでしょうか。ここが信頼の終点です。
ルートCA証明書は、Windows、macOS、iOS、Android、FirefoxなどのOSやブラウザに、信頼済みの一覧としてあらかじめ入っています。ブラウザはネットワーク越しに届いたルートCAをその場で信じるのではなく、端末内の信頼ストアに入っているルートCAを信頼の起点にします。
信頼ストアとは
あらかじめ信頼できる認証局(CA)の公開鍵がリスト化されたものです。
信頼ストアを汚染されたらどうなるか
ここで自然に出てくる疑問があります。もしOSやブラウザの配布時点で、信頼ストアに偽のルートCA証明書を入れられたらどうなるのか、という疑問です。結論から言うと、その端末上ではHTTPSの信頼は崩れます。
攻撃者が偽のルートCA証明書を信頼ストアに登録でき、さらにそのルートCAの秘密鍵を持っているなら、攻撃者は www.example.com 用の偽証明書に署名できます。ブラウザは証明書チェーンをたどった結果、最後に信頼ストア内のルートCAへ到達してしまうため、その偽証明書を正規のものとして扱う可能性があります。
偽のサーバ証明書
対象: www.example.com
公開鍵: 攻撃者の公開鍵 Km
署名: 偽ルートCAまたは偽中間CA
信頼ストア
偽ルートCAが登録済み
ブラウザの判断
チェーンが信頼ストアへ到達する
その端末では偽証明書を信頼してしまう
つまりWeb PKIは、純粋な暗号技術だけで完結しているわけではありません。OSやブラウザの配布元、アップデートの署名検証、ルートCAを追加する審査プロセス、CAへの監査といった運用上の信頼も含めて成立しています。
さらに言えば、OSそのものが悪意あるものなら、信頼ストア以前に、証明書検証処理、画面表示、キーボード入力、通信内容をいくらでも改ざんできます。HTTPSは「端末とOSがある程度信頼できる」という前提の上で、ネットワーク上の盗聴や中間者攻撃から守る仕組みです。
なぜ中間CAを挟むのか
ルートCAが直接すべてのWebサイト証明書に署名すれば単純に見えます。しかし実際には、多くの場合ルートCAは中間CA証明書に署名し、日常的なサーバ証明書の発行は中間CAが担当します。
理由は、ルートCAの秘密鍵をできるだけ使わず、厳重に隔離するためです。ルートCAの秘密鍵は信頼の根っこなので、漏えいすると影響が非常に大きくなります。そこでルートCAは中間CAを認めるときだけ使い、サーバ証明書の発行業務は中間CAに任せます。
| 役割 | 主な仕事 |
|---|---|
| ルートCA | 中間CA証明書へ署名する。秘密鍵は厳重に保管し、頻繁には使わない |
| 中間CA | ドメイン所有確認などを経て、Webサイトのサーバ証明書へ署名する |
| サーバ証明書 | ドメイン名とサーバー公開鍵の結び付きをブラウザへ示す |
中間CAを挟むことで、万一中間CAに問題が起きた場合でも、その中間CAを失効・交換することで影響を限定しやすくなります。信頼の根であるルートCAを守るための、現実的な分業です。
公開鍵を差し替える攻撃はどう失敗するか
攻撃者Mが www.example.com の公開鍵をKmに差し替えたとします。MはKmに対応する秘密鍵を持っているので、AがKmで暗号化した内容は読めます。しかし、正規のCAが「www.example.com の公開鍵はKmです」と署名した証明書を用意できません。
Mが自己署名証明書を出した場合、ブラウザは「この証明書は誰が保証しているのか」を確認します。保証者がM自身で、そのルートが信頼ストアに入っていなければ、証明書チェーンは信頼済みルートCAへ到達できません。結果として、ブラウザは警告を出すか接続を拒否します。
攻撃者の偽証明書
対象: www.example.com
公開鍵: Km
署名: 攻撃者M
ブラウザの判断
署名の検証先が信頼済みルートCAへつながらない
対象ドメインと公開鍵の結び付きを信頼できない
接続を安全とは扱わない
現代のTLSでは何に使われるのか
ここまでの説明では理解しやすさのために「Bの公開鍵で文章を暗号化する」と表現しました。現代のHTTPS、とくにTLS 1.3では、サーバ証明書の公開鍵でHTTP本文そのものを暗号化するのが中心ではありません。
実際には、ECDHEなどの鍵交換でこの接続だけの共通鍵を作り、その共通鍵でHTTP通信を暗号化します。サーバ証明書の公開鍵は主に、サーバーが正しい秘密鍵を持っていること、つまり相手が本当に目的のドメインのサーバーであることを確認するために使われます。
自己署名証明書はなぜ警告されるのか
自己署名証明書は、証明書の発行者が自分自身である証明書です。検証用の公開鍵と署名者が同じなので、技術的には署名検証できます。しかし、それは「自分で自分を本物だと言っている」状態です。
開発環境や社内環境で、あらかじめその自己署名ルート証明書を信頼ストアへ登録しているなら使えます。逆に、一般の利用者のブラウザにそのルートが入っていなければ、公開サイトでは信頼されません。攻撃者も同じように自己署名証明書を作れるためです。
まとめ
公開鍵暗号だけでは、「その公開鍵が本当に相手のものか」を保証できません。そこで、CAがドメイン名と公開鍵の結び付きを証明書にし、秘密鍵で署名します。ブラウザはサーバ証明書から中間CA、ルートCAへ署名をたどり、最後に信頼ストアへ到達できるかを確認します。
ルートCAは信頼の起点であり、中間CAは日常的な証明書発行を担う実務上の階層です。この仕組みによって、攻撃者が公開鍵だけを差し替えても、信頼済みCAの署名を作れないため、ブラウザはなりすましを検出できます。
関連して読むなら、サーバ証明書の基本 と TLSハンドシェイクの基本 も参考になります。