図解！SSL/TLSを支える「鍵」の役割と仕組み

:::note warn
※ 筆者は今までフロントエンドエンジニアとしての活動が多く、本記事で取り上げたSSL/TLSのようなトピックについては、学習を始めたばかりです。そのため、内容に誤りや説明が不十分な点などがございましたら、コメントにてご指摘いただけますと幸いです。
いただいたコメントをもとに、私自身も勉強をしながらアップデートしていけたら、という記事です。
:::

## SSL/TLSにおける鍵の役割

インターネット上の通信は、たとえるなら「たくさんの人が行き交う大通り」のようなものです。重要な情報（クレジットカード番号、ログインパスワードなど）をそのまま送ってしまうと、悪意のある第三者に盗み見られたり、内容を改ざんされたりする危険があります。

SSL/TLS（Transport Layer Security、その前身がSSL）は、これらの危険から通信を守り、ウェブサイトとブラウザの間などで安全な通信路を確立するための仕組みです。SSL/TLSは、 **「鍵」** という要素を使って、以下の3つの安全性を実現します。

1. **秘匿性（Confidentiality）**: 通信内容を第三者に盗み見られないように暗号化する
1. **完全性（Integrity）**: 通信内容が途中で改ざんされていないことを確認する
1. **認証（Authentication）**: 通信相手が正しい相手であることを確認する（特にサーバー側）

これらの安全性は、複数の種類の鍵と暗号技術を組み合わせることで実現されています。

## SSL/TLSで使われる「鍵」の種類

SSL/TLSでは、主に以下の3種類の鍵と、それを補う技術としてハッシュ関数が使われます。

### 1. 公開鍵暗号方式（非対称鍵）

- **特徴:** **ペアとなる異なる二つの鍵（公開鍵と秘密鍵）**を使用する暗号技術です。主に以下の機能を提供します
  - **暗号化・複合: ** 一方の鍵（通常は公開鍵）で暗号化したデータは、ペアとなるもう一方の鍵（秘密鍵）でしか復号できません。これにより、第三者からの盗聴を防ぎます
  - **デジタル署名: ** 秘密鍵の所有者であることを証明したり、データが改ざんされていないことを保証したりするために使われます
  - **鍵交換の基盤: ** Diffie-Hellman (DH) や Elliptic Curve Diffie-Hellman (ECDH) のように、安全に共通鍵を生成するためのプロトコルにも利用されます
  - **公開鍵:** 広く公開され、誰でも入手できる鍵
  - **秘密鍵:** 鍵の所有者だけが厳重に管理し、他人には知られないようにする鍵
- **用途:** 主に「鍵交換」や「デジタル署名（認証）」に使われる。処理速度は共通鍵暗号に比べて遅い
- **代表的なアルゴリズム:** RSA、Diffie-Hellman (DH)、Elliptic Curve Diffie-Hellman (ECDH) など

#### [公開鍵暗号のイメージ]

```mermaid
graph LR
    M1("メッセージ(M)") --> Encrypt("公開鍵で暗号化")
    Encrypt --> C("暗号文(C)")
    C -->|誰でもOK| Decrypt("秘密鍵で復号化")
    Decrypt --> M2("メッセージ(M)")

    PublicKey("相手の公開鍵<br/>（インターネットなどで公開）")
    PrivateKey("自分の秘密鍵<br/>（自分だけが持つ）")

    PublicKey --> Encrypt
    PrivateKey --> Decrypt

    classDef square rect;
    class M1,C,M2,PublicKey,PrivateKey square;
```
用途①：秘匿性を持った通信（AさんがBさんの公開鍵で暗号化し、Bさんが自分の秘密鍵で復号）

```mermaid
graph LR
    M1("メッセージ(M)") --> DigitalSign("自分の秘密鍵でデジタル署名")
    DigitalSign --> S("署名(S)")
    S -->|誰でもOK| Verification("自分の公開鍵で署名検証")
    Verification --> V1("検証(OK/NG)")

    PublicKey("自分の公開鍵<br/>（インターネットなどで公開）")
    PrivateKey("自分の秘密鍵<br/>（自分だけが持つ）")

    PrivateKey --> DigitalSign
    PublicKey --> Verification

    classDef square rect;
    class M1,S,V1,PublicKey,PrivateKey square;
```
用途②：デジタル署名（メッセージの要約値（ハッシュ値）を送信者の秘密鍵で処理し、受信者が送信者の公開鍵で検証）
→ 「そのメッセージが確かに秘密鍵の所有者によって承認され、かつ途中で改ざんされていない」ことを証明

### 2. 共通鍵暗号方式（対称鍵）

- **特徴:** **暗号化と復号に全く同じ鍵**を使用
- **用途:** 実際の通信データ（メッセージ本体）の暗号化に使われます。公開鍵暗号に比べて処理速度が非常に高速。ただし、通信相手と安全に共通鍵を共有する必要があるという課題がある
- **代表的なアルゴリズム:** AES、3DESなど

#### [共通鍵暗号のイメージ]

```mermaid
graph LR
    M1("メッセージ(M)") --> Encrypt("共通鍵で暗号化")
    Encrypt --> C("暗号文(C)")
    C -->|誰でもOK| Decrypt("共通鍵で復号化")
    Decrypt --> M2("メッセージ(M)")

    CommonKey1("同じ共通鍵<br/>（通信する両者だけが知っている）")
    CommonKey2("同じ共通鍵<br/>（通信する両者だけが知っている）")

    CommonKey1 --> Encrypt
    CommonKey2 --> Decrypt

    classDef square rect;
    class M1,C,M2,CommonKey1,CommonKey2 square;
```

### 3. ハッシュ関数（鍵ではないが、完全性確保に重要）

- **特徴:** 入力データから、決まった長さの全く異なる値（ハッシュ値、ダイジェスト、フィンガープリントなどと呼ばれる）を計算する一方向の関数
    - 入力が少しでも変わると、出力されるハッシュ値は大きく異なる
    - ハッシュ値から元の入力データを復元することは非常に困難
- **用途:** 入力データから固定長のハッシュ値を生成する特性を活かし、主に以下のような目的で利用されます
  - **データの完全性検証:** データが送信中や保存中に改ざんされていないかを確認するために使われます（例: HMAC）
  - **デジタル署名:** メッセージのハッシュ値に対して署名処理を行うことで、署名対象のデータサイズを小さくし、効率的に署名・検証を行えるようにします
  - **パスワードの保存:** パスワードそのものではなく、パスワードのハッシュ値を保存することで、万が一データベースが漏洩しても元のパスワードが直接知られるリスクを低減します
  - **鍵導出 (Key Derivation):** マスターキーやパスワードなどから、実際の暗号化に使用するセッションキーなどを生成する際にも利用されます（例: HKDF - HMAC-based Key Derivation Function）。TLSのハンドシェイクでも鍵生成に関わります
  - **データの一意性の識別:** データのフィンガープリントとして、重複を検出したり、特定のデータブロックを識別したりするのに使われます

#### [ハッシュ関数のイメージ]

```mermaid
graph LR
    D1("任意の長さのデータ") --> HashFunc("ハッシュ関数")
    HashFunc --> HashValue("固定の短い長さのハッシュ値")

    classDef square rect;
    class D1,HashFunc,HashValue square;
```
例：
"apple"      --> SHA-256 --> 3a44f2c...
"apple pie"  --> SHA-256 --> b1f40e8... (全く違う値になる)

---

## SSL/TLSの鍵を使った通信の流れ

SSL/TLS通信は、まず「ハンドシェイク」という初期設定の段階で、安全に通信するための準備（特に共通鍵の共有と認証）を行い、その後に実際のデータ通信が始まります。

### 1. ハンドシェイク（鍵交換と認証）

この段階で最も重要なのが、これから大量のデータを暗号化するために使う「共通鍵」を、通信相手と安全に共有することです。公開鍵暗号や鍵交換アルゴリズムがここで活躍します。また、通信相手（特にサーバー）が本物であるかの認証も行います。

SSL/TLSで使われる代表的な鍵交換方式をいくつかご紹介します。

#### a) RSA鍵交換

古いバージョンのSSL/TLSや、一部の設定で使われるシンプルながら課題のある方式です。

1. クライアントはサーバーに接続します
1. サーバーは、自分の「サーバー証明書」をクライアントに送ります。この証明書には、サーバーの「公開鍵」が含まれています
1. クライアントは、受け取った証明書が信頼できる認証局(CA)によって発行されたものであり、改ざんされていないこと、そして証明書に含まれる公開鍵が本物であることを確認します（後述の認証の仕組みを使います）
1. クライアントは、これからデータ暗号化に使う「共通鍵」を自分で生成します
1. クライアントは、**サーバーから受け取った「公開鍵」を使って、生成した共通鍵を暗号化**します
1. クライアントは、暗号化した共通鍵をサーバーに送ります
1. サーバーは、暗号化された共通鍵を、**自分だけが持っている「秘密鍵」を使って復号**し、共通鍵を得ます
1. これでクライアントとサーバーは同じ共通鍵を持つことになります

**[RSA鍵交換のイメージ]**

```mermaid
sequenceDiagram
    participant クライアント
    participant サーバ

    クライアント->>サーバ: Hello, Server!
    サーバ->>クライアント: Server Certificate (公開鍵含む)
    Note left of クライアント: サーバ認証も行う

    Note left of クライアント: 共通鍵を生成 (例: K)<br/>Kをサーバ公開鍵で暗号化 (Enc(K, 公開鍵))

    クライアント->>サーバ: Enc(K, 公開鍵)

    Note right of サーバ: Enc(K, 公開鍵) を秘密鍵で復号 → K を得る

    Note over クライアント,サーバ: 共通鍵 K を使用する準備完了
```

**欠点（Perfect Forward Secrecyがない）:**
この方式の大きな課題は、サーバーの「秘密鍵」が万が一漏洩してしまうと、**過去にその秘密鍵で暗号化された共通鍵がすべて復号できてしまう**ことです。これにより、過去の通信内容がすべて第三者に読まれてしまいます。これを防ぐのが次の方式です。

#### b) Diffie-Hellman (DH) / Ephemeral Diffie-Hellman (DHE)

Diffie-Hellman (DH) は、安全でない通信路を使って、通信を行う二者が共通の秘密の鍵（共通鍵）を安全に合意（生成）するための鍵交換プロトコルです。アルゴリズムとしての発明はRSAよりも古く、SSL/TLSにおいてもSSLv3の時代から採用されています。
SSL/TLSでは、通信セッションごとに新しい一時的な鍵ペアを生成して鍵交換を行う「Ephemeral」（一時的、使い捨ての）な方式である **DHE (Diffie-Hellman Ephemeral)** がよく使われます。DHEは、RSA鍵交換方式が持つ「サーバーの秘密鍵が漏洩すると過去の通信も解読される」という課題（Perfect Forward Secrecyがない点）を解決する手段として、特に重要視されています。SSL/TLSでは、通信ごとに新しい鍵を生成する「Ephemeral」（一時的、使い捨ての）な方式である **DHE** がよく使われます。

DHEは、数学の「離散対数問題」という解くのが非常に難しい問題を応用しています。詳細な計算式は難しいですが、概念的には以下の流れです。

1. サーバーとクライアントは、公開されている共通のパラメータ（大きな数など）を決めます
1. クライアントは、**自分だけが知っている「秘密の値A」** を選びます
1. サーバーは、**自分だけが知っている「秘密の値B」** を選びます
1. クライアントは、共通パラメータと秘密の値Aを使って「公開の値a」を計算し、サーバーに送ります
1. サーバーは、共通パラメータと秘密の値Bを使って「公開の値b」を計算し、クライアントに送ります
1. クライアントは、サーバーから受け取った「公開の値b」と、**自分の「秘密の値A」** を使って、共通鍵 K を計算します
1. サーバーは、クライアントから受け取った「公開の値a」と、**自分の「秘密の値B」** を使って、同じ共通鍵 K を計算します

**ポイント:**
- インターネット上には「共通パラメータ」「公開の値a」「公開の値b」が流れますが、これらから「秘密の値A」や「秘密の値B」を知ることは（離散対数問題が困難であるため）非常に難しいです
- そして、「公開の値a」「公開の値b」と「共通パラメータ」だけから共通鍵Kを知ることもできません
- 共通鍵 K を計算するには、相手の「公開の値」と、**自分の「秘密の値」** が必要になります
- **Ephemeral (DHE):** 通信セッションごとに新しい秘密の値AとBを選び、それに基づいて新しい共通鍵Kを生成します

**[DHE鍵交換のイメージ (概念)]**

```mermaid
sequenceDiagram
    participant クライアント
    participant サーバ

    クライアント->>サーバ: ClientHello (DHE希望)

    サーバ->>クライアント: ServerHello (DHE同意)
    サーバ->>クライアント: Server Certificate + DHパラメータ
    Note right of サーバ: 認証とパラメータを送る

    Note left of クライアント: 秘密の値Aを生成<br/>公開の値aを計算 (パラメータ + 秘密の値A)
    Note right of サーバ: 秘密の値Bを生成<br/>公開の値bを計算 (パラメータ + 秘密の値B)

    クライアント->>サーバ: クライアントDHパラメータ (a)

    Note left of クライアント: 公開の値bと秘密の値Aから共通鍵 K を計算
    Note right of サーバ: 公開の値aと秘密の値Bから共通鍵 K を計算

    Note over クライアント,サーバ: (それぞれの計算式は異なるが結果は同じ K)

    Note over クライアント,サーバ: 共通鍵 K を使用する準備完了
```

**利点（Perfect Forward Secrecy: PFS）:**
DHEでは、共通鍵 K は通信セッションごとに新しく生成され、サーバーの「秘密鍵」とは直接関係がありません（サーバーの秘密鍵は認証にのみ使われます）。そのため、仮にサーバーの秘密鍵が漏洩しても、過去の通信で使われた共通鍵 K を復元することはできません。これが **Perfect Forward Secrecy (PFS)** と呼ばれる重要な性質です。これにより、過去に傍受された暗号化通信が、将来秘密鍵が漏洩しても安全であるという「フォワードセキュリティ」が提供されます。

#### c) Elliptic Curve Diffie-Hellman (ECDH) / Ephemeral ECDH (ECDHE)

DHEと同じ考え方を、**楕円曲線暗号**という別の数学的仕組みに応用したものです。SSL/TLSでは、一時的な鍵を使う **ECDHE** が現代の標準的な鍵交換方式となっています。

1. 仕組みはDHEと非常に似ています。共通パラメータとして「楕円曲線の定義」や「基準点」を使います
1. クライアントは秘密の値Aを選び、楕円曲線上の計算で公開の値a（楕円曲線上の点）を計算し送ります
1. サーバーは秘密の値Bを選び、楕円曲線上の計算で公開の値b（楕円曲線上の点）を計算し送ります
1. クライアントは公開の値bと秘密の値Aから、サーバーは公開の値aと秘密の値Bから、同じ共通鍵Kを計算します

**[ECDHE鍵交換のイメージ (概念)]**

```mermaid
sequenceDiagram
    participant クライアント
    participant サーバー

    クライアント->>サーバー: ClientHello (ECDHE希望)

    サーバー->>クライアント: ServerHello (ECDHE同意)
    サーバー->>クライアント: Server Certificate + ECパラメータ
    Note right of サーバー: 認証とパラメータを送る

    Note left of クライアント: 秘密の値Aを生成<br/>公開の値a (EC上の点) を計算 (パラメータ + 秘密の値A)
    Note right of サーバー: 秘密の値Bを生成<br/>公開の値b (EC上の点) を計算 (パラメータ + 秘密の値B)

    クライアント->>サーバー: クライアントECパラメータ (a)

    Note left of クライアント: 公開の値bと秘密の値Aから共通鍵 K を計算
    Note right of サーバー: 公開の値aと秘密の値Bから共通鍵 K を計算

    Note over クライアント,サーバー: (それぞれの計算式は異なるが結果は同じ K)

    Note over クライアント,サーバー: 共通鍵 K を使用する準備完了
```

**利点:**
- DHEと同様に **Perfect Forward Secrecy (PFS)** を提供します
- DHEに比べて、**より短い鍵長（秘密の値）で同等のセキュリティ強度**を実現できます。これは、楕円曲線暗号が応用している数学問題（楕円曲線上の離散対数問題）が、通常の離散対数問題よりも解くのが難しいとされているためです
- そのため、通信速度や計算負荷の面でDHEよりも効率が良いことが多いです

現代のSSL/TLS通信の多くは、PFSを提供し効率の良い **ECDHE** 方式で共通鍵の交換（生成）を行っています。

#### 認証の仕組み（デジタル署名）

ハンドシェイクの初期段階で、クライアントはサーバーから送られてくる「サーバー証明書」を受け取ります。この証明書を使って、通信相手が本物のサーバーであることを確認します。

サーバー証明書には、サーバーの公開鍵やドメイン名などが含まれており、**信頼できる第三者機関である「認証局（CA）」が、証明書の内容が正しいことを保証するために、CAの「秘密鍵」でデジタル署名**を施しています。

クライアントは、あらかじめブラウザなどに組み込まれている「信頼できるCAの公開鍵」を使って、サーバー証明書のデジタル署名を検証します。

**[デジタル署名による認証のイメージ]**

```mermaid
graph LR
    subgraph "サーバー (証明書発行時)"
        ServerCertContent("サーバー証明書の内容<br/>(サーバー公開鍵, ドメイン等)") --> CASign("CAの秘密鍵で署名") --> Signature("署名データ")
    end

    subgraph "クライアント (接続時)"
        ReceivedCert("サーバー証明書<br/>(署名データ含む)") --> Verify("署名データと証明書内容を検証")
        TrustedCAPubKey("信頼できるCAの公開鍵<br/>(ブラウザ等に事前登録)") --> Verify
        Verify --> ValidationResult("検証結果 (OK/NG)")
    end

    classDef square rect;
    class ServerCertContent,CASign,Signature,ReceivedCert,Verify,ValidationResult,TrustedCAPubKey square;
```
検証OKなら：「この証明書は信頼できるCAが発行し、内容は改ざんされていない。含まれる公開鍵は本物だ」と判断。

この認証プロセス（証明書の検証と、サーバー秘密鍵による署名の検証）により、クライアントは通信相手のサーバーが正当なものであると確信できます。この信頼関係に基づいて、安全な鍵交換を行い、その後の暗号化通信の安全性が担保されます。

**補足:** TLSのバージョン（特にTLS 1.2以前とTLS 1.3）によって、ハンドシェイクにおける認証、鍵交換、暗号化開始の厳密な順序やタイミングには違いがあります。例えば、TLS 1.3では、一部の暗号化をより早期に開始し、効率化を図る工夫がなされています。

**サーバー認証におけるサーバー秘密鍵の役割 (特に DHE/ECDHE の場合):**
クライアントがサーバー証明書を検証することで、証明書に含まれるサーバーの公開鍵が信頼できるCAによって保証されたものであることを確認できます。
さらに、DHEやECDHEのような鍵交換方式では、サーバーはその証明書に対応する **秘密鍵** を使って、鍵交換プロセスでやり取りされる重要な情報（例: サーバーが生成したDH/ECDHの公開パラメータや、ハンドシェイクメッセージの一部）に対して **デジタル署名** を行います。

1. サーバーは、鍵交換に必要な自身のパラメータ（例: DH公開値 b）と、その他の関連情報をまとめます
2. サーバーは、このまとめられた情報に対して、 **自身の証明書に対応する秘密鍵でデジタル署名** を生成します
3. サーバーは、自身の証明書、鍵交換パラメータ、そしてこのデジタル署名をクライアントに送ります
4. クライアントは、受け取ったサーバー証明書を検証し、その証明書内の公開鍵を使って、同時に送られてきたデジタル署名を検証します

この署名検証が成功することで、クライアントは以下の2点を確認できます。

- **サーバーが本物であること:** 正しい秘密鍵を持つサーバーだけが有効な署名を作成できるため、なりすましではないことを確認できます
- **鍵交換パラメータの完全性:** 署名された鍵交換パラメータが途中で改ざんされていないことを確認できます

このように、証明書の検証（CAによる信頼性の担保）と、サーバー秘密鍵による署名（現在の通信相手がその証明書の正当な所有者であることの証明）の組み合わせによって、クライアントは通信相手のサーバーを認証します。これにより、クライアントは鍵交換で得られた共通鍵を、確かに認証されたサーバーとの間で共有していると確信できます。

### 2. データ通信（共通鍵暗号）

ハンドシェイクが成功し、クライアントとサーバーが同じ「共通鍵」を安全に共有できたら、いよいよ実際のデータ通信が始まります。

この段階では、ハンドシェイクで共有した共通鍵を使って、**すべての通信データ（リクエストやレスポンスの本体）を共通鍵暗号方式で暗号化・復号**します。

**[共通鍵によるデータ暗号化・復号のイメージ]**

```mermaid
sequenceDiagram
    participant クライアント
    participant サーバー

    Note left of クライアント: メッセージM1を共通鍵Kで暗号化(C1)
    クライアント->>サーバー: C1

    Note right of サーバー: C1を共通鍵Kで復号 → M1 を得る<br/>応答メッセージM2を共通鍵Kで暗号化(C2)
    サーバー->>クライアント: C2

    Note left of クライアント: C2を共通鍵Kで復号 → M2 を得る

    Note over クライアント,サーバー: （このやり取りを高速に繰り返す）
```

共通鍵暗号は公開鍵暗号に比べて非常に高速なため、大量のデータを効率的に暗号化・復号するのに適しています。

### 3. 完全性（Integrity）の確保

データが通信中に改ざんされていないことを確認するために、メッセージ認証符号（MAC: Message Authentication Code）という技術が用いられます。MACは、メッセージと共通鍵を基に計算される短い固定長のデータです。

1. **送信側:** 送信するメッセージと共有している共通鍵を使って、MAC値を計算します。そして、メッセージと計算したMAC値を一緒に送信します
2. **受信側:** 受け取ったメッセージと **同じ共通鍵** を使って、送信側と全く同じ方法でMAC値を再計算します
計算し直したMAC値と、受け取ったMAC値を比較します。
これらが一致すれば、メッセージは改ざんされていないと判断できます。共通鍵を知らない第三者は、メッセージを改ざんしても正しいMAC値を再計算できないため、改ざんを検知できます。

**MACの実現方法:**

- **HMAC (Hash-based MAC):** ハッシュ関数（SHA-256など）と共通鍵を組み合わせてMAC値を生成する代表的な方法の一つです。過去のTLSバージョンで広く使われていました
- **CMAC (Cipher-based MAC):** AESのようなブロック暗号アルゴリズムをベースにMAC値を生成する方法もあります

**現代のTLSにおける完全性確保: AEAD (認証付き暗号)**

現代のSSL/TLSでは、 **AEAD (Authenticated Encryption with Associated Data)** と呼ばれる暗号モードが主流です。AEADは、 **データの暗号化（秘匿性）とメッセージ認証（完全性および認証性）を単一の処理で同時に行う** ことができる効率的かつ安全な方式です。

AEADを用いると、暗号化処理の一部としてMAC値の計算と検証が組み込まれるため、別途HMACのような仕組みを適用する必要がありません。代表的なAEADアルゴリズムには、AES-GCM (Galois/Counter Mode) や ChaCha20-Poly1305 などがあります。

これにより、SSL/TLS通信では、共通鍵を使ってデータを暗号化すると同時に、そのデータが改ざんされていないことも保証されます。

---

## まとめ

SSL/TLSは、これらの異なる種類の鍵と技術を組み合わせて、インターネット通信の安全性を確保しています。

- まず、**公開鍵暗号（またはDHE/ECDHE鍵交換）**を使って、これから使う**共通鍵を安全に共有**します
- 同時に、**デジタル署名**を使って**通信相手（サーバー）が本物であるかを確認**します
- 安全に共有できた**共通鍵**を使って、**実際の通信データを高速に暗号化・復号**します
- **ハッシュ関数と共通鍵**を組み合わせたMACで、**データの改ざんがないかを確認**します

特に、DHEやECDHEといった鍵交換方式は、通信セッションごとに新しい共通鍵を生成することで、サーバーの秘密鍵が将来漏洩しても過去の通信が安全であるという **Perfect Forward Secrecy (PFS)** を提供しており、現代のSSL/TLS通信において非常に重要な役割を果たしています。

このように、SSL/TLSは単一の技術ではなく、複数の暗号技術を効果的に組み合わせることで、複雑なインターネット環境下でも安全な通信を実現しています。