
公開鍵は誰のものなのか。自己署名証明書、CSR、公開CA、ルート証明書・中間証明書・証明書チェーンを、bricklayer.workの実例とOpenSSL・C#で追います。

暗号化と認証⑤
誰の紹介なら、その公開鍵を信用できるのか
ルート証明書・中間証明書・証明書チェーンを追う
③と④では、RSAそのものの中へかなり深く潜りました。
小さな数字でRSAを作り、OpenSSLで実際の秘密鍵を覗き、C#の RSAParameters から、
n e d p q DP DQ InverseQ
といった値が、本物のRSA鍵にも入っているところまで確認しました。
でも、RSAの計算がどれだけ正しくても、一つ大きな問題が残っています。
その公開鍵は、本当に通信したい相手のものなのか。
これは②で出てきた「公開鍵を途中ですり替えられたらどうするのか」という問題です。
今回はRSAの計算から少し離れて、その公開鍵をなぜ信用してよいのかを追ってみます。
公開鍵を渡されただけでは信用できない
たとえば、ロミオがジュリエットへ暗号文を送りたいとします。
ジュリエットから、
これが私の公開鍵です
と公開鍵を受け取れば、その鍵で暗号化できます。
しかし途中に第三者が入り、
これがジュリエットの公開鍵です
と言って、自分の公開鍵を渡してきたらどうでしょう。
RSAそのものは正常に動きます。
問題は暗号ではありません。
その公開鍵が誰のものなのか確認できないことです。
そこで登場するのが電子証明書です。
証明書は「公開鍵の紹介状」
かなり乱暴に言えば、証明書は、
この公開鍵は、この相手のものです
という情報を、別の誰かが保証したものです。
Webサーバー用の証明書には、たとえば、
Subject 公開鍵 有効期限 Issuer Subject Alternative Name 署名
といった情報が入っています。
HTTPSなら、
この公開鍵
と、
bricklayer.work
のようなホスト名との対応も重要です。
ところが、ここですぐ次の疑問が出ます。
その証明書に署名した相手は、信用していいのか。
証明書にも「紹介者」がいる
証明書には Issuer があります。
その証明書を発行した認証局、CAです。
典型的には、
ルートCA │ │ 署名 ↓ 中間CA │ │ 署名 ↓ サーバー証明書
という関係になります。
サーバー証明書だけを見て、
この公開鍵は本物だ
と判断しているわけではありません。
その証明書を発行した中間CAを確認し、
では、その中間CAを誰が保証しているのか
と上へたどっていきます。
これが証明書チェーンです。
では、一番上のルートCAは誰が保証するのか
サーバー証明書は中間CAが保証する。
中間CAは、さらに上のCAが保証する。
では、一番上のルートCAは誰が保証するのでしょう。
さらに上のCAを作れば、そのCAを保証するCAがまた必要になります。
これでは永遠に終わりません。
どこかで、
ここは信用する
と決める必要があります。
その信頼の起点がルート証明書です。
自己署名だから信用されないわけではない

ルート証明書を見ると、
Subject = 自分 Issuer = 自分
となっているものがあります。
自己署名です。
ここで、
自己署名証明書だから信用されない
と考えるのは少し違います。
自己署名は、言ってみれば、
「俺だよ! オレオレ!」
と自分で名乗っている状態です。
初対面の人にそう言われても、
「いや、誰だよ」
となる。
だから普通は信用されません。
でも、その「俺」がすでに信頼済みストアに登録されている顔パスメンバーなら話は別です。
自己署名証明書
│
│ 「俺だよ!」
↓
クライアント
信頼済みストアにいる?
│
├─ NO → 誰だよ → 警告
│
└─ YES → あ、この人なら信用する
つまり重要なのは、
自己署名かどうかではなく、その証明書をクライアントが信頼の起点として認めているか
です。
その顔パスメンバーは、OSなどに最初から登録されていてもいい。
あるいは、自分や組織の管理者が後からルート証明書を入れてもいい。
最初から登録されている → 公開CAのルート証明書 自分たちで後から登録する → 自前CA・組織内CAのルート証明書
信頼済みストアから見れば、どちらも信頼の起点として登録された証明書です。
自宅ラボのESXiでよく見るやつ
自己署名証明書というと、自宅ラボのESXiを思い出します。
ESXiのWeb管理画面へアクセスすると、初期状態では証明書警告が出ることがあります。
https://esxi01.example.local/
ESXi自身が作った証明書なら、
ESXi │ └─「俺だよ!」
という状態です。
TLSによる暗号化そのものはできます。
でもブラウザーからすると、
「知らんがな」
となる。
これが、いわゆる「オレオレ証明書」でよく見る状態です。
結局は「誰にそのサイトを見せるのか」

ここまで考えると、公開CAと自前CAをどう使い分けるかは意外に単純です。
誰にそのサイトを見せるのか。
これでかなり決まります。
bricklayer.work は不特定多数に見せる
この bricklayer.work は、私が管理していないPCやスマートフォンからもアクセスされます。
閲覧者全員に、
bricklayer.work用のCA証明書をあなたの端末へ入れてください。
とは言えません。
だから、世の中の多くのOSやブラウザーがすでに信用している公開CAを使います。
私の場合は、OpenSSLで秘密鍵を作ります。
そこからCSRを作り、いわゆる「SSL屋」へ提出しています。
秘密鍵を作成
↓
CSRを作成
↓
SSL屋へ提出
↓
本当にドメインを管理しているか確認
↓
証明書を発行
↓
Apacheへ登録
CSR ― 証明書署名要求

CSRは、
Certificate Signing Request
証明書署名要求
です。
ざっくり言えば、
この公開鍵について、この名前の証明書を発行してください
という要求です。
OpenSSLなら、たとえば、
openssl req -new \
-key server.key \
-out server.csrのように作れます。
ここで大事なのは、秘密鍵をSSL屋へ渡さないことです。
サーバー 秘密鍵 ────── 手元に保持 │ ↓ 公開鍵 │ ↓ CSR ─────────→ CA
CAはCSRの内容をもとに証明書を発行し、CA自身の秘密鍵でその証明書へ署名します。
でもSSL屋は、私をどう信用するのか
ここが証明書の仕組みで面白いところです。
こちらが、
bricklayer.work の証明書をください。
と言っただけで発行してくれたら大変です。
SSL屋としても、
本当にあなたはbricklayer.workを管理できる人なのですか?
を確認しなければなりません。
そのやり方がなかなか面白い。
「本当に管理者ならDNSを書き換えられるよね?」
たとえば、
本当にそのドメインを管理しているなら、DNSを書き換えられますよね。
では、この指定した文字列をDNSへ入れてください。
という方式があります。
CA │ │ 「この値をDNSへ入れて」 ↓ 申請者 │ │ DNSを書き換える ↓ bricklayer.work のDNS │ │ 指定値あり ↓ CA
CAから見れば、
この人は少なくとも、このドメインのDNSを操作できるらしい。
と判断できます。
ここで証明しているのは、その人の身元そのものではありません。
そのドメインを制御できることです。
「本当に管理者なら、このメールを読めるよね?」
別の方法がメールです。
たとえば、
admin@bricklayer.work administrator@bricklayer.work webmaster@bricklayer.work hostmaster@bricklayer.work postmaster@bricklayer.work
のような、そのドメインの管理者なら扱えそうなアドレスへ確認メールを送ります。
そこに届いたメールを使って認証できれば、
この人は、このドメインの管理メールを扱えるらしい。
と判断できます。
現在の bricklayer.work のサーバー証明書は、このメール方式です。
安かったです(笑)。
でも、安い証明書だからTLSの暗号そのものが弱くなるわけではありません。
確認しているのは主に、
この申請者が、このドメインを管理できるか
です。
「そのドメインを管理できる」と「その会社である」は別
一方で、
この人はこのドメインを管理できる
だけでなく、
この申請者は、本当にその会社なのか
まで確認する証明書もあります。
OVやEVと呼ばれるものです。
法人登記など、公的・信頼できる情報源を使って組織を確認します。
自宅ラボで使う証明書を検討していたとき、証明書事業者の料金表を見て、
高いコースになると会社の書類まで要求するんだな。
と思ったことがあります。
つまり、
高い証明書 ↓ 強い暗号
という意味ではありません。
むしろ、
DV → ドメインを管理できることを確認 OV / EV → さらに組織そのものも確認
という、何を確認してから証明書を発行するのかの違いです。
自分の管理下だけなら、自前CAでもいい
今度は自宅ラボです。
自宅ラボのESXiを見るのは、基本的に自分です。
接続するPCも自分で管理できます。
だったら、
このCAを信用する。
と、自分の端末に登録できます。
自宅ラボ用CA
│
├── ESXi
├── テストWeb
└── テストAPI
クライアントPCの信頼済みストアへ自宅ラボ用CAを登録しておけば、
ESXiの証明書
↓
自宅ラボ用CA
↓
自分のPCが信頼
という関係を作れます。
自宅のESXiを見るためだけに、わざわざ外部のSSL屋へお願いする必要はありません。
ESXiからCSRを出して、証明書を戻す
ESXiには、証明書署名要求を作成し、CAで発行してもらった証明書を登録する仕組みがあります。
ESXi
│
│ 鍵ペアを作成
│
├── 秘密鍵 → ESXiに保持
│
└── 公開鍵
↓
CSR
↓
自宅ラボ用CA
↓
証明書を発行
↓
ESXiへ登録
これならESXiの秘密鍵を外へ出す必要もありません。
HTTPSが必要なテストWebサーバーでも同じ
自宅ラボにWebアプリのテスト環境を作る場合にも使えます。
https://test-web.example.local/
本番に近いTLS構成を試したい。
Secure属性付きCookieを試したい。
認証やリダイレクトをHTTPS前提で動かしたい。
そんなとき、
テストWeb
↓ CSR
自宅ラボ用CA
↓ 証明書
テストWeb
とすればいい。
接続する端末は自分のものなので、
このCAを信頼済みストアへ入れる。
ができます。
自前CAでも、暗号化も認証もできる
ここは大事です。
公開CAから証明書を買わないと、まともなHTTPSにはならない。
わけではありません。
自前CAでも、クライアントがそのCAを信頼していて、証明書を適切に発行していれば、
- TLSによる通信の暗号化
- サーバー認証
- 証明書チェーンの検証
- ホスト名の確認
はできます。
必要なら、クライアント証明書を使ったmTLSもできます。
違うのは暗号の強さではありません。
公開CA → 不特定多数の端末がすでに信頼している 自前CA → 自分が管理する端末へ自分で信頼させる
という違いです。
企業の組織内CAでも基本的な考え方は同じでしょう。
管理下の端末へルートCAを配布できるなら、その範囲内で信頼の仕組みを作れます。
結局、
誰にそのサイトを見せるのか。
に戻ってきます。
ただし、CAを持つなら管理責任も自分に来る
自前CAを作れば何でも解決、というわけでもありません。
CAの秘密鍵を持つということは、
「これは本物のサーバーです」と保証できる力を持つ
ということです。
誰でもCAを使える状態なら、
これは正規のサーバーです
という証明書を勝手に発行できてしまいます。
クライアントはそのCAを信用しています。
つまり、CAを運用するなら、
CAの秘密鍵をどう守るか 誰が証明書を発行できるか 何の証明書を発行したか 有効期限をどうするか 必要ならどう失効させるか
を考えなければなりません。
規模が大きければ、
ルートCA │ ↓ 発行用の中間CA │ ├── Web ├── ESXi └── API
と分ける方法もあります。
公開CAの世界で中間CAが使われるのも、同じように信頼の根っこであるルート鍵を日常的に使わないためです。
自宅ラボで中間証明書を入れ忘れて、エラーになった

証明書チェーンは、図の上だけの話ではありません。
以前、自宅ラボでApacheへサーバー証明書を設定したのに、ブラウザーで証明書エラーになったことがあります。
記憶では、
NET::ERR_CERT_AUTHORITY_INVALID
だったと思います。ただし、この表示だけで原因を中間証明書不足と断定できるわけではありません。
証明書そのものはSSL屋から正規に発行してもらったものです。
なので、原因には比較的すぐ気付きました。
中間証明書が足りないのでは?
サーバー証明書だけを返していると、
サーバー証明書
↓
???
↓
信頼済みルートCA
となります。
ブラウザーから見ると、信頼しているルートまでたどれません。
そこで、サーバー証明書と中間証明書を一つのPEMファイルにまとめました。
-----BEGIN CERTIFICATE----- サーバー証明書 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 中間証明書 -----END CERTIFICATE-----
それをApacheへ登録すると、エラーは解消しました。
サーバー証明書
↓
中間証明書
↓
ルートCA ← クライアントが持っている
今から考えると、
単純に証明書チェーンが途中で切れていた
わけです。
「中間証明書とは何か」と文章で読むより、このエラーを一度踏んだ方がよく分かります。
ルート証明書までサーバーから送る必要はない
通常、Webサーバーが送るのは、
サーバー証明書 中間証明書
です。
ルート証明書はクライアント側がすでに持っています。
Webサーバー
サーバー証明書
中間証明書
│
↓
----------------
↓
クライアント
信頼済みルート証明書
重要なのは、
クライアントが元々信用しているルートまで、チェーンがつながること
です。
証明書の有効期限をもっと長く作れないの?
証明書には有効期限があり、使い続けるには更新が必要です。
更新の手間を考えると、「有効期間を長くすれば、更新回数を減らせるのでは?」という疑問が浮かびます。
技術的には、自前のCAで長い有効期間を設定して、証明書を発行することはできます。
ただし、その証明書をブラウザーやOSが受け入れるかは別です。発行元が信頼されていることに加えて、有効期間や用途なども、クライアントの検証ルールを満たす必要があります。
ここも証明書らしいところです。
証明書を作れた
≠
クライアントが受け入れる
有効期間を設定できることと、その期間で利用できることは、同じではありません。
公開Web用の証明書には公開Web PKIのルールがあり、自前のCAを使う場合にも、利用するOSやブラウザーの検証ルールを確認する必要があります。
証明書の寿命は、むしろどんどん短くなっている
公開Webで使うTLS証明書については、CA/Browser ForumのBaseline Requirementsで最大有効期間が決められています。
2026年10月現在、新たに発行する公開Web用のTLSサーバー証明書は、最大200日です。
さらに、
2026/03/15~ 最大200日 2027/03/15~ 最大100日 2029/03/15~ 最大47日
と短縮されていくことが決まっています。CA/Browser Forum
長くするどころか、逆方向です。
秘密鍵が漏れた。
誤った証明書が発行された。
管理状態が変わった。
そんなとき、何年も有効な証明書が残るより、短期間で更新される方が影響を限定できます。
とはいえ47日ともなると、
人間が思い出して CSRを作って 証明書を入れ替える
という運用は、かなりつらい。
証明書更新の自動化が重要になるのも納得できます。
「作れたのにSafariでは駄目」があり得る
証明書でややこしいのは、発行側だけでルールが完結しないところです。
私も自宅ラボで、有効期間を長くした証明書が、EdgeやFirefoxでは通るのに、Safariではエラーになったことがあります。
当時、「Appleは有効期間の長い証明書に厳しい」という情報を見掛けました。そこで、398日という制限を目安に、有効期間を380日程度にして再発行しました。「年に一回更新すればよいか」と考えたわけです。すると、Safariでもエラーが出なくなりました。
ただ、今になってAppleの要件を読み直すと、二つの制限を区別する必要がありました。
Appleが2020年に導入した398日制限は、Apple製品にあらかじめ入っているルートCAに基づくTLSサーバー証明書が対象です。ユーザーや管理者が後から追加したルートCAに基づく証明書には適用されません。私が使っていた自前CAも、こちらの制限の対象ではありませんでした。Appleサポート:398日制限
一方、AppleがiOS 13・macOS 10.15向けに公表した要件には、2019年7月以降に発行されたTLSサーバー証明書の有効期間を825日以下にするという条件があります。こちらは自前CAの証明書にも関係します。例えば1000日で作った証明書は、この上限を超えます。Appleサポート:信頼済み証明書の要件
380日程度へ短縮すれば、825日以下という条件も満たします。そのため、私のケースもこちらの要件に引っかかっていた可能性があります。ただし、当時の証明書やエラーの詳細を確認できないため、原因だったとは断定できません。
当時は、二つの制限を区別して対処したわけではありません。長い有効期間を避けて再発行すると、動くようになった。その経験の理由を、今になって整理しているところです。
自前CAなら、発行する証明書の有効期間を自分で決められます。でも、その証明書を受け入れるOSやブラウザーのルールまで、自分で決められるわけではありません。
つまり、証明書では、
「作れるか」と「使えるか」は別です。
という区別が必要です。
OpenSSLで実際の証明書チェーンを見る
せっかくなので、実際に bricklayer.work が返している証明書をOpenSSLから見てみます。
openssl s_client \
-connect bricklayer.work:443 \
-servername bricklayer.work \
-showcerts-showcerts を付ければ、サーバーから送られてくる証明書を確認できます。
証明書をファイルへ保存すれば、
openssl x509 \
-in server.pem \
-noout \
-subject \
-issuer \
-datesとして、
subject= issuer= notBefore= notAfter=
を見ることもできます。
ここまで来ると、
Subject Issuer
という項目も単なる文字列ではなく、
これは誰の証明書なのか 誰がそれを保証したのか
という意味に見えてきます。
C#からも証明書チェーンを見る
もちろん、ここでもC#から確認できます。
.NETには X509Chain があります。
using System.Security.Cryptography.X509Certificates;
using var cert =
new X509Certificate2("server.cer");
using var chain =
new X509Chain();
bool result = chain.Build(cert);
Console.WriteLine($"Valid: {result}");
foreach (var element in chain.ChainElements)
{
Console.WriteLine(element.Certificate.Subject);
Console.WriteLine(element.Certificate.Issuer);
Console.WriteLine();
}これで.NETが構築した証明書チェーンを見ることができます。
イメージとしては、
bricklayer.work
↓
中間CA
↓
ルートCA
です。
今回も結局、
普段ライブラリに任せているものを、一度中身まで見てみる
ということになります。
数学だけでは「信用」は始まらない
RSAやECDSAによる署名検証は数学です。
この署名は正しいか
↓
数学で確認
でも、
このルートCAを信用する
という最初の判断は、数式からは出てきません。
どこかで、
このCAは信頼する。
と決めています。
公開サイトなら、OSやブラウザーなどが持つ信頼済みストア。
自宅ラボなら、自分で登録したCA。
企業なら、管理者が管理端末へ配布した組織内CAという構成もあり得ます。
そしてCA側も、証明書を発行する前に、
DNSを書き換えられるか 管理用メールを扱えるか 組織が本当に存在するか
などを確認しています。
証明書というのは、数学だけで完結するものではありません。
暗号 + 署名 + 技術 + 運用 + 人間が決めた信頼関係
で成り立っています。
まとめ
②で残った問題は、
その公開鍵は、本当に通信したい相手のものなのか。
でした。
その答えは、
証明書があるから
だけではありません。
公開鍵 ↓ サーバー証明書 ↓ 中間CA ↓ ルートCA ↓ クライアントがそのルートを信用している
という信頼の経路があります。
そして証明書を発行する側でも、
申請者 ↓ DNS・メール・組織情報などで確認 ↓ CA ↓ 証明書を発行
という確認をしています。
公開CAを使うか、自前CAを使うかを考える基準も、突き詰めれば、
誰にそのサイトを見せるのか。
です。
bricklayer.work のように不特定多数へ公開するなら、世の中の端末がすでに信用している公開CAを使う。
自宅ラボのESXiやテストWebサーバーなら、自分の端末へ自前CAを信頼させることもできます。
どちらでも、正しく構成すればTLSによる暗号化と認証はできます。
ただし、自分でCAを持つなら、今度は自分が、
誰を本物と認めるのか
を管理する側になります。
そして、中間証明書を一つ入れ忘れるだけでも、
NET::ERR_CERT_AUTHORITY_INVALID
とブラウザーに怒られる。
証明書の有効期限を好きに長くしても、クライアント側が受け入れてくれるとは限らない。
そして自己署名証明書も、
自己署名だから信用されないわけではない。
「俺だよ! オレオレ!」と言っている相手が、信頼済みストアに登録された顔パスメンバーなのかどうか。
結局そこです。
自宅ラボで証明書を取ってApacheへ入れたり、中間証明書不足でエラーになったり、自己署名証明書を見たりしていると、電子証明書は単なる「暗号のファイル」ではなく、
信頼をどうつなぎ、誰がその信頼を認めるのかを仕組みにしたもの
だと分かります。
次回
次は、この証明書が実際のHTTPS通信の中でいつ送られ、どう検証され、その後どうやって共通鍵による通信へ移っていくのか。
TLS通信を最初から最後まで追ってみます。