Old-School Networking / 2026 ・ No.08
いにしえのネットワーク入門
第8回 第5・6層と TLS ── 申請に鍵をかける
公開
ここまでの申請は、途中の誰にでも中身が読める状態で運ばれていました。今回は、TCP とアプリの間に入って申請に鍵をかける TLS です。HTTPS の S の正体でもあります。
練習用の CA(証明書を発行する役)を作ってサーバーに証明書を渡し、HTTPS にすると何が見えなくなり、何がまだ見えていて、どれだけ遅くなるのか。確かめに失敗する4つの場面も、わざと作って確かめます。
表し方と会話の層
新井路線図の 8 番、「第5・6層」ってまとめてありますね。どうして1つの回なんですか?
古井インターネットの実際では、第5層と第6層はほとんど独立していない。TLS やアプリの中に溶けこんでいるんだ。だから一緒に片づける。
全12回の路線図(いまここ:第8回)
- 第5・6層 ── 表し方(文字コード、JSON、圧縮、暗号)と会話の管理
- HTTPS にする ── 何が見えなくなるか、どれだけ遅くなるか(実測)
- 鍵と証明書 ── 鍵の共有、証明書の連なり、確かめに失敗する4つの場面(実測)
- まだ見えるもの ── 名前(SNI)と、それを隠す ECH。証明書の期限が短くなる流れ
第5・6層は、アプリと TCP の間にいる
第6層は表し方の層です。同じ「交通費」でも、相手と同じ決まりで並べなければ読めません。いまの決まりは、文字なら UTF-8、データなら JSON、縮めるなら gzip です。
# 「交通費」は UTF-8 で 9 バイト(第7回の表紙のバイトの欄にも出ていた)
交通費 → e4 ba a4 e9 80 9a e8 b2 bb
# CSV 2 MB を gzip で縮める(同じ行のくり返しなので極端に縮む)
2,097,152 bytes → 6,179 bytes
第5層は会話の管理の層で、会話の始まりと終わり、途切れたときの再開を受け持ちます。TCP の接続や、HTTP のクッキー、TLS の再開の機能などに分かれて入っています。そして暗号も表し方の一つ。TLS は、この2つの層にまたがる仕事をしています。
丸見えの申請
第7回の結びで、申請の JSON が Wireshark でそのまま読めることに気づきました。記録ファイルの中から、申請の文字を探してみます。
# 記録ファイルの中から、申請の文字を探す
HTTP '"amount":1280' 見つかった
HTTP 'POST /api/expenses' 見つかった
HTTPS '"amount":1280' 見つからない
HTTPS 'POST /api/expenses' 見つからない
HTTPS 'expenses.example' 見つかった ← Client Hello SNI
HTTP の記録には、URL も金額もそのまま入っています。途中のルーターや、同じ Wi-Fi にいる誰か、プロバイダの機械を通るたびに、読める状態で運ばれていたわけです。読めるということは、書きかえて先へ渡すこともできるということです。HTTPS の記録からは、URL も金額も見つかりませんでした。ただ一つ、expenses.example という名前を除いて(第7章)。
TLS の3つの約束
HTTPS にする
サーバーに、名前 expenses.example の証明書を持たせ、Node.js の https で 3443 番に申請の API を立てました(.example は説明用に予約された名前です)。証明書は、筆者が作った練習用の CA「Kiji Practice Root CA」で発行しました。PC の curl には、その CA を --cacert で教えています。
| つながるまで | TLS のあいさつ完了 | 返事の最初の1バイト | 往復の数 | |
|---|---|---|---|---|
| HTTP | 0.041 秒 | — | 0.083 秒 | 2 |
| HTTPS(TLS 1.3) | 0.041 秒 | 0.085 秒 | 0.127 秒 | 3 |
| HTTPS(TLS 1.2) | 0.041 秒 | 0.126 秒 | 0.168 秒 | 4 |
往復 0.041 秒の線で、HTTP は返事まで 2 往復。HTTPS(TLS 1.3)は TLS のあいさつで 1 往復増えて 3 往復、古い TLS 1.2 では 2 往復増えて 4 往復でした。暗号の計算そのものにかかった時間は、ほんの数ミリ秒です。遅さの正体は、計算ではなく往復なのです。
# TLS 1.3(片道 20 ms の線):TCP のあいさつのあと、TLS のあいさつは 1 往復。申請(9番)は暗号の中 1 0.000 pc → srv TCP [SYN] 2 0.041 srv → pc TCP [SYN, ACK] 3 0.041 pc → srv TCP [ACK] 4 0.043 pc → srv TLSv1 Client Hello (SNI=expenses.example) 5 0.083 srv → pc TCP [ACK] 6 0.084 srv → pc TLSv1.3 Server Hello, Change Cipher Spec, Application Data, Application Data, Application Data, Application Data 7 0.084 pc → srv TCP [ACK] 8 0.085 pc → srv TLSv1.3 Change Cipher Spec, Application Data 9 0.086 pc → srv TLSv1.3 Application Data 10 0.126 srv → pc TLSv1.3 Application Data, Application Data 11 0.130 srv → pc TLSv1.3 Application Data # TLS 1.2:TLS のあいさつに 2 往復。申請(10番)はそのあと 1 0.000 pc → srv TCP [SYN] 2 0.041 srv → pc TCP [SYN, ACK] 3 0.041 pc → srv TCP [ACK] 4 0.042 pc → srv TLSv1 Client Hello (SNI=expenses.example) 5 0.083 srv → pc TCP [ACK] 6 0.083 srv → pc TLSv1.2 Server Hello, Certificate, Server Key Exchange, Server Hello Done 7 0.083 pc → srv TCP [ACK] 8 0.085 pc → srv TLSv1.2 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message 9 0.128 srv → pc TLSv1.2 Change Cipher Spec, Encrypted Handshake Message 10 0.128 pc → srv TLSv1.2 Application Data 11 0.170 srv → pc TLSv1.2 Application Data
TLS 1.3 のあいさつ(実測の時刻)
TLS 1.3 では、PC が最初の Client Hello で鍵の材料を先に送ってしまうので、サーバーの返事(Server Hello)を受け取った時点で暗号の準備ができます。証明書も、Server Hello より後ろはもう暗号の中です。TLS 1.2 では、材料のやりとりにもう1往復かかっていました。TLS 1.0 と 1.1 は、2021 年に正式に使用をやめるよう定められています。
往復が増える分、第6回の「接続の使い回し」がいっそう効きます。HTTPS では、つなぎ直すたびに TCP と TLS で2往復。keep-alive や接続プールは必須です(第10回)。
鍵をどう決めるか
新井暗号の鍵って、どうやって相手に渡すんですか? 線で送ったら、それこそ途中で読まれますよね。
古井鍵そのものは送らない。それぞれが手元で同じ鍵を計算するんだ。
鍵は送らず、両側で同じものを計算する
PC とサーバーは、それぞれ秘密の数を作り、そこから計算した「公開してよい数」だけを Client Hello と Server Hello に載せて交換します。自分の秘密と相手の公開の数を組み合わせると、両側で同じ鍵ができます。途中で公開の数を見ても、鍵は計算できません。秘密は接続ごとに作り直して捨てるので、あとでサーバーの鍵が漏れても、過去の記録を読み解くことはできません(前方秘匿性)。
# openssl s_client で、決まった中身を見る
pc$ openssl s_client -connect 203.0.113.20:3443 -servername expenses.example -CAfile ca.pem -brief
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN = expenses.example
Hash used: SHA256
Signature type: ECDSA
Verification: OK
Server Temp Key: X25519, 253 bits
# 証明書の連なり(0 がサーバー、i: は署名した者)
0 s:CN = expenses.example
i:CN = Kiji Practice Root CA
Verify return code: 0 (ok)
# サーバーの証明書
subject=CN = expenses.example
issuer=CN = Kiji Practice Root CA
notBefore=Oct 24 23:40:01 2026 GMT
notAfter=Jan 22 23:40:01 2027 GMT
DNS:expenses.example
openssl s_client で見ると、この接続は TLS 1.3、鍵の共有は X25519、中身の暗号は TLS_AES_256_GCM_SHA384 でした。AES-256-GCM は、暗号にすると同時に「書きかえられていないか」の印も付けるので、3つの約束のうち2つをこれで果たします。残る1つ、相手が本物かどうかを受け持つのが、証明書です。
相手が本物か:証明書
鍵の共有がどれほど安全でも、相手が偽物のサーバーなら、偽物と安全に話すだけです。そこでサーバーは、証明書を見せて「わたしは expenses.example です」と名乗ります。証明書には、名前、期限、公開鍵が書かれ、信頼できる CA の署名が付いています。
署名をたどって、信頼できるルートに着くか
本物のサイトの証明書は、OS やブラウザに最初から入っている「ルート CA」から、中間 CA を経て署名されています。PC は、名前が合うか・期限の内か・署名をたどってルートに着くかの3つを確かめます。この回の練習用 CA は筆者の PC の一覧に入っていないので、curl に教えて信頼させました。
確かめに失敗する4つの場面
開発中に一度は見るエラーを、わざと起こしました。名前違い、期限切れ、自己署名の証明書は、それぞれ練習用に作ったものです。
# 練習用 CA を教えずにつなぐ
curl: (60) SSL certificate problem: unable to get local issuer certificate
# 住所(IP)でつなぐ。証明書の名前は expenses.example
curl: (60) SSL: no alternative certificate subject name matches target host name '203.0.113.20'
# 期限切れの証明書(2026-09-29 まで)
curl: (60) SSL certificate problem: certificate has expired
# 自分で自分に署名した証明書
curl: (60) SSL certificate problem: self-signed certificate
# 練習用 CA を教え、名前でつなぐ → 確かめに成功
HTTP 404 (GET /api/expenses)
| エラー | どの確かめに失敗したか | 正しい直し方 |
|---|---|---|
| unable to get local issuer certificate | 署名をたどってもルートに着かない | 中間証明書もサーバーに設定する。社内 CA なら、その CA を信頼する一覧に入れる |
| no alternative certificate subject name matches | 名前が合わない | 証明書に書かれた名前でつなぐ。必要な名前を証明書に入れる |
| certificate has expired | 期限切れ | 更新する。自動更新にする(第8章) |
| self-signed certificate | 信頼できる署名がない | CA から発行してもらう。開発用なら手元に開発用の CA を作る |
エラーを消すために、確かめそのものを切ってはいけません(curl の -k、Node.js の NODE_TLS_REJECT_UNAUTHORIZED=0、Python requests の verify=False など)。暗号はかかっても、相手が本物かどうかがわからなくなり、3つの約束の1つが消えます。開発中に「とりあえず」で入れたものが本番に残る事故は、昔から絶えません。
まだ見えているもの
表紙の写真のとおり、Client Hello の SNI には、つなぎたい相手の名前 expenses.example がそのまま書かれています。1台のサーバーがたくさんの名前のサイトを抱えているとき、どの証明書を出せばよいかを、暗号の前に知る必要があるからです。ほかにも、相手の IP アドレス、通信の量と時刻は、TLS をかけても途中から見えます。
この名前を隠すために、Client Hello そのものを暗号にする ECH(Encrypted Client Hello)が、2026 年 3 月に RFC 9849 として標準になりました。ただし、名前は DNS の問い合わせ(第9回)からも漏れるので、両方がそろって初めて隠れます。
証明書の期限と自動更新
公開サイトの証明書の最長の期限(CA/Browser Forum の決定)
ブラウザと CA の業界団体 CA/Browser Forum は、公開サイトの証明書の期限を段階的に短くすることを決めました。2026 年 3 月 15 日からは最長 200 日、2029 年 3 月 15 日からは 47 日です。年に数回の更新を人の手でこなすのは現実的ではないので、ACME という手順で自動的に発行・更新するのが前提になります。無料の CA の Let's Encrypt も、この ACME で証明書を配っています。
開発者としては、「証明書の期限切れで止まる」事故を、監視と自動更新で防ぐことが大切です。期限は openssl x509 -noout -dates や、ブラウザの鍵のマークから確かめられます。
自分の PC で確かめる
| 調べたいこと | どうやって |
|---|---|
| TLS の版と暗号 | openssl s_client -connect example.com:443 -servername example.com -brief |
| あいさつの経過と証明書 | curl -v https://example.com/ -o /dev/null |
| あいさつにかかった時間 | curl -o /dev/null -s -w '%{time_connect} %{time_appconnect}\n' URL |
| 証明書の期限 | openssl x509 -in cert.pem -noout -dates |
| 信頼する CA の一覧 | Mac:キーチェーンアクセス、Windows:certmgr.msc、Linux:/etc/ssl/certs |
ブラウザのアドレス欄の左の印を押すと、そのサイトの証明書を見られます。発行した CA、名前、期限を確かめてみましょう。
結 — 一枚まとめと、次回
- 第6層は表し方(UTF-8、JSON、gzip、暗号)、第5層は会話の管理。実際には TLS やアプリに溶けこんでいる。
- HTTP の記録では URL も金額も読めた。HTTPS では見つからない。
- TLS の約束は3つ:読まれない・書きかえに気づく・相手が本物。
- HTTPS(TLS 1.3)は HTTP より 1 往復、TLS 1.2 は 2 往復多い。遅さの正体は計算ではなく往復。
- 鍵は送らず、公開してよい数を交換して両側で計算する(X25519)。接続ごとに作り直す。
- 証明書は名前・期限・署名の連なりを確かめる。失敗しても、確かめを切ってはいけない。
- SNI の名前、IP、量と時刻は見えている。名前は ECH(RFC 9849)で隠せる。証明書の期限は 2029 年に 47 日へ。
新井そういえば、curl にずっと --resolve で「expenses.example は 203.0.113.20」って書いてましたよね。あれ、ずるくないですか?
古井ずるい。本当は、名前から住所を引く仕組みがある。次回は DNS。インターネットの電話帳だ。
参考
- RFC 8446 — The Transport Layer Security (TLS) Protocol Version 1.3
- RFC 8996 — Deprecating TLS 1.0 and TLS 1.1
- RFC 5280 — Internet X.509 Public Key Infrastructure Certificate and CRL Profile
- RFC 9525 — Service Identity in TLS
- RFC 9849 — TLS Encrypted Client Hello (2026-03)
- RFC 8555 — Automatic Certificate Management Environment (ACME)
- RFC 2606 — Reserved Top Level DNS Names(.example)
- DigiCert — Public TLS certificates: 199-day validity(SC-081)
- DNSimple — SSL Certificate Validity Changes (2026–2029)
- Let's Encrypt — How It Works
- curl — SSL Certificate Verification
- Node.js — TLS (SSL)
記録は、筆者がクラウド上の Linux(Ubuntu 24.04、カーネル 6.18)にネットワーク名前空間で作った練習用ネットワークで、2026年10月25日に取ったものです。PC とサーバーの間は第6回のいたずら線(片道 20 ミリ秒)です。証明書は、筆者が OpenSSL 3.0.13 で作った練習用の CA から発行し(期限切れのものは Python の cryptography で日付を指定して作成)、サーバーは Node.js 22(OpenSSL 3.5)、クライアントは curl 8.5.0 です。表の数字はすべて、この記録とそのときのコマンドの出力から読んだ実際の値です。証明書の期限の決まりは CA/Browser Forum の決定(SC-081)を伝える DigiCert と DNSimple の告知によります。登場人物は架空の人物です。
いにしえのネットワーク入門 第8回 — 第5・6層と TLS / 2026.10