TLS
TLS(Transport Layer Security)完全ガイド
はじめに
TLS(Transport Layer Security)は、インターネット上の通信を暗号化し、データの機密性、完全性、および認証を提供するプロトコルである。Webブラウジング(HTTPS)、メール(SMTPS/IMAPS)、VPN、VoIPなど、あらゆるネットワーク通信のセキュリティ基盤として広く使用されている。
TLSはSSL(Secure Sockets Layer)の後継プロトコルであり、Netscape社が1994年に開発したSSL 2.0から始まり、SSL 3.0を経て、1999年にIETFによりTLS 1.0(RFC 2246)として標準化された。最新のTLS 1.3(RFC 8446)は2018年に公開され、セキュリティの強化とパフォーマンスの改善が行われた。
本記事では、TLSの基本概念から暗号技術の基礎、ハンドシェイクプロトコル、証明書管理、パフォーマンス最適化、セキュリティベストプラクティス、そしてTLS 1.3の最新仕様まで、TLSの全容を体系的に解説する。
第1章: TLSの基本概念とプロトコルスタック
1.1 TLSの位置づけ
TLSはOSI参照モデルのトランスポート層(第4層)とアプリケーション層(第7層)の間に位置する。正確にはセッション層(第5層)とプレゼンテーション層(第6層)に相当する。
アプリケーション層 HTTP, SMTP, FTP, IMAP ...
─────────────────────────────────────────────
TLS 暗号化・認証・完全性
─────────────────────────────────────────────
トランスポート層 TCP
─────────────────────────────────────────────
ネットワーク層 IP
─────────────────────────────────────────────
データリンク層 Ethernet, Wi-Fi
1.2 TLSの3つの主要機能
| 機能 | 説明 | 実現手段 |
|---|---|---|
| 機密性(Confidentiality) | 通信内容を第三者に読み取られないようにする | 対称暗号(AES-GCM等) |
| 完全性(Integrity) | 通信内容が改ざんされていないことを保証 | MAC(HMAC, AEAD) |
| 認証(Authentication) | 通信相手が正当であることを確認 | デジタル証明書(X.509) |
1.3 TLSバージョンの歴史
| バージョン | RFC | 年 | 状態 |
|---|---|---|---|
| SSL 1.0 | - | 1994 | 未公開(重大な欠陥) |
| SSL 2.0 | - | 1995 | 非推奨(RFC 6176で禁止) |
| SSL 3.0 | RFC 6101 | 1996 | 非推奨(POODLE攻撃、RFC 7568で禁止) |
| TLS 1.0 | RFC 2246 | 1999 | 非推奨(RFC 8996で禁止) |
| TLS 1.1 | RFC 4346 | 2006 | 非推奨(RFC 8996で禁止) |
| TLS 1.2 | RFC 5246 | 2008 | 現在も広く使用されている |
| TLS 1.3 | RFC 8446 | 2018 | 最新の推奨バージョン |
1.4 TLSプロトコルの構造
TLSは2つの主要な層で構成される:
┌──────────┬──────────────┬──────────┬──────────────┐
│Handshake │ Change Cipher│ Alert │ Application │
│ Protocol │ Spec Protocol│ Protocol │ Data Protocol│
├──────────┴──────────────┴──────────┴──────────────┤
│ TLS Record Protocol │
├─────────────────────────────────────────────────────┤
│ TCP │
└─────────────────────────────────────────────────────┘
TLS Record Protocol(レコードプロトコル):
- 上位プロトコルのデータをフラグメント化
- 圧縮(TLS 1.3では廃止)
- MAC(メッセージ認証コード)の付加またはAEAD暗号化
- 暗号化
- TCPへの送信
上位プロトコル:
- Handshake Protocol: 暗号パラメータの交渉、認証
- Change Cipher Spec Protocol: 暗号仕様の切り替え通知(TLS 1.3では廃止)
- Alert Protocol: エラーや警告の通知
- Application Data Protocol: アプリケーションデータの転送
1.5 TLSレコードの構造
0 1 2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Content Type | Version | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Fragment (payload) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| フィールド | サイズ | 説明 |
|---|---|---|
| Content Type | 1バイト | 20=ChangeCipherSpec, 21=Alert, 22=Handshake, 23=ApplicationData |
| Version | 2バイト | TLS 1.0=0x0301, TLS 1.2=0x0303, TLS 1.3=0x0303 |
| Length | 2バイト | フラグメントの長さ(最大16384バイト = 2^14) |
| Fragment | 可変 | ペイロードデータ |
注意: TLS 1.3では、互換性のためレコードのバージョンフィールドは0x0303(TLS 1.2)のままとなる。実際のバージョンはHandshakeのsupported_versions拡張で交渉される。
第2章: 暗号技術の基礎
TLSを理解するためには、その基盤となる暗号技術を理解する必要がある。
2.1 対称暗号(Symmetric Encryption)
同じ鍵で暗号化と復号を行う方式。高速だが、鍵の安全な共有が課題。
AES(Advanced Encryption Standard)
現在最も広く使用されているブロック暗号。128ビットのブロックサイズを持ち、鍵長は128/192/256ビット。
TLSで使用される暗号利用モード:
| モード | 説明 | TLSバージョン |
|---|---|---|
| CBC(Cipher Block Chaining) | ブロック連鎖方式 | TLS 1.0-1.2(非推奨) |
| GCM(Galois/Counter Mode) | 認証付き暗号(AEAD) | TLS 1.2-1.3(推奨) |
| CCM(Counter with CBC-MAC) | 認証付き暗号(AEAD) | TLS 1.2-1.3 |
ChaCha20-Poly1305
Googleが開発したストリーム暗号(ChaCha20)とMAC(Poly1305)の組み合わせ。AES-NIハードウェアアクセラレーションが利用できない環境(モバイルデバイスなど)で高いパフォーマンスを発揮する。
AES-GCM vs ChaCha20-Poly1305:
- AES-GCM: AES-NIサポートのあるCPUで高速
- ChaCha20-Poly1305: ソフトウェア実装で高速、サイドチャネル攻撃に強い
2.2 AEAD(Authenticated Encryption with Associated Data)
暗号化と認証を同時に行う方式。TLS 1.3ではAEAD暗号のみが使用可能。
AEAD暗号化:
入力: 平文 + 関連データ(AAD)+ 鍵 + ノンス(IV)
出力: 暗号文 + 認証タグ
関連データ(AAD): 暗号化はされないが、認証の対象となるデータ
(例: TLSレコードヘッダ)
TLS 1.3で許可されるAEAD暗号スイート:
TLS_AES_128_GCM_SHA256TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256TLS_AES_128_CCM_SHA256TLS_AES_128_CCM_8_SHA256
2.3 公開鍵暗号(Asymmetric Encryption)
異なる2つの鍵(公開鍵と秘密鍵)を使用する方式。鍵の共有問題を解決するが、対称暗号に比べて計算コストが高い。
RSA(Rivest-Shamir-Adleman)
大きな整数の素因数分解の困難性に基づく。鍵交換と電子署名の両方に使用可能。
RSA鍵長の推奨:
- 2048ビット: 最低限の推奨(2030年まで)
- 3072ビット: 中程度のセキュリティ
- 4096ビット: 高セキュリティ
注意: TLS 1.3ではRSA鍵交換は廃止された(前方秘匿性がないため)
RSA署名は引き続き使用可能
ECDSA(Elliptic Curve Digital Signature Algorithm)
楕円曲線暗号に基づく電子署名アルゴリズム。RSAと同等のセキュリティをより短い鍵長で実現。
鍵長の比較:
RSA 2048ビット ≈ ECDSA 224ビット(secp224r1)
RSA 3072ビット ≈ ECDSA 256ビット(secp256r1/P-256)
RSA 7680ビット ≈ ECDSA 384ビット(secp384r1/P-384)
RSA 15360ビット ≈ ECDSA 521ビット(secp521r1/P-521)
EdDSA(Edwards-curve Digital Signature Algorithm)
Twisted Edwardsカーブに基づく高速な電子署名アルゴリズム。Ed25519(Curve25519ベース)が広く使用されている。
2.4 鍵交換(Key Exchange)
TLSハンドシェイクで対称暗号の鍵(セッション鍵)を安全に共有するためのメカニズム。
Diffie-Hellman鍵交換(DH)
アリス(クライアント) ボブ(サーバー)
a = ランダムな秘密値 b = ランダムな秘密値
A = g^a mod p B = g^b mod p
│ │
│ ← g, p, A → │
│ ← B → │
│ │
共有秘密 = B^a mod p 共有秘密 = A^b mod p
= g^(ab) mod p = g^(ab) mod p
ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)
楕円曲線上のDiffie-Hellman鍵交換。一時的な鍵を使用するため、前方秘匿性(Forward Secrecy)を提供する。
TLS 1.3で使用される名前付き曲線:
- x25519: 高速、安全、推奨(Daniel J. Bernstein設計)
- secp256r1(P-256): NIST標準、広く使用
- secp384r1(P-384): より高いセキュリティ
- x448: 高セキュリティ
前方秘匿性(Forward Secrecy / PFS):
- 一時的な鍵(Ephemeral Key)を各セッションで生成
- サーバーの長期秘密鍵が漏洩しても、過去のセッションは解読不可
- TLS 1.3では前方秘匿性が必須
非PFS(RSA鍵交換、TLS 1.2以前):
サーバーの秘密鍵が漏洩 → 過去のすべてのセッションが解読可能
PFS(ECDHE鍵交換):
サーバーの秘密鍵が漏洩 → 過去のセッションは安全
2.5 ハッシュ関数
任意の長さのデータから固定長のハッシュ値を生成する一方向関数。
| アルゴリズム | 出力長 | TLSでの使用 |
|---|---|---|
| MD5 | 128ビット | 非推奨(衝突攻撃可能) |
| SHA-1 | 160ビット | 非推奨(衝突攻撃実証済み) |
| SHA-256 | 256ビット | TLS 1.2/1.3で使用 |
| SHA-384 | 384ビット | TLS 1.2/1.3で使用 |
2.6 HMAC(Hash-based Message Authentication Code)
ハッシュ関数と秘密鍵を使用してメッセージ認証コードを生成する。
HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
K: 秘密鍵
m: メッセージ
H: ハッシュ関数
opad: 0x5c5c5c...
ipad: 0x363636...
2.7 HKDF(HMAC-based Key Derivation Function)
TLS 1.3で使用される鍵導出関数。マスターシークレットから複数の暗号鍵を安全に導出する。
HKDF-Extract(salt, IKM) → PRK
salt: ソルト値
IKM: 入力鍵材料(Input Keying Material)
PRK: 擬似ランダム鍵(Pseudorandom Key)
HKDF-Expand(PRK, info, L) → OKM
PRK: 擬似ランダム鍵
info: コンテキスト情報
L: 出力長
OKM: 出力鍵材料(Output Keying Material)
第3章: TLS 1.2 ハンドシェイク
3.1 フルハンドシェイク(ECDHE鍵交換)
クライアント サーバー
│ │
│ ① ClientHello │
│ (バージョン, ランダム値, │
│ 暗号スイート一覧, │
│ 圧縮方式, 拡張) │
│─────────────────────────────────────────>│
│ │
│ ② ServerHello │
│ (選択されたバージョン, ランダム値, │
│ 選択された暗号スイート) │
│<─────────────────────────────────────────│
│ │
│ ③ Certificate │
│ (サーバー証明書チェーン) │
│<─────────────────────────────────────────│
│ │
│ ④ ServerKeyExchange │
│ (ECDHEパラメータ + 署名) │
│<─────────────────────────────────────────│
│ │
│ ⑤ ServerHelloDone │
│<─────────────────────────────────────────│
│ │
│ ⑥ ClientKeyExchange │
│ (クライアントのECDH公開値) │
│─────────────────────────────────────────>│
│ │
│ ⑦ ChangeCipherSpec │
│─────────────────────────────────────────>│
│ │
│ ⑧ Finished │
│ (ハンドシェイクの検証値) │
│─────────────────────────────────────────>│
│ │
│ ⑨ ChangeCipherSpec │
│<─────────────────────────────────────────│
│ │
│ ⑩ Finished │
│<─────────────────────────────────────────│
│ │
│ ===== 暗号化通信開始 ===== │
│ Application Data ◄──────────────────► │
ハンドシェイクに必要なRTT: 2 RTT
3.2 各メッセージの詳細
ClientHello
ClientHello {
client_version: TLS 1.2 (0x0303)
random: 32バイトのランダム値(タイムスタンプ4バイト + ランダム28バイト)
session_id: セッション再開用ID(空の場合は新規接続)
cipher_suites: [
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // 0xC02F
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // 0xC030
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // 0xC02B
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // 0xC02C
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, // 0xCCA8
...
]
compression_methods: [null (0x00)]
extensions: [
server_name (SNI): "www.example.com",
supported_groups: [x25519, secp256r1, secp384r1],
signature_algorithms: [rsa_pss_rsae_sha256, ecdsa_secp256r1_sha256, ...],
ec_point_formats: [uncompressed],
...
]
}
暗号スイートの命名規則(TLS 1.2)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
│ │ │ │ │ │ │
│ │ │ │ │ │ └─ PRFのハッシュ: SHA-256
│ │ │ │ │ └────── 暗号モード: GCM(AEAD)
│ │ │ │ └────────── 鍵長: 128ビット
│ │ │ └────────────── 対称暗号: AES
│ │ └──────────────────────── 認証: RSA証明書
│ └────────────────────────────── 鍵交換: ECDHE
└─────────────────────────────────── プロトコル: TLS
ServerKeyExchange(ECDHE)
ServerKeyExchange {
curve_type: named_curve (0x03)
named_curve: x25519 (0x001D) or secp256r1 (0x0017)
public_key: サーバーの一時ECDH公開鍵
signature: 上記パラメータに対するサーバーの署名
}
3.3 鍵の導出(TLS 1.2)
プリマスターシークレット(ECDHE共有秘密)
│
▼
マスターシークレット = PRF(pre_master_secret,
"master secret",
client_random + server_random)
│
▼
鍵ブロック = PRF(master_secret,
"key expansion",
server_random + client_random)
│
├── client_write_MAC_key
├── server_write_MAC_key
├── client_write_key(暗号化鍵)
├── server_write_key(暗号化鍵)
├── client_write_IV
└── server_write_IV
3.4 セッション再開(Session Resumption)
Session ID方式
初回接続:
ClientHello (session_id = 空) → ServerHello (session_id = ABC123)
→ フルハンドシェイク
再接続:
ClientHello (session_id = ABC123)
→ ServerHello (session_id = ABC123) ← サーバーがIDを認識
→ ChangeCipherSpec + Finished(双方)
ハンドシェイク: 1 RTT(フルハンドシェイクの半分)
Session Ticket方式(RFC 5077)
初回接続:
フルハンドシェイク
→ サーバーがNewSessionTicket(暗号化されたセッション情報)を送信
→ クライアントがチケットを保存
再接続:
ClientHello (session_ticket拡張 = チケットデータ)
→ サーバーがチケットを復号してセッション情報を復元
→ 省略ハンドシェイク(1 RTT)
# OpenSSLでセッション再開の確認
$ openssl s_client -connect www.example.com:443 -reconnect -sess_out /tmp/session.pem
# セッションチケットの確認
$ openssl s_client -connect www.example.com:443 -tlsextdebug 2>&1 | grep -i ticket
3.5 SNI(Server Name Indication)
1つのIPアドレスで複数のTLSサイトをホスティングするための拡張。ClientHelloにサーバー名を含めることで、サーバーが適切な証明書を選択できる。
ClientHello {
extensions: {
server_name: [{
name_type: host_name (0),
host_name: "www.example.com"
}]
}
}
注意: SNIは平文で送信されるため、接続先のホスト名は盗聴可能。この問題を解決するために、Encrypted Client Hello(ECH、旧ESNI)が提案されている。
# SNIの確認
$ openssl s_client -connect 192.168.1.100:443 -servername www.example.com
# SNI無しの場合(デフォルト証明書が返される)
$ openssl s_client -connect 192.168.1.100:443 -noservername
第4章: TLS 1.3 ハンドシェイク
4.1 TLS 1.3の主要な変更点
TLS 1.3はTLS 1.2から大幅に改善されている:
| 項目 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| ハンドシェイクRTT | 2 RTT | 1 RTT(0-RTT再開も可能) |
| 鍵交換 | RSA, DHE, ECDHE | ECDHEのみ(前方秘匿性必須) |
| 対称暗号 | CBC, GCM, CCM | AEADのみ(GCM, CCM, ChaCha20) |
| ハッシュ | MD5, SHA-1, SHA-256... | SHA-256以上のみ |
| 圧縮 | 対応 | 廃止 |
| 再ネゴシエーション | 対応 | 廃止 |
| ChangeCipherSpec | 使用 | 廃止(互換性のためダミーは送信可) |
| 暗号化開始タイミング | Finished後 | ServerHello直後 |
| 0-RTT | なし | 対応(Early Data) |
4.2 TLS 1.3 フルハンドシェイク(1-RTT)
クライアント サーバー
│ │
│ ① ClientHello │
│ + key_share(ECDHE公開鍵) │
│ + supported_versions(TLS 1.3) │
│ + signature_algorithms │
│ + psk_key_exchange_modes │
│─────────────────────────────────────────>│
│ │
│ ② ServerHello │
│ + key_share(ECDHE公開鍵) │
│ + supported_versions(TLS 1.3) │
│<─────────────────────────────────────────│
│ │
│ ───── 以降、暗号化 ───── │
│ │
│ ③ EncryptedExtensions │
│<─────────────────────────────────────────│
│ │
│ ④ CertificateRequest(オプション) │
│<─────────────────────────────────────────│
│ │
│ ⑤ Certificate │
│<─────────────────────────────────────────│
│ │
│ ⑥ CertificateVerify │
│ (証明書の秘密鍵による署名) │
│<─────────────────────────────────────────│
│ │
│ ⑦ Finished │
│<─────────────────────────────────────────│
│ │
│ ⑧ Certificate(クライアント認証時) │
│─────────────────────────────────────────>│
│ │
│ ⑨ CertificateVerify(クライアント認証時)│
│─────────────────────────────────────────>│
│ │
│ ⑩ Finished │
│─────────────────────────────────────────>│
│ │
│ ===== 暗号化通信開始 ===== │
ハンドシェイクに必要なRTT: 1 RTT
4.3 TLS 1.3のハンドシェイクが1 RTTである理由
TLS 1.2では、サーバーが暗号スイートを選択した後にECDHEパラメータを送信していた。TLS 1.3では:
- クライアントがClientHelloに推測したECDHE公開鍵を含める(key_share拡張)
- サーバーが即座に鍵交換を完了できる
- ServerHello直後から暗号化通信が可能
TLS 1.2:
RTT 1: ClientHello → ServerHello + Certificate + ServerKeyExchange + ServerHelloDone
RTT 2: ClientKeyExchange + ChangeCipherSpec + Finished → ChangeCipherSpec + Finished
合計: 2 RTT
TLS 1.3:
RTT 1: ClientHello(+key_share) → ServerHello(+key_share) + {暗号化データ} + Finished
Finished →
合計: 1 RTT
4.4 TLS 1.3 暗号スイート
TLS 1.3では暗号スイートの命名規則が簡略化された。鍵交換と署名アルゴリズムはスイートに含まれず、拡張で個別に交渉される。
TLS 1.3の暗号スイート(AEAD + ハッシュのみ):
TLS_AES_128_GCM_SHA256 (0x1301) ← 必須実装
TLS_AES_256_GCM_SHA384 (0x1302)
TLS_CHACHA20_POLY1305_SHA256 (0x1303)
TLS_AES_128_CCM_SHA256 (0x1304)
TLS_AES_128_CCM_8_SHA256 (0x1305)
鍵交換は supported_groups 拡張で交渉:
x25519, secp256r1, secp384r1, x448, ...
署名は signature_algorithms 拡張で交渉:
rsa_pss_rsae_sha256, ecdsa_secp256r1_sha256, ed25519, ...
4.5 TLS 1.3 鍵スケジュール
TLS 1.3の鍵導出はHKDFベースの構造化された鍵スケジュールに従う。
0
│
▼
PSK ──> HKDF-Extract = Early Secret
│
├── Derive-Secret(., "ext binder" | "res binder", "")
│ = binder_key
├── Derive-Secret(., "c e traffic", ClientHello)
│ = client_early_traffic_secret
└── Derive-Secret(., "e exp master", ClientHello)
= early_exporter_master_secret
│
▼
(EC)DHE -> HKDF-Extract = Handshake Secret
│
├── Derive-Secret(., "c hs traffic", ClientHello...ServerHello)
│ = client_handshake_traffic_secret
└── Derive-Secret(., "s hs traffic", ClientHello...ServerHello)
= server_handshake_traffic_secret
│
▼
0 ──────> HKDF-Extract = Master Secret
│
├── Derive-Secret(., "c ap traffic", ClientHello...server Finished)
│ = client_application_traffic_secret_0
├── Derive-Secret(., "s ap traffic", ClientHello...server Finished)
│ = server_application_traffic_secret_0
├── Derive-Secret(., "exp master", ClientHello...server Finished)
│ = exporter_master_secret
└── Derive-Secret(., "res master", ClientHello...client Finished)
= resumption_master_secret
4.6 0-RTT(Early Data)
TLS 1.3では、前回の接続で取得したPSK(Pre-Shared Key)を使用して、最初のClientHelloと同時にアプリケーションデータを送信できる。
クライアント サーバー
│ │
│ ClientHello │
│ + early_data │
│ + key_share │
│ + psk_identity │
│─────────────────────────────────────────>│
│ [Early Data(0-RTT)] │
│═════════════════════════════════════════>│ ← 暗号化データ(0-RTT)
│ │
│ ServerHello │
│ + pre_shared_key │
│<─────────────────────────────────────────│
│ {EncryptedExtensions} │
│ {Finished} │
│<─────────────────────────────────────────│
│ │
│ {Finished} │
│─────────────────────────────────────────>│
│ │
│ ===== 1-RTTデータ ===== │
0-RTTのセキュリティ上の注意点:
- リプレイ攻撃に脆弱: 0-RTTデータは前方秘匿性を持たず、リプレイ可能
- 冪等(idempotent)なリクエストのみに使用すべき: GETリクエストなど
- POSTやDELETEなどの非冪等操作には使用しない
# 0-RTTの確認(OpenSSL 1.1.1以降)
$ openssl s_client -connect www.example.com:443 -tls1_3 \
-sess_out /tmp/session.pem
$ openssl s_client -connect www.example.com:443 -tls1_3 \
-sess_in /tmp/session.pem -early_data /tmp/request.txt
4.7 HelloRetryRequest
クライアントが提供したkey_shareがサーバーでサポートされていない場合、サーバーはHelloRetryRequestを送信して別のグループでの鍵共有を要求する。
クライアント サーバー
│ ClientHello │
│ + key_share: [x25519] │
│─────────────────────────────────────────>│
│ │
│ HelloRetryRequest │
│ + key_share: secp384r1を要求 │
│<─────────────────────────────────────────│
│ │
│ ClientHello(再送) │
│ + key_share: [secp384r1] │
│─────────────────────────────────────────>│
│ │
│ ServerHello(通常のハンドシェイク続行) │
│<─────────────────────────────────────────│
この場合、ハンドシェイクは2 RTTになる。
第5章: X.509証明書とPKI
5.1 X.509証明書の構造
Certificate {
tbsCertificate {
version: v3 (2)
serialNumber: 一意のシリアル番号
signature: 署名アルゴリズム(例: sha256WithRSAEncryption)
issuer: 発行者のDN(Distinguished Name)
validity {
notBefore: 開始日時
notAfter: 終了日時
}
subject: 主体者のDN
subjectPublicKeyInfo {
algorithm: 公開鍵アルゴリズム
subjectPublicKey: 公開鍵
}
extensions {
basicConstraints: CA: TRUE/FALSE, pathLenConstraint
keyUsage: digitalSignature, keyEncipherment, ...
extKeyUsage: serverAuth, clientAuth, ...
subjectAltName: DNS:www.example.com, DNS:example.com
authorityKeyIdentifier: 発行者の鍵識別子
subjectKeyIdentifier: 主体者の鍵識別子
crlDistributionPoints: CRL配布点
authorityInfoAccess: OCSPレスポンダURL, CA発行者URL
certificatePolicies: 証明書ポリシー(DV/OV/EV)
}
}
signatureAlgorithm: 署名アルゴリズム
signatureValue: 発行者による署名
}
5.2 証明書の種類
| 種類 | 検証内容 | 信頼性 | コスト | 発行時間 |
|---|---|---|---|---|
| DV(Domain Validation) | ドメインの所有権のみ | 低 | 無料〜安価 | 数分 |
| OV(Organization Validation) | ドメイン + 組織の実在性 | 中 | 中程度 | 数日 |
| EV(Extended Validation) | ドメイン + 組織の詳細審査 | 高 | 高価 | 数週間 |
5.3 証明書チェーンの検証
ルートCA証明書(自己署名) ← ブラウザ/OSのトラストストアに格納
│
│ 署名
▼
中間CA証明書 ← サーバーが送信
│
│ 署名
▼
サーバー証明書(エンドエンティティ) ← サーバーが送信
検証プロセス:
- サーバー証明書の署名を中間CA証明書の公開鍵で検証
- 中間CA証明書の署名をルートCA証明書の公開鍵で検証
- ルートCA証明書がトラストストアに存在するか確認
- 各証明書の有効期限を確認
- 各証明書が失効していないか確認(CRL/OCSP)
- サーバー証明書のSAN(Subject Alternative Name)がホスト名と一致するか確認
# 証明書チェーンの確認
$ openssl s_client -connect www.example.com:443 -showcerts
# 証明書の詳細表示
$ openssl s_client -connect www.example.com:443 2>/dev/null | \
openssl x509 -noout -text
# 証明書の有効期限確認
$ openssl s_client -connect www.example.com:443 2>/dev/null | \
openssl x509 -noout -dates
notBefore=Jan 1 00:00:00 2024 GMT
notAfter=Dec 31 23:59:59 2024 GMT
# SANの確認
$ openssl s_client -connect www.example.com:443 2>/dev/null | \
openssl x509 -noout -ext subjectAltName
5.4 証明書の失効確認
CRL(Certificate Revocation List)
CAが発行する失効証明書のリスト。定期的にダウンロードして確認する。
# CRL配布点の確認
$ openssl x509 -in cert.pem -noout -text | grep -A 2 "CRL Distribution"
# CRLのダウンロードと確認
$ curl -o crl.der http://crl.example.com/ca.crl
$ openssl crl -in crl.der -inform DER -noout -text
OCSP(Online Certificate Status Protocol)
リアルタイムで個別の証明書の失効状態を確認するプロトコル。
# OCSPレスポンダURLの確認
$ openssl x509 -in cert.pem -noout -ocsp_uri
# OCSPリクエストの送信
$ openssl ocsp -issuer issuer.pem -cert server.pem \
-url http://ocsp.example.com -resp_text
OCSP Stapling
サーバーが事前にOCSPレスポンスを取得し、TLSハンドシェイク中にクライアントに提供する。クライアントがOCSPレスポンダに直接問い合わせる必要がなくなる。
# OCSP Staplingの確認
$ openssl s_client -connect www.example.com:443 -status
# OCSP Response Data:
# OCSP Response Status: successful (0x0)
# Response Type: Basic OCSP Response
# ...
# Cert Status: good
Nginxでの設定:
server {
listen 443 ssl;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
5.5 Let's Encryptと自動証明書管理
Let's Encryptは、無料のDV証明書を自動発行するCA。ACME(Automatic Certificate Management Environment)プロトコルを使用する。
# certbotによる証明書の取得(Nginx)
$ sudo certbot --nginx -d www.example.com -d example.com
# 証明書の自動更新
$ sudo certbot renew --dry-run
# 証明書の自動更新(cron)
$ echo "0 3 * * * root certbot renew --quiet" | sudo tee /etc/cron.d/certbot-renew
# 証明書の確認
$ sudo certbot certificates
5.6 CT(Certificate Transparency)
CAが発行したすべての証明書を公開ログに記録し、不正な証明書発行を検知する仕組み。
# SCT(Signed Certificate Timestamp)の確認
$ openssl s_client -connect www.example.com:443 -ct_log_file ct_log_list.cnf
# CTログの検索
# https://crt.sh で証明書を検索可能
第6章: TLSの設定と実装
6.1 Nginxの設定
# /etc/nginx/conf.d/ssl.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
# === 証明書 ===
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
# === プロトコルバージョン ===
ssl_protocols TLSv1.2 TLSv1.3;
# === 暗号スイート ===
# TLS 1.2の暗号スイート
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
# サーバー側の暗号スイート優先
ssl_prefer_server_ciphers on;
# TLS 1.3の暗号スイート(OpenSSL 1.1.1以降)
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
# === セッション管理 ===
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; # チケットの鍵管理が困難なため無効化推奨
# === OCSP Stapling ===
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# === DH パラメータ ===
ssl_dhparam /etc/ssl/dhparam.pem; # 2048ビット以上
# === ECDHカーブ ===
ssl_ecdh_curve X25519:secp256r1:secp384r1;
# === 0-RTT(TLS 1.3) ===
ssl_early_data on;
proxy_set_header Early-Data $ssl_early_data;
# === セキュリティヘッダ ===
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
}
# DHパラメータの生成
$ openssl dhparam -out /etc/ssl/dhparam.pem 2048
# 設定のテスト
$ sudo nginx -t
# Nginxの再読み込み
$ sudo systemctl reload nginx
6.2 Apacheの設定
# /etc/httpd/conf.d/ssl.conf
<VirtualHost *:443>
ServerName www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/fullchain.pem
SSLCertificateKeyFile /etc/ssl/private/privkey.pem
# プロトコルバージョン
SSLProtocol -all +TLSv1.2 +TLSv1.3
# 暗号スイート
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLCipherSuite TLSv1.3 TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
SSLHonorCipherOrder on
# OCSP Stapling
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
# HSTS
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>
SSLStaplingCache shmcb:/var/run/ocsp(128000)
6.3 OpenSSLコマンドリファレンス
# === 接続テスト ===
# TLS接続の確認
$ openssl s_client -connect www.example.com:443
# TLS 1.3での接続
$ openssl s_client -connect www.example.com:443 -tls1_3
# TLS 1.2での接続
$ openssl s_client -connect www.example.com:443 -tls1_2
# 特定の暗号スイートでの接続
$ openssl s_client -connect www.example.com:443 -cipher ECDHE-RSA-AES128-GCM-SHA256
# SNI指定
$ openssl s_client -connect 192.168.1.100:443 -servername www.example.com
# === 証明書操作 ===
# 自己署名証明書の作成(テスト用)
$ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj "/CN=localhost"
# CSR(証明書署名要求)の作成
$ openssl req -new -newkey rsa:2048 -keyout key.pem -out csr.pem -nodes \
-subj "/C=JP/ST=Tokyo/L=Shibuya/O=Example Inc/CN=www.example.com"
# SAN付きCSRの作成
$ openssl req -new -newkey rsa:2048 -keyout key.pem -out csr.pem -nodes \
-subj "/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:example.com"
# ECDSA鍵と証明書の作成
$ openssl ecparam -genkey -name prime256v1 -out ec-key.pem
$ openssl req -new -x509 -key ec-key.pem -out ec-cert.pem -days 365 \
-subj "/CN=www.example.com"
# 証明書の内容確認
$ openssl x509 -in cert.pem -noout -text
# 証明書のフィンガープリント
$ openssl x509 -in cert.pem -noout -fingerprint -sha256
# 秘密鍵と証明書の一致確認
$ openssl x509 -noout -modulus -in cert.pem | openssl md5
$ openssl rsa -noout -modulus -in key.pem | openssl md5
# 両方のMD5が一致すれば鍵ペアが正しい
# PEM → DER 形式変換
$ openssl x509 -in cert.pem -outform DER -out cert.der
# PKCS#12(PFX)形式への変換
$ openssl pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -certfile chain.pem
# === 暗号化テスト ===
# 対応する暗号スイートの確認
$ openssl ciphers -v 'HIGH:!aNULL:!MD5'
# TLS 1.3の暗号スイート確認
$ openssl ciphers -v -tls1_3
# ベンチマーク
$ openssl speed aes-128-gcm aes-256-gcm chacha20-poly1305
6.4 curlでのTLS診断
# 詳細なTLS情報
$ curl -vI https://www.example.com 2>&1 | grep -E "SSL|TLS|subject|issuer|expire"
# TLSバージョンの指定
$ curl --tlsv1.3 https://www.example.com
$ curl --tlsv1.2 --tls-max 1.2 https://www.example.com
# 暗号スイートの指定
$ curl --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://www.example.com
# 証明書検証の無効化(テスト用のみ)
$ curl -k https://self-signed.example.com
# クライアント証明書の使用
$ curl --cert client.pem --key client-key.pem https://mutual-auth.example.com
# 証明書チェーンの詳細
$ curl -w "
TLS Version: %{ssl_version}
Certificate Subject: %{ssl_verify_result}
" -sI https://www.example.com
6.5 testssl.shによるTLS診断
# testssl.shのインストール
$ git clone https://github.com/drwetter/testssl.sh.git
$ cd testssl.sh
# 基本的なスキャン
$ ./testssl.sh www.example.com
# 特定のチェック
$ ./testssl.sh --protocols www.example.com # プロトコル
$ ./testssl.sh --ciphers www.example.com # 暗号スイート
$ ./testssl.sh --vulnerabilities www.example.com # 脆弱性
$ ./testssl.sh --headers www.example.com # HTTPヘッダ
# JSON出力
$ ./testssl.sh --jsonfile result.json www.example.com
第7章: TLSセキュリティのベストプラクティスとTLS攻撃
7.1 プロトコルバージョン
推奨:
✅ TLS 1.3 — 最優先
✅ TLS 1.2 — 後方互換性のために許容
非推奨/禁止:
❌ TLS 1.1 — RFC 8996で禁止
❌ TLS 1.0 — RFC 8996で禁止
❌ SSL 3.0 — POODLE攻撃に脆弱
❌ SSL 2.0 — 多数の脆弱性
7.2 暗号スイートの選択
推奨暗号スイート(優先順):
✅ TLS_AES_128_GCM_SHA256 (TLS 1.3)
✅ TLS_AES_256_GCM_SHA384 (TLS 1.3)
✅ TLS_CHACHA20_POLY1305_SHA256 (TLS 1.3)
✅ ECDHE-ECDSA-AES128-GCM-SHA256 (TLS 1.2)
✅ ECDHE-RSA-AES128-GCM-SHA256 (TLS 1.2)
✅ ECDHE-ECDSA-AES256-GCM-SHA384 (TLS 1.2)
✅ ECDHE-RSA-AES256-GCM-SHA384 (TLS 1.2)
✅ ECDHE-ECDSA-CHACHA20-POLY1305 (TLS 1.2)
✅ ECDHE-RSA-CHACHA20-POLY1305 (TLS 1.2)
避けるべき暗号スイート:
❌ RC4系 — 統計的バイアスが存在
❌ CBC系 — パディングオラクル攻撃に脆弱
❌ 3DES — Sweet32攻撃に脆弱
❌ RSA鍵交換 — 前方秘匿性なし
❌ EXPORT系 — 意図的に弱い暗号
❌ NULL系 — 暗号化なし
❌ MD5/SHA-1ベース — 衝突攻撃が可能
7.3 HSTS(HTTP Strict Transport Security)
ブラウザにHTTPS接続のみを使用するよう指示するHTTPヘッダ。
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-age: 有効期間(秒)— 63072000 = 2年
includeSubDomains: サブドメインにも適用
preload: HSTSプリロードリストへの登録(ブラウザに組み込み)
# HSTSプリロードリストへの登録
# https://hstspreload.org/ で登録申請
7.4 主要なTLS攻撃と対策
BEAST攻撃(Browser Exploit Against SSL/TLS)
- 対象: TLS 1.0のCBCモード
- 手法: CBCの初期化ベクトル(IV)が予測可能な点を悪用
- 対策: TLS 1.1以上の使用、AES-GCMの使用
CRIME/BREACH攻撃
- 対象: TLS圧縮 / HTTP圧縮
- 手法: 圧縮率の差異からデータを推測
- 対策: TLS圧縮の無効化(TLS 1.3では廃止)、HTTP圧縮の注意深い使用
POODLE攻撃(Padding Oracle On Downgraded Legacy Encryption)
- 対象: SSL 3.0のCBCモード
- 手法: CBCパディングの検証欠陥を悪用
- 対策: SSL 3.0の無効化
Heartbleed(CVE-2014-0160)
- 対象: OpenSSL 1.0.1〜1.0.1f
- 手法: TLS Heartbeat拡張のバッファオーバーリードにより、サーバーメモリの内容(秘密鍵を含む可能性)を読み取り
- 対策: OpenSSLのアップデート、秘密鍵の再生成、証明書の再発行
FREAK攻撃(Factoring RSA Export Keys)
- 対象: EXPORT暗号スイートをサポートするサーバー
- 手法: ダウングレード攻撃によりEXPORT暗号(512ビットRSA)を強制
- 対策: EXPORT暗号スイートの無効化
Logjam攻撃
- 対象: 512/768ビットDHパラメータ
- 手法: 弱いDHパラメータを使用してDH鍵交換を攻撃
- 対策: 2048ビット以上のDHパラメータ、ECDHEの使用
ROBOT攻撃(Return Of Bleichenbacher's Oracle Threat)
- 対象: RSA鍵交換(PKCS#1 v1.5パディング)
- 手法: パディングオラクル攻撃
- 対策: RSA鍵交換の無効化、ECDHE鍵交換の使用
Downgrade攻撃
- 手法: 中間者攻撃によりプロトコルバージョンや暗号スイートをダウングレード
- 対策: TLS_FALLBACK_SCSV(RFC 7507)、TLS 1.3のdowngrade防止機構
7.5 Certificate Pinning
アプリケーションレベルで特定の証明書や公開鍵をピン留めし、CAの妥協による偽証明書を防ぐ。
# HTTP Public Key Pinning (HPKP) — 非推奨(Chrome 72で廃止)
# 代わりにCAAレコード、CTログの監視を推奨
# CAA(Certification Authority Authorization)DNSレコード
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 issuewild "letsencrypt.org"
example.com. IN CAA 0 iodef "mailto:security@example.com"
第8章: TLSパフォーマンス最適化
8.1 TLSのパフォーマンスオーバーヘッド
レイテンシのオーバーヘッド:
TLS 1.2: 2 RTT(フルハンドシェイク)/ 1 RTT(再開)
TLS 1.3: 1 RTT(フルハンドシェイク)/ 0 RTT(再開)
CPU オーバーヘッド:
RSA-2048署名: ~1ms(ハードウェアアクセラレーション有り)
ECDSA-P256署名: ~0.1ms
AES-GCM暗号化: AES-NIで ~1GB/s
ChaCha20-Poly1305: ~500MB/s(ソフトウェア)
帯域幅のオーバーヘッド:
TLSレコードヘッダ: 5バイト
AES-GCMタグ: 16バイト
パディング: 最大255バイト(TLS 1.3のレコードパディング)
8.2 セッション再開の最適化
# Nginx: セッションキャッシュの設定
ssl_session_cache shared:SSL:50m; # 50MBの共有メモリ(約20万セッション)
ssl_session_timeout 1d; # セッションの有効期間
# セッションチケットの鍵ローテーション
# 鍵ファイルの生成(48バイト = AES-256 + HMAC-SHA-256)
$ openssl rand 48 > /etc/ssl/ticket-key-current.pem
$ openssl rand 48 > /etc/ssl/ticket-key-previous.pem
# Nginx設定
ssl_session_ticket_key /etc/ssl/ticket-key-current.pem;
ssl_session_ticket_key /etc/ssl/ticket-key-previous.pem;
# 前回の鍵も保持することで、鍵ローテーション時のセッション断を防止
8.3 OCSP Staplingの最適化
# OCSPレスポンスのキャッシュサイズ確認
# Nginx: ssl_stapling_verify on;
# レスポンスは自動的にキャッシュされる
# OCSPレスポンスの有効期間確認
$ openssl ocsp -issuer issuer.pem -cert server.pem \
-url http://ocsp.example.com -resp_text | grep "Next Update"
8.4 TLS 1.3 0-RTTの活用
# Nginx: 0-RTTの有効化
ssl_early_data on;
# バックエンドへの0-RTTデータの転送
proxy_set_header Early-Data $ssl_early_data;
# バックエンド側でリプレイ攻撃対策
# Early-Data: 1 ヘッダが存在する場合、冪等でないリクエストは拒否
8.5 ハードウェアアクセラレーション
# AES-NIの有無確認
$ grep -o aes /proc/cpuinfo | head -1
aes
# OpenSSLのエンジン確認
$ openssl engine
(rdrand) Intel RDRAND engine
(dynamic) Dynamic engine loading support
# AES-GCMのベンチマーク
$ openssl speed -evp aes-128-gcm
# AES-NI有り: ~4 GB/s
# AES-NI無し: ~500 MB/s
8.6 証明書の最適化
証明書サイズの比較:
RSA-2048証明書: ~1KB
RSA-4096証明書: ~2KB
ECDSA-P256証明書: ~0.5KB
推奨:
1. ECDSA証明書を使用(サイズが小さく、署名/検証が高速)
2. 証明書チェーンを最小限に保つ(不要な中間証明書を省く)
3. デュアルスタック(RSA + ECDSA)で互換性を確保
# デュアル証明書の設定(Nginx 1.11.0以降)
ssl_certificate /etc/ssl/ecdsa-fullchain.pem;
ssl_certificate_key /etc/ssl/ecdsa-privkey.pem;
ssl_certificate /etc/ssl/rsa-fullchain.pem;
ssl_certificate_key /etc/ssl/rsa-privkey.pem;
第9章: mTLS(相互TLS認証)
9.1 mTLSの概要
通常のTLSではサーバーのみが認証されるが、mTLS(mutual TLS)ではクライアントも証明書で認証される。マイクロサービス間通信、API認証、ゼロトラストネットワークで使用。
通常のTLS:
クライアント ──→ サーバー証明書で認証 ──→ サーバー
mTLS:
クライアント ←── サーバー証明書で認証 ──→ サーバー
クライアント ──→ クライアント証明書で認証 → サーバー
9.2 mTLSの設定
# Nginx: mTLSの設定
server {
listen 443 ssl;
# サーバー証明書
ssl_certificate /etc/ssl/server-cert.pem;
ssl_certificate_key /etc/ssl/server-key.pem;
# クライアント認証
ssl_client_certificate /etc/ssl/client-ca.pem; # クライアント証明書のCA
ssl_verify_client on; # 必須認証
# ssl_verify_client optional; # オプション認証
ssl_verify_depth 2; # 証明書チェーンの深さ
# クライアント証明書情報のバックエンドへの転送
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
}
9.3 サービスメッシュでのmTLS
# Istio: mTLSの設定
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT # すべてのサービス間通信にmTLSを強制
第10章: TLSの最新動向と将来
10.1 Encrypted Client Hello(ECH)
TLS 1.3のClientHelloを暗号化し、SNIの盗聴を防止する拡張。
従来: ClientHello → SNI(平文)→ 接続先ホスト名が見える
ECH: ClientHello → 外部ClientHello(ダミーSNI)+ 内部ClientHello(暗号化されたSNI)
→ 接続先ホスト名が保護される
10.2 Post-Quantum TLS
量子コンピュータによる暗号解読に備えた暗号アルゴリズムの標準化が進行中。
ポスト量子暗号アルゴリズム(NIST標準化):
- ML-KEM(Kyber): 鍵カプセル化メカニズム(鍵交換用)
- ML-DSA(Dilithium): デジタル署名
- SLH-DSA(SPHINCS+): ハッシュベースの署名
ハイブリッドアプローチ:
TLSで従来の鍵交換(ECDHE)とポスト量子鍵交換を組み合わせる
例: X25519 + ML-KEM-768
# OpenSSLでのポスト量子サポート確認(OQS Provider使用時)
$ openssl list -kem-algorithms | grep -i kyber
10.3 TLS over QUIC
QUIC(HTTP/3のトランスポート)は、TLS 1.3をUDP上に統合している。TCP+TLSの代替として急速に普及中。
従来: TCP 3ウェイハンドシェイク(1 RTT) + TLS 1.3ハンドシェイク(1 RTT) = 2 RTT
QUIC: QUIC + TLS 1.3 統合ハンドシェイク = 1 RTT(0-RTTも可能)
まとめ
TLSはインターネットセキュリティの根幹であり、その正しい理解と設定は全てのエンジニアに求められる。本記事の要点:
- 暗号技術の基礎: 対称暗号(AES-GCM)、公開鍵暗号(ECDSA)、鍵交換(ECDHE)、AEAD
- TLS 1.2: 2 RTTのハンドシェイク、セッション再開
- TLS 1.3: 1 RTTハンドシェイク、0-RTT、前方秘匿性必須、脆弱アルゴリズムの廃止
- PKI/証明書: X.509構造、チェーン検証、OCSP Stapling、Let's Encrypt
- 設定実践: Nginx/Apache設定、OpenSSLコマンド
- セキュリティ: BEAST/POODLE/Heartbleed等の攻撃と対策、HSTS
- パフォーマンス: セッション再開、0-RTT、ハードウェアアクセラレーション、ECDSA証明書
- mTLS: 相互認証、サービスメッシュ
- 将来: ECH、ポスト量子暗号、QUIC
参考文献
- RFC 5246 - The Transport Layer Security (TLS) Protocol Version 1.2 (2008)
- RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 (2018)
- RFC 6066 - TLS Extensions: Extension Definitions (2011)
- RFC 6961 - TLS Multiple Certificate Status Request Extension (2013)
- RFC 7301 - TLS Application-Layer Protocol Negotiation Extension (2014)
- RFC 7507 - TLS Fallback Signaling Cipher Suite Value (2015)
- RFC 7525 - Recommendations for Secure Use of TLS and DTLS (2015)
- RFC 8996 - Deprecating TLS 1.0 and TLS 1.1 (2021)
- RFC 9325 - Recommendations for Secure Use of TLS and DTLS (2022)
- "Bulletproof SSL and TLS" - Ivan Ristić
- Mozilla SSL Configuration Generator: https://ssl-config.mozilla.org/
- SSL Labs Server Test: https://www.ssllabs.com/ssltest/