本記事は生成AIと共同で執筆しています。事実関係は可能な範囲で公式ドキュメント等と照合していますが、誤りが含まれている可能性があります。重要な判断を行う前にご自身でも一次情報をご確認ください。
前の記事公開鍵と署名の仕組みを、実際の数字で追うで、署名を検証して言えることは 1 つだけだと書きました。
このデータは、この公開鍵と対になる秘密鍵の持ち主が作った。そして作られてから変わっていない。
そして言えないことの 1 つ目として、「その鍵の持ち主が誰なのか」を挙げました。本記事はその続きで、TLS(Transport Layer Security、通信を保護する規格)がその穴をどう埋めているかを見ます。
先に結論を書くと、証明書の鎖の骨格に新しい概念は出てきません。前の記事でやった「署名して、公開鍵で検証する」を繰り返しているだけです。よくある構成なら 3 回になります(回数は構成によって変わります。詳しくは後述)。
前提: ここからは「人」ではなく「ホスト名」の話です
先に断っておきます。前の記事では、署名する主体をアリスという人に見立てて説明しました。本記事で扱う TLS のサーバ証明書が結びつけるのは、人ではなくホスト名と公開鍵です。
前の記事 アリス(人) ↔ 公開鍵
本記事 www.example.com ↔ 公開鍵
仕組みは同じで、結びつける相手が変わるだけです。個人と鍵を結びつける仕組みも別にあり、S/MIME 証明書や Verifiable Credentials、あるいはクライアント証明書として使われる個人向けの証明書がそれにあたります(クライアント証明書の中身は人とは限らないので、次の記事で改めて扱います)。
紛らわしいことに、この後で例に出す SSH(Secure Shell)にも 2 種類あります。
| SSH の鍵 | 何を識別するか | 保存先 |
|---|---|---|
| ホスト鍵 | サーバ | known_hosts |
| ユーザ鍵 | 個人 | authorized_keys |
本記事で扱うのは前者、つまりサーバのほうです。
手渡しできるなら、証明書は要らない
アリスがボブに会って公開鍵を手渡ししたなら、それで十分です。目の前にいるのがアリスだと分かるので、その鍵はアリスのものです。証明書も認証局(CA、Certificate Authority)も要りません。以後、本記事では短い CA のほうを使います。
この方式は実際に広く使われています。SSH(Secure Shell)が初回接続で出す、次の確認がそれです。
The authenticity of host '...' can't be established.
Are you sure you want to continue connecting (yes/no)?
「手渡しの代わりに、あなたが目視で確認してください」と言っています。yes と答えると ~/.ssh/known_hosts に保存され、以後は覚えられます。アプリに公開鍵を埋め込む方法も同じ考え方です。
手渡しできないと、途中で差し替えられる
会ったことのない相手とネットワーク越しに通信する場合、「これはアリスの公開鍵です」というメッセージが届いても、送ってきたのが本当にアリスかは分かりません。署名で確かめようにも、その署名を検証するには、まさにいま受け取ろうとしている公開鍵が要ります。
差し替えられるとどうなるか、短いコードで確かめられます。
import { generateKeyPair, SignJWT, jwtVerify } from 'jose';
const alice = await generateKeyPair('ES256');
const mallory = await generateKeyPair('ES256');
// マロリーが受け渡しを横取りし、自分の公開鍵を「アリスのです」と渡した
const bobThinksThisIsAlice = mallory.publicKey;
const realDoc = await new SignJWT({ 差出人: 'アリス(本物)' })
.setProtectedHeader({ alg: 'ES256' }).sign(alice.privateKey);
const fakeDoc = await new SignJWT({ 差出人: 'マロリー' })
.setProtectedHeader({ alg: 'ES256' }).sign(mallory.privateKey);
for (const [name, doc] of [['本物のアリス', realDoc], ['マロリー', fakeDoc]]) {
try { await jwtVerify(doc, bobThinksThisIsAlice); console.log(`${name} → 受理`); }
catch { console.log(`${name} → 拒否`); }
}
本物のアリス → 拒否
マロリー → 受理
立場が入れ替わります。暗号は破られておらず、最初の受け渡しを間違えただけで、以後ボブはマロリーをアリスだと信じ続けます。
証明書は「他人の公開鍵が書いてある手紙」
そこで、既に手渡し済みの誰かに保証してもらう方法が使われます。
この保証をする組織を CA(Certificate Authority、認証局)と呼びます。
このときに使われるのが証明書です。前の記事でアリスが署名したのは {"name":"Alice"} という主張でした。証明書で署名するのは、次の主張です。
「この名前は、この公開鍵の持ち主です」
署名の仕組みはまったく同じで、中身が変わっただけです。証明書は特別なものではなく、本文に他人の公開鍵が書いてある、署名済みの手紙です。
検証は 3 回。出力はどれも「はい/いいえ」
ここが混乱しやすいところなので、はっきりさせておきます。検証の出力は、前の記事と同じで「署名が正しいか」の判定だけです。公開鍵が出てくるわけではありません。
公開鍵は、手紙の本文にもともと書いてあったものです。検証が通ったので、それを信じてよくなる、という順序です。
バケツリレーのように、手紙の中に次の鍵が入っています。
最初から持っている ルート CA の公開鍵
↓ これで証明書1 を検証 → はい
↓ 本文に書いてあった中間 CA の公開鍵を、信じてよくなる
↓ その鍵で証明書2 を検証 → はい
↓ 本文に書いてあったサーバの公開鍵を、信じてよくなる
↓ その鍵で 3 回目の検証(いま進行中のやり取りへの署名)→ はい
ブラウザは 3 回とも「確かめる側」で、相手が 3 回変わります。
ただし、ブラウザが確かめているのは署名だけではありません。各証明書について有効期間が切れていないか、発行者の名前が上の段の持ち主と一致しているか、中間 CA の証明書に「これは CA である」という印(basicConstraints の CA フラグ)が立っているか、そして一番下の証明書に書かれたホスト名が接続先と一致するかも確かめます(RFC 5280 §6.1、RFC 9525)。とくに CA フラグの確認が抜けると、ふつうのサーバ証明書の持ち主が他人の証明書に署名できてしまいます。本記事は署名の鎖だけを取り出して説明しています。
3 回目だけは、手紙ではなく「いま進行中の通信」に署名しています
1 回目と 2 回目の本文は公開鍵でした。3 回目は違います。
前の記事で「確かめる側が先に使い捨ての数字を送り、それに署名させる」という形を扱いました。TLS でも似た発想の値(ClientHello の random)が使われますが、TLS 1.3 でサーバが署名するのは、その数字そのものではありません。RFC 8446 §4.4.3 によれば、署名の対象は Transcript-Hash(Handshake Context, Certificate)、つまりそこまでのやり取りの記録全体のハッシュです。
さらに同じ節では、署名対象の先頭に 0x20 を 64 個並べると定めています。これは以前のバージョンの TLS にあった「相手が指定した値にそのまま署名してしまう」性質を、意図的に壊すための構造です。
つまり TLS 1.3 は「渡された数字に署名する」形をあえて避けています。狙い(使い回しを防ぐ)は同じでも、形は違います。
実際の証明書を見る
東京大学の 2 つのサイトを取得しました(2026-08-13 時点)。
echo | openssl s_client -connect www.lib.u-tokyo.ac.jp:443 \
-servername www.lib.u-tokyo.ac.jp -showcerts 2>/dev/null
(macOS に同梱されている openssl は LibreSSL で、この後の記事で使うホスト名検証の指定などが使えません。Homebrew の OpenSSL 3 を入れると揃います。)
www.lib.u-tokyo.ac.jp の鎖です。
[0] 持ち主: CN=www.lib.u-tokyo.ac.jp
署名者: C=US, O=Let's Encrypt, CN=YR1
期間 : 2026-07-24 → 2026-10-22 約 3 か月
[1] 持ち主: C=US, O=Let's Encrypt, CN=YR1
署名者: C=US, O=ISRG, CN=Root YR
期間 : 2025-09-03 → 2028-09-02 約 3 年
[2] 持ち主: C=US, O=ISRG, CN=Root YR
署名者: C=US, O=Internet Security Research Group, CN=ISRG Root X1
www.u-tokyo.ac.jp は別の認証局でした。
[0] 持ち主: C=JP, ST=Tokyo, L=Bunkyo-ku, O=The University of Tokyo, CN=www.u-tokyo.ac.jp
署名者: C=JP, O=SECOM Trust Systems Co., Ltd., CN=NII Open Domain CA - G8 RSA
期間 : 2026-05-22 → 2026-12-06 約 6 か月
[1] 持ち主: C=JP, O=SECOM Trust Systems Co., Ltd., CN=NII Open Domain CA - G8 RSA
署名者: C=JP, O=SECOM Trust Systems Co., Ltd., CN=SECOM TLS RSA Root CA 2024
期間 : 2025-08-21 → 2040-08-21 15 年
[2] 持ち主: C=JP, O=SECOM Trust Systems Co., Ltd., CN=SECOM TLS RSA Root CA 2024
署名者: C=JP, O=SECOM Trust Systems CO.,LTD., OU=Security Communication RootCA2
同じ大学の中でも、サイトによって認証局が違います。
証明書に書かれている項目
subject= の中身は、次のように分かれています。
subject=C=JP, ST=Tokyo, L=Bunkyo-ku, O=The University of Tokyo, CN=www.u-tokyo.ac.jp
│ │ │ │ └ CN = Common Name(ホスト名)
│ │ │ └ O = Organization(組織)
│ │ └ L = Locality(市区町村)
│ └ ST = State(都道府県)
└ C = Country(国)
O= は「この証明書の持ち主」を表します。どの段の証明書かによって、指すものが変わります。
| 段 | O= が指すもの |
|---|---|
| サーバ証明書 | サイトを運営する組織 |
| 中間 CA 証明書 | CA 事業者 |
| ルート CA 証明書 | CA 事業者 |
O= の有無が、確認の仕方の違いを表している
2 つを並べると、違いが見えます。
www.lib.u-tokyo.ac.jp subject=CN=www.lib.u-tokyo.ac.jp ← O= が無い
www.u-tokyo.ac.jp subject=C=JP, ..., O=The University of Tokyo, ... ← O= がある
証明書には種類があります。
| 種類 | 確認する範囲 | O= |
|---|---|---|
| DV(Domain Validation) | ドメインを支配しているかだけ | 入らない |
| OV(Organization Validation) | 組織が実在するかも確認 | 入る |
| EV(Extended Validation) | さらに厳格に確認 | 入る |
3 つの違いは 証明書に書ける身元の範囲であって、通信そのものの保護の強さの違いではありません。鍵長や署名アルゴリズムの要件は CA/Browser Forum の Baseline Requirements が 3 種類に共通で課しており(6.1.5 Key sizes)、EV も EV Guidelines 7.1.4.3 で「暗号アルゴリズムと鍵長は Baseline Requirements の規定がそのまま適用される」とされています。種類によって違うのは持ち主の欄・その確認方法・方針の識別子の 3 点だけで、ドメイン名についての保証の水準はどの種類でも同じです。
www.lib.u-tokyo.ac.jp は DV です。Let's Encrypt は機械的な確認だけで自動発行するため、組織名を書けません。確認していないことを書いてはいけない、という理屈です。
www.u-tokyo.ac.jp は OV です。人が確認する工程があるからこそ O=The University of Tokyo と書けます。
つまり 「人が確認したかどうか」が、この欄に現れています。
確認する人と、署名する人は別
www.u-tokyo.ac.jp の署名者をもう一度見ます。
CN=NII Open Domain CA - G8 RSA
O=SECOM Trust Systems Co., Ltd.
名前には NII(国立情報学研究所)と入っていますが、O= はセコムトラストシステムズです。これは NII が学術機関向けに提供している UPKI 電子証明書発行サービスによるもので、規程のページには「セコム電子認証基盤認証運用規程」が挙げられ、認証局のリポジトリもセコムのドメインに置かれています。
そして、申請者が本当にその大学の部局かどうかは、認証局だけでは判断できません。学内の事情は学内でしか分からないためです。実際、このサービスでは確認の役が分かれています。公開されている記述を並べると、次のようになります。
| どこ | 公開情報にある記述 |
|---|---|
| 大学側 | 機関責任者から任命された「登録担当者」が、機関内での申請の審査を担当する(NII の参加案内)。東京大学の場合、情報基盤センターが「東京大学での受け口」をつとめると書かれています(情報基盤センター PKI) |
| NII | 「サービス窓口」が審査業務等を実施する事務窓口とされています(用語について) |
| セコム | 認証局としての業務 |
一般に、申請者の確認を担う役は RA(Registration Authority、登録局) と呼ばれます。ただし、このサービスの「登録担当者」が規程上 RA として定義されているかまでは、調査した限りでは確認できませんでした。ここでは公開情報にある呼び方に留めます。
いずれにせよ、確認する人と署名する人が別であること、そして確認が学内と NII の 2 段階になっていることは読み取れます。
DV の場合、この確認は機械が担います。ネームサーバやウェブサーバを支配していること自体が、ドメインを支配している証拠になるため、人手の確認が要りません。
なぜルートと中間を分けるのか
ルート CA の秘密鍵が漏れると、回収できません。その公開鍵は世界中のブラウザや OS に焼き込まれているため、差し替えるには本体の更新が要り、行き渡るまでに年単位の時間がかかります。
そこでルートの秘密鍵はオフラインで保管し、普段は取り出しません。中間 CA の証明書に署名するときだけ使います。日々の発行作業は中間 CA が担当し、こちらの鍵が漏れた場合は、その中間 CA 証明書を失効させることで対処できます。ただし、その中間 CA が発行した証明書はすべて検証を通らなくなるため、サイト側は証明書の再発行が要ります。それでも、世界中の端末に焼き込まれたルートを差し替えずに済む点が決定的な違いです。
先ほどの実データにも、その差が出ています。
| 有効期間(上の実データより) | |
|---|---|
| サーバ証明書 | 3 か月(www.lib)/ 6 か月(www.u-tokyo) |
| 中間 CA 証明書 | 3 年(Let's Encrypt)/ 15 年(NII Open Domain CA) |
ルート証明書そのものの有効期間は、上のダンプには現れていません。鎖の 3 枚目として送られてきているのは自己署名のルートではなく、別のルートに署名してもらったもの(クロス署名)だからです。
実印を金庫にしまい、日常の書類は別の印で回す、という運用に近い構造です。
ルート CA はブラウザごとに違うのか
一覧そのものはブラウザによって別々です。ただし「Chrome だから Google、Firefox だから Mozilla」ということはありません。そもそも Mozilla は認証局ではないので、ルート証明書を持っていません。
ルート CA の一覧(トラストストア、信頼の起点となる証明書の集まり)は、macOS では次のように確認できます。
security find-certificate -a /System/Library/Keychains/SystemRootCertificates.keychain | grep -c labl
手元では 158 件でした(macOS 26.5.2、2026-08-13 時点)。この件数は OS のバージョンや更新で変わります。中身を見ると、Google Trust Services、GlobalSign、DigiCert、セコム、ISRG(Let's Encrypt の運営団体)などが同居しています。
ブラウザごとに違うのは、どの一覧を見るかです。
| ブラウザ | 見る一覧 |
|---|---|
| Firefox | Mozilla 独自の一覧 |
| Chrome | 以前は OS の一覧。現在は Chrome 独自の一覧 |
| Safari / Edge | OS(Apple / Microsoft)の一覧 |
ただし中身は大きく重なります。どの一覧も、CA/Browser Forum という業界団体の基準に沿った監査を通った認証局を載せるためです。
サイトがどの証明書を送るかで、たどれる経路はある程度決まります。ただし、最終的にどのルートに行き着くかを決めるのはブラウザ側です。ブラウザが確かめているのは「送られてきた鎖の一番上が一覧に載っているか」ではなく、鎖をたどった先が、自分の一覧にあるルートに行き着くかです。
信頼の起点が消えたわけではありません。「誰の鍵を手渡しで受け取るか」が、「どの一覧を信じるか、その一覧を誰が決めるか」に移っただけです。上の表の Mozilla や Apple、Microsoft、Google が、それぞれの方針で載せる認証局を選んでいます。たとえば Mozilla のルートストア方針は、収録を一般の利用者にとってのリスクに基づいて判断すること、収録しない権利を留保することを明記しています。
上の 2 例が、そのまま違いを見せてくれます。手元の一覧(macOS、158 件)を調べると、こうなっていました。
SECOM TLS RSA Root CA 2024 あり
Root YR なし
ISRG Root X1 あり
www.u-tokyo.ac.jp は 3 枚目の SECOM TLS RSA Root CA 2024 が一覧にあるので、そこで止まれます。一方 www.lib.u-tokyo.ac.jp の 3 枚目 Root YR は一覧に無いため、その証明書に署名している ISRG Root X1 までたどることになります。同じ 3 枚を受け取っても、行き着く先が違うわけです。
なお、ルートの証明書は鎖に含まれないこともあります。RFC 8446 §4.4.2 は、トラストアンカーにあたる証明書を鎖から省いてよいと定めています。一覧に載っているルートの公開鍵はブラウザが最初から持っているので、送る必要がないためです。
認証局は、どこまで関与しているのか
最後に、時間の流れを整理しておきます。
2025-08-21 ルート CA が中間 CA の証明書に署名 ← 1 年前
2026-05-22 中間 CA が www.u-tokyo.ac.jp に署名 ← 数か月前
いま ブラウザ ⇄ サーバ だけが通信する
3 回のうち、いまこの瞬間に作られた署名を検証するのは 3 回目だけです。ただし材料が届く経路は 3 回とも同じです。サーバは ClientHello への応答として、証明書(1・2 回目の材料)と自身の署名(3 回目の材料)を一度にまとめて送ってきます(RFC 8446 §2 の Figure 1。署名が証明書の直後に置かれることは §4.4.3 が定めています)。3 回目のために追加の往復が起きるわけではありません。1 回目と 2 回目の署名の計算は、そうして受け取った証明書をブラウザが手元で行います。
(厳密には、サーバが中間 CA の証明書を送ってこない場合、ブラウザが証明書に書かれた取得先から補いに行くことがあります。上の実データにも CA Issuers - URI:... としてその取得先が入っていました。)
したがって、署名の検証そのものには認証局は関与していません。
ただし「認証局はあなたの閲覧を一切知らない」と言い切れるかは、次の失効確認をどうするかで変わります。
失効確認をすると、話が変わる
証明書には、失効していないかを問い合わせる先が書かれていることがあります。www.u-tokyo.ac.jp の証明書を見ると、こうなっていました。
openssl x509 -in cert.pem -noout -ext authorityInfoAccess
Authority Information Access:
CA Issuers - URI:http://repo1.secomtrust.net/sppca/nii/odca4/nii-odca4g8rsa.cer
OCSP - URI:http://niig8rsa.ocsp.secom-cert.jp
OCSP の行が、失効していないかを尋ねる先です。OCSP(Online Certificate Status Protocol)で問い合わせる構成のクライアントであれば、その証明書のシリアル番号を含む要求が、認証局側のサーバに届きます。つまり「どのサイトの証明書について尋ねたか」は、そこで知られ得ます。
これを避けるための仕組みもあります。OCSP Stapling は、サーバがあらかじめ失効情報を取得してブラウザに同梱する方式で、ブラウザから認証局への通信が発生しません。また主要なブラウザは、証明書ごとに問い合わせるのではなく、失効情報をまとめて配布する独自の仕組みを持っています。
したがって、認証局が閲覧を知り得るかどうかは PKI の構造で決まっているのではなく、失効確認をどう実装するかで決まります。本記事で確かめた「1 回目と 2 回目は手元の計算だけ」という点は、そのまま成り立ちます。
触れていないもの
- 失効の確認の詳細。CRL(Certificate Revocation List)や OCSP、OCSP Stapling の具体的な手順と、ブラウザごとの既定の違いは本記事では扱っていません。ただし「認証局が閲覧を知り得るか」はこの選択で決まるため、前節で最小限だけ触れました
- クロス署名。上の実データでは、ルート CA の証明書がさらに別のルートに署名されている箇所があります。新しいルートを、まだ一覧に載っていない古い端末でも使えるようにするための措置です
- 証明書の透明性(Certificate Transparency)。誤発行を検出するための公開ログの仕組みがあります
- TLS の暗号化そのもの。本記事で扱ったのは「誰と話しているか」を決める部分だけです
まとめ
- 証明書は「この名前は、この公開鍵の持ち主です」に署名した手紙で、署名の仕組みは前の記事と同じ
- 鎖は、その検証を繰り返しているだけ。出力はどれも「はい/いいえ」で、公開鍵は手紙の本文にもともと書いてある。回数は構成によって変わり、よくある構成で 3 回
- 手渡しできるなら証明書は要らない。CA は、手渡しの回数を減らすための仕組み
- 信頼の起点は消えておらず、ルート CA の一覧を誰が決めるかに移動している
O=の有無が、人が確認したかどうかを表している- 確認する人と署名する人は別。大学の例では、確認が学内と NII の 2 段階に分かれている
- ルートと中間を分けるのは、ルートの秘密鍵を使わずに済ませるため
- 署名の検証そのものに認証局は関与しない。ただし失効確認をどう実装するかによっては、認証局が利用を知り得る
コメント
…