「HTTPS なら安全」とよく言われますが、なぜ安全なのでしょうか。 証明書はどうやって本物だと確認され、ブラウザはどうやって“目の前のサーバーが本当に正当な相手か”を判断しているのでしょうか。
この記事では、Let’s Encrypt の証明書を例に、TLS 1.3 のハンドシェイクで行われる二種類の署名検証を中心に、HTTPS の安全性を支える仕組みを分かりやすく解説します。
TLS とは何か?
TLS 1.3 は、Web をはじめとしたインターネット通信の暗号化を支える重要なプロトコルです。
● TLS の役割
* 通信の暗号化(暗号化)
* 改ざんの検出(完全性)
* 相手が正しいかの確認(認証)← この記事で扱う内容。
● TLS と HTTPS の関係
HTTPS = HTTP + TLS
私は専門家ではありませんので、AIに聞いた書いてます。解釈に誤りがある可能性もありますが、その点はあらかじめご了承ください。
この記事ではTLS1.3のサーバー証明書に関する部分しか扱いませんが、TLS1.3全体の解説はこちらをご覧ください。↓↓↓

サーバーでHTTPS通信を行うために必要なもの
サーバーは次のものを用意しておかなければならない。
- 公開鍵と秘密鍵のペア ← サーバーで自分で作る。
- ドメインと公開鍵を結びつける証明書 ← 認証局に証明書を作ってもらう。
認証局にLet’s Encryptというところがあり、無料で利用できる。
Let’s Encryptは、申請者がそのドメインを本当に管理していることを確認し、提出された公開鍵に署名して証明書を発行する。秘密鍵はサーバーに保持され、認証局(CA)はそれを確認することはないが、証明書を正しく使うためには必ず公開鍵とペアの秘密鍵が存在している必要がある。
—
おすすめ!https://www.youtube.com/watch?v=nH70Ph3RxZg
特に、「CA(認証局)の署名」は必須の知識です。Youtubeでの署名・検証はRSA方式の場合ですが、本記事では署名・検証にECDSAを使ってます。
サーバーとLet’s Encryptのやり取り
🔐 鍵ペアの生成
– サーバー上で 公開鍵と秘密鍵のペア を生成します。
– 秘密鍵はサーバーに保存され、外部に送られることはありません。
– 公開鍵は証明書署名要求(CSR: Certificate Signing Request)に含められ、認証局(Let’s Encrypt)に送られます。
🏢 認証局(Let’s Encrypt)の役割
– Let’s Encryptは、送られてきたCSRの公開鍵とドメイン情報を確認します。
– ドメイン所有者であることを「HTTP-01」や「DNS-01」など複数あるチャレンジ手法のうちの一つで検証します。
– 検証が成功すると、公開鍵を含む 証明書 に署名して返します。
🔑 HTTP-01 チャレンジの仕組み
Let’s Encrypt が「トークン」を発行します。
ACME クライアント(サーバー上で動作するアプリで認証局とやりとりする。例: Certbot)がそのトークンを含むファイルを、サーバー上の特定の場所に設置します。
URL: http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>
Let’s Encrypt がその URL にアクセスし、正しいレスポンスが返ってくれば「このサーバーは確かにそのドメインを管理している」と確認できます
ACMEは「証明書の自動発行・更新を行うための標準プロトコル」。
Let’s Encrypt では ACME クライアントというアプリを利用でき、その代表的なものが Certbot です。
📂 サーバーに設置されるもの
– `/etc/letsencrypt/live/ドメイン名/` に以下が保存されます:
– `privkey.pem` → サーバー秘密鍵(公開鍵とペアのもの、サーバーの外には出さない。)
– `cert.pem` → サーバー証明書(Let’s Encryptが署名済み)
– `chain.pem` → 中間証明書
– `fullchain.pem` → サーバー証明書+中間証明書の結合ファイル
クライアントとの通信で渡されるのは`fullchain.pem`。
`privkey.pem`は、新しくサイトを見に来たクライアントに対し1度だけ使われる。(後述)
証明書の署名を検証するためには、上位の認証局の公開鍵が必要です。その公開鍵は中間証明書に含まれています。今回のケースでは、中間証明書は Let’s Encrypt を証明するものであり、この中間証明書を検証するための公開鍵は、ブラウザにあらかじめ組み込まれているルート証明書に含まれています。(後述)
[サーバー証明書] ←署名検証— [中間証明書] ←署名検証— [ルート証明書(ブラウザ内)]
サーバー証明書(Let’s Encryptが署名済み) cert.pem を見てみる。
私のサーバー(kikuichige.com)に設置されているcert.pemを見てましょう。
証明書(cert.pem)は、ブラウザに送られるので、後述する方法でも中身が確認できます。
(下記のcert.pemは2026/2/5時点のものです。3か月弱毎に更新しているので、次の更新以降、変わります。なので実際にブラウザで確認して違う値の場合は、更新されてます。)
cat cert.pem で表示される証明書は「暗号化されている」ように見えますが、実際には 暗号化された秘密情報ではなく、Base64エンコードされた公開情報です。
証明書の内容を「人間が読める形」にすることは可能です
場所:cd /etc/letsencrypt/live/kikuichige.com
表示コマンド:openssl x509 -in cert.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
05:9c:58:cc:cb:ae:d2:a9:5c:3e:20:b2:80:9e:98:f9:e5:71
Signature Algorithm: ecdsa-with-SHA384
Issuer: C = US, O = Let's Encrypt, CN = E7
Validity
Not Before: Jan 17 05:34:07 2026 GMT
Not After : Apr 17 05:34:06 2026 GMT
Subject: CN = kikuichige.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:fa:91:f4:31:4e:6b:21:d7:ab:a4:0d:e9:16:98:
7e:28:be:b2:0c:53:f2:a1:9c:c6:09:72:14:22:d7:
cb:67:37:18:17:63:f5:64:41:53:bc:6f:a6:03:6f:
e2:06:bf:7c:f0:04:e6:b1:98:66:9a:0b:3c:d1:74:
e8:d2:15:88:a5
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
4D:47:CA:67:F0:6B:F0:98:E9:14:F5:3C:AC:D3:1D:85:28:E5:B6:98
X509v3 Authority Key Identifier:
AE:48:9E:DC:87:1D:44:A0:6F:DA:A2:E5:60:74:04:78:C2:9C:00:80
Authority Information Access:
CA Issuers - URI:http://e7.i.lencr.org/
X509v3 Subject Alternative Name:
DNS:kikuichige.com
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
X509v3 CRL Distribution Points:
Full Name:
URI:http://e7.c.lencr.org/114.crl
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : CB:38:F7:15:89:7C:84:A1:44:5F:5B:C1:DD:FB:C9:6E:
F2:9A:59:CD:47:0A:69:05:85:B0:CB:14:C3:14:58:E7
Timestamp : Jan 17 06:32:37.876 2026 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:21:00:A8:95:29:35:6B:4B:D3:02:1A:70:B4:
16:A1:97:21:F4:8A:19:3C:AB:6B:E6:DE:EC:0C:81:45:
7E:69:38:4C:5B:02:20:12:62:DE:AF:B5:99:E5:90:1D:
BB:EE:79:26:12:95:EB:16:07:56:DF:F4:6B:60:81:4C:
A4:7F:2F:44:29:29:17
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 71:7E:95:F3:C2:38:8A:6D:B1:E3:84:49:3D:31:E1:5A:
A9:62:08:76:2D:42:00:E0:05:0C:D0:67:B5:A6:61:E2
Timestamp : Jan 17 06:32:37.943 2026 GMT
Extensions: 00:00:05:00:07:25:D9:19
Signature : ecdsa-with-SHA256
30:45:02:20:1C:8D:04:A9:26:A8:10:39:FC:8A:60:20:
1A:C6:6A:37:8F:6F:F0:9C:96:35:B6:9F:5D:BA:6F:BB:
75:BF:7F:99:02:21:00:E0:48:9A:96:3E:B9:09:DA:F5:
B1:FD:80:21:B0:9E:37:28:F0:4C:04:C4:6C:F1:5D:FE:
26:C3:DA:69:24:D5:11
Signature Algorithm: ecdsa-with-SHA384
Signature Value:
30:66:02:31:00:fa:02:63:34:b1:96:3f:28:88:d2:8d:bc:1b:
31:8f:04:1e:69:b8:f7:14:f8:48:0a:32:bb:ff:0d:19:9f:74:
a0:13:f1:0f:7f:3f:03:13:51:67:dc:68:ae:16:74:27:ea:02:
31:00:9b:19:b0:05:01:ca:d8:bf:8e:e7:fc:a0:ad:ad:3e:0c:
03:1a:58:07:ca:31:c6:14:3d:ba:76:53:ca:80:f1:d8:f3:e4:
72:14:68:9d:7c:5e:cd:3b:5c:14:ee:fb:97:a3
証明書の発行者→Issuer: C = US, O = Let’s Encrypt, CN = E7
証明対象のドメイン→Subject: CN = kikuichige.com
証明対象の公開鍵→Subject Public Key Info:
これらが書かれているCertificateの部分をSignature Algorithm: ecdsa-with-SHA384で署名したものが、Signature Valueになる。
TLS(HTTPS)の仕組み
クライアントがサーバーに接続する流れは、TLS 1.3 ハンドシェイクの流れ (kikuichige.com)のようになっています。
この中でサーバー証明書が使われるのは、4.Certificateと5.CertificateVerifyです。
また、上記の公開鍵と秘密鍵のペアは証明書の検証のみにしか使われていません。
それ以外の暗号通信や署名には、複数の鍵をクライアントとサーバー間で作成して使っています。

TLS 1.3において、ブラウザの証明書ビュアーで見える情報(静的なデータ)を使って、どのように検証されるのかを紐解いていきましょう。
混乱しやすいポイントですが、TLS 1.3には2つの異なる署名検証が登場します。
- Certificate(証明書そのもの)の検証: その証明書が本物か?(有効期限や認証局の署名)
- CertificateVerify(ハンドシェイク中の挙動)の検証: 今通信している相手は本当にその証明書の持ち主か?
これらがどう繋がるのかを解説します。
サーバー証明書をブラウザで見てみる
証明書はブラウザで見れます。
Edgeの場合ですが、アドレスバーの左にある①鍵マークをクリック→「接続がセキュリティで保護されています」をクリック

証明書のマークをクリック

証明書が表示されます。
詳細タブに切り替え、「証明書フィールド」で見たいものをクリックすると値が「フィールド値」に表示されます。

「証明書署名アルゴリズム」:SHA-384 を使用した X9.62 ECDSA 署名
「証明書の署名値」:30 66 02 31 00 FA 02 63 34 B1 96 3F 28 88 D2 8D 略
「サブジェクトの公開キー」:00 04 FA 91 F4 31 4E 6B 21 D7 AB A4 0D E9 16 98 略
補足:9.62 ECDSA署名の「X9.62」は、金融業界標準化団体ANSIが定めた楕円曲線デジタル署名アルゴリズム(ECDSA)の技術仕様書(ANSI X9.62)
Certificate(証明書)の検証
ブラウザの証明書ビュアーで見ることができる「署名」は、「この証明書の内容(ドメイン名(CN)や公開鍵)は、認証局(今回Let’s Encrypt)が保証したものである」ことを証明するためのものです。
検証のプロセス
ビュアーに表示されている以下の3つの要素を使います。
- ① サブジェクトの公開キー: サーバー(kikuichige.com)自身の公開鍵です。
実際の値-00 04 FA 91 F4 31 4E 6B 21 D7 AB A4 0D E9 16 98 略 - ② 証明書署名アルゴリズム: 「SHA-384 を使用した X9.62 ECDSA 署名」
- ③ 証明書の署名値: CA(Let’s Encrypt )が作成した「証明書に記載されたサーバー情報や公開鍵などのデータ全体」に対する署名
実際の値-30 66 02 31 00 FA 02 63 34 B1 96 3F 28 88 D2 8D 略
【手順】
- ハッシュ化: ブラウザは、「証明書に記載されたサーバー情報や公開鍵などのデータ全体(cert.pemのCertifcate部分)」を、指定されたアルゴリズム(SHA-384)でハッシュ値に変換します。
- 検証: 「認証局(Let’s Encrypt )の公開鍵」を用いて、証明書に添付されている「③ 証明書の署名値」を検証します。
補足:後述の「認証局(Let’s Encrypt )の公開鍵」がどこからくるのか?を参照してください。
検証の方法は、こちらのパスキーの仕組みの記事を参照してください。
そこで説明されている「署名対象データ」が
署名対象データ: 「証明書に記載されたサーバー情報や公開鍵などのデータ全体」
に代わっているだけで基本的に同じです。
CertificateVerify(ハンドシェイク)の検証
証明書が本物だとわかっても、「悪意のある第三者が本物の証明書をコピーして使い回している」可能性が残ります。通信している相手が、証明書の公開鍵とペアの秘密鍵をもっていることを確かめる必要があります。この役目が、TLS 1.3のハンドシェイク中に送られてくる CertificateVerify メッセージです。
ここで、ビュアーに表示されていた 「① サブジェクトの公開キー」 が牙を剥きます(守りの要になります)。
検証のプロセス
- サーバー側の動作: サーバーは、ここまでの通信内容(ハンドシェイクのログ全体)に対して、自分の秘密鍵を使って署名を作成し、クライアントへ送ります。これが
CertificateVerifyです。 - クライアント(ブラウザ)の動作: ブラウザは、先ほど「本物だ」と確認した証明書の中にある 「① サブジェクトの公開キー」 を取り出します。
- 検証: その公開鍵とクライアントもサーバー同様、通信内容(ハンドシェイクのログ全体)は把握しているので、それを使って、送られてきた署名を検証します。
検証の方法は、こちらのパスキーの仕組みの記事で説明した「署名対象データ」が
署名対象データ: 通信内容(ハンドシェイクのログ全体)
に代わっているだけで基本的に同じです。
ここがポイント!
サーバーが持つ「秘密鍵」で署名されたものは、証明書に載っている「公開鍵」でしか検証できません。これが成功すれば、「目の前の相手は、間違いなくこの証明書に対応する秘密鍵を持っている本人だ」と断定できるわけです。
Let’s Encrypt 自体の公開鍵はどこからくるのか?
結論から言うと、「Let’s Encrypt 自体の公開鍵」が直接ブラウザに入っているわけではありません。
しかし、ブラウザ(または OS)の中には、Let’s Encrypt が信頼している「親(ルート認証局)」の公開鍵が必ず入っています。これによって、バケツリレーのように信頼が繋がります。これを「信頼の連鎖(Certificate Chain)」と呼びます。
ブラウザがサイトの証明書を検証する際、以下のようなステップを踏みます。
1. あなたの証明書(End Entity)
ブラウザはあなたのサイトの証明書を見ます。そこには「Let’s Encrypt が署名しました」と書かれています。
2. 中間証明書(Intermediate CA)
次にブラウザは、Let’s Encrypt の公開鍵が含まれる「中間証明書」を確認します。
表示コマンドopenssl x509 -in chain.pem -text -noout
結果
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
aa:75:f1:e6:2b:8f:0a:22:09:66:d3:8b:bf:d4:ba:a1
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
Validity
Not Before: Mar 13 00:00:00 2024 GMT
Not After : Mar 12 23:59:59 2027 GMT
Subject: C = US, O = Let's Encrypt, CN = E7
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
04:41:e8:04:93:08:58:7f:be:37:30:0c:c0:a0:41:
ea:fe:56:da:84:93:3e:c9:00:db:ab:67:12:cf:f9:
4f:53:09:e8:a8:2f:ab:29:e5:9f:15:46:f4:5b:62:
4e:0f:d4:83:41:99:b7:9f:40:72:45:1c:2c:5c:4a:
32:a6:c2:db:c6:05:6a:65:ff:da:da:f0:75:b4:40:
3b:14:68:95:b6:a8:e2:6a:71:e2:74:65:51:53:de:
16:d4:1e:27:c1:33:fd
ASN1 OID: secp384r1
NIST CURVE: P-384
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
X509v3 Extended Key Usage:
TLS Web Client Authentication, TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Subject Key Identifier:
AE:48:9E:DC:87:1D:44:A0:6F:DA:A2:E5:60:74:04:78:C2:9C:00:80
X509v3 Authority Key Identifier:
79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E
Authority Information Access:
CA Issuers - URI:http://x1.i.lencr.org/
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
X509v3 CRL Distribution Points:
Full Name:
URI:http://x1.c.lencr.org/
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
8f:1e:ba:7c:37:4b:93:9c:b0:16:7d:c2:cc:0d:70:d6:a7:f2:
略
- ※この中間証明書は、サーバーからサイトの証明書と一緒に送られてきます。
- Let’s Encrypt の公開鍵(Public-Key)で、あなたの証明書の署名を検証します。
- しかし、まだ「Let’s Encrypt 自体が本物か?」が不明です。この中間証明書には「ISRG Root X1(または別の親)が署名しました」と書かれています。
3. ルート証明書(Root CA)
最後に、ブラウザは自分の内部(信頼されたルート証明書ストア)を探します。そこに 「ISRG Root X1」 という名前の公開鍵を見つけます。
- これこそが、ブラウザに最初から入っている**「世界で信頼されている公開鍵」**です。
- このルート公開鍵で、中間証明書(chain.pem)の署名(Signature Value)を検証します。
なぜ Let’s Encrypt の鍵を直接入れないのか?
もし、すべての認証局(CA)の公開鍵をブラウザに直接入れてしまうと、以下のような問題が発生します。
- 更新が大変: 新しい CA が増えるたびにブラウザをアップデートしなければなりません。
- リスク分散: 万が一 Let’s Encrypt の鍵が盗まれても、親であるルート証明書が無事なら、中間証明書を失効させるだけで被害を食い止められます(ルート証明書を入れ替えるのは非常に困難な作業です)。
まとめ
私が、勘違いして分かったことや新しく学んだことを書いておきます。
- 署名と検証は、暗号・復号と同じ鍵を使った技術だが、署名と検証は、情報を隠すことが目的ではなく、情報の改ざんを防ぐものである。
- サーバー証明書に載っている公開鍵は、ドメインが正しいことを証明するだけで、HTTPSの暗号通信で使われている鍵とは無関係である。
この記事を書いたイチゲを応援する(質問でもokです)
Vプリカでのお支払いがおすすめです。