- Authors
- Name
- はじめに
- IPsecとは?
- 核心概念
- ESP(Encapsulating Security Payload)— 核心
- IKE(Internet Key Exchange)— 鍵ネゴシエーション
- 実務: AWS Site-to-Site VPN
- IPsec vs WireGuard vs OpenVPN

はじめに
VPNをオンにするとインターネットが遅くなることは知っているけど、なぜ遅くなるか知っていますか?それは内部でIPsecというプロトコルがすべてのパケットを暗号化し、認証し、トンネリングしているからです。
IPsecはネットワークエンジニアだけでなく、クラウドアーキテクト(VPCピアリング、Site-to-Site VPN)、DevOpsエンジニア(WireGuard vs IPsec)、セキュリティエンジニアすべてにとって必須の知識です。
IPsecとは?
IPsec (IP Security) = IP層(L3)で動作するセキュリティプロトコルスイート
構成要素:
├── AH (Authentication Header): 認証のみ(完全性 + 送信元確認)
├── ESP (Encapsulating Security Payload): 暗号化 + 認証(実務で99%)
├── IKE (Internet Key Exchange): 鍵交換 + SAネゴシエーション
└── SA (Security Association): セキュリティ接続情報の格納庫
核心概念
SA(Security Association)— セキュリティ接続の契約書
SA = 2つのデバイス間の「セキュリティ合意書」
含まれる情報:
├── SPI (Security Parameter Index): SA識別子(32bit)
├── 暗号化アルゴリズム: AES-256-GCM
├── 認証アルゴリズム: SHA-256 HMAC
├── 鍵の値: 共有秘密鍵
├── 有効期間: 3600秒または100GB
└── モード: トンネル / トランスポート
SAは一方向 → 双方向通信にはSAが2つ必要!
トンネルモード vs トランスポートモード
[トランスポートモード(Transport Mode)]
ホスト間の直接通信
元のIPヘッダーを保持、ペイロードのみ暗号化
┌──────────┬──────────┬────────────────┐
│ IP Header│ESP Header│ 暗号化された │
│ (元の) │ │ Payload │
└──────────┴──────────┴────────────────┘
[トンネルモード(Tunnel Mode)] ← Site-to-Site VPN
ゲートウェイ間(元のパケット全体を包む)
新しいIPヘッダーを追加、元のIPも暗号化!
┌──────────┬──────────┬─────────────────────────┐
│ 新しいIP │ESP Header│ 暗号化された │
│ Header │ │ [元のIP + Data] │
└──────────┴──────────┴─────────────────────────┘
実務でトンネルモードが重要な理由: 内部ネットワークのIPアドレスが外部に露出しない!
ESP(Encapsulating Security Payload)— 核心
ESPパケット構造:
┌─────────────────────────────────────────────┐
│ IP Header(新しい、トンネルモード) │
├─────────────────────────────────────────────┤
│ ESP Header │
│ ├── SPI (32bit): どのSAを使うか │
│ └── Sequence Number (32bit): リプレイ防止 │
├─────────────────────────────────────────────┤
│ 暗号化領域(Encrypted) │
│ ├── 元のIP Header(トンネルモード) │
│ ├── 元のPayload(TCP/UDP + Data) │
│ └── ESP Trailer(Padding + Next Header) │
├─────────────────────────────────────────────┤
│ ESP Auth (ICV): HMAC-SHA256(完全性検証) │
└─────────────────────────────────────────────┘
# ESP暗号化プロセス(疑似コード)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def esp_encrypt(original_packet, sa):
"""ESP暗号化(AES-256-GCM)"""
# 1. SAから鍵とアルゴリズムを取得
key = sa.encryption_key # 256-bit
spi = sa.spi
seq = sa.next_sequence_number()
# 2. ESP Header生成
esp_header = spi.to_bytes(4, 'big') + seq.to_bytes(4, 'big')
# 3. Padding追加(ブロックサイズに合わせる)
pad_len = (16 - (len(original_packet) + 2) % 16) % 16
padding = bytes(range(1, pad_len + 1))
esp_trailer = padding + bytes([pad_len, 4]) # 4 = IP-in-IP
# 4. 暗号化(AES-256-GCM)
nonce = os.urandom(12)
aesgcm = AESGCM(key)
plaintext = original_packet + esp_trailer
ciphertext = aesgcm.encrypt(nonce, plaintext, esp_header) # AAD = ESP Header
# 5. 新しいIP Header + ESP Header + 暗号化データ
new_ip = create_ip_header(sa.tunnel_src, sa.tunnel_dst)
return new_ip + esp_header + nonce + ciphertext
IKE(Internet Key Exchange)— 鍵ネゴシエーション
IPsecで最も複雑な部分!2つのフェーズに分かれます。
Phase 1(IKE SA)— 「お互いを信頼できるか?」
目的: 安全なチャネルの確立(以降のPhase 2を保護)
[Initiator] [Responder]
│ │
│──── SA Proposal ────────────▶│ 暗号化/ハッシュ/DHグループ提案
│◀─── SA Accepted ────────────│ 合意
│ │
│──── DH Public Value ────────▶│ Diffie-Hellman鍵交換
│◀─── DH Public Value ────────│ 双方が同じ秘密鍵を導出!
│ │
│──── Auth (PSK/Cert) ────────▶│ 事前共有鍵または証明書
│◀─── Auth ────────────────────│ 相互認証
│ │
╔══════════════════════════════╗
║ IKE SA確立(暗号化チャネル) ║
╚══════════════════════════════╝
Phase 2(IPsec SA)— 「どのトラフィックをどう保護するか」
IKE SAで保護されたチャネル内で:
[Initiator] [Responder]
│ │
│──── IPsec SA Proposal ──────▶│ ESP/AH、暗号化アルゴリズム
│◀─── IPsec SA Accepted ──────│ 合意
│ │
│──── Traffic Selector ────────▶│ どのIP/Port範囲を保護?
│◀─── Traffic Selector ────────│ 合意
│ │
╔══════════════════════════════╗
║ IPsec SA確立(x2、双方向) ║
║ → 実データの暗号化開始! ║
╚══════════════════════════════╝
Diffie-Hellman鍵交換(魔法の核心)
# DH鍵交換 — 盗聴者が見ても秘密鍵は分からない!
p = 23 # 大きな素数(実際: 2048/4096 bit)
g = 5 # 生成元
# Alice
a = 6 # Aliceの秘密値
A = pow(g, a, p) # 5^6 mod 23 = 8 → 公開送信
# Bob
b = 15 # Bobの秘密値
B = pow(g, b, p) # 5^15 mod 23 = 19 → 公開送信
# 共有秘密鍵の導出(同じ値!)
alice_secret = pow(B, a, p) # 19^6 mod 23 = 2
bob_secret = pow(A, b, p) # 8^15 mod 23 = 2
assert alice_secret == bob_secret # ✅ 同じ秘密鍵!
# 盗聴者: p=23, g=5, A=8, B=19を知っていても
# aやbが分からなければ秘密鍵2は求められない(離散対数問題)
実務: AWS Site-to-Site VPN
オンプレミス (192.168.1.0/24)
│
├── Customer Gateway (IPsec)
│ │
│ │ ══ IPsecトンネル1 (Active) ══
│ │ ══ IPsecトンネル2 (Standby) ══
│ │
├── Virtual Private Gateway
│
AWS VPC (10.0.0.0/16)
# LinuxでIPsec VPN設定(strongSwan)
sudo apt install strongswan
# /etc/ipsec.conf
cat << 'EOF'
conn aws-vpn
type=tunnel
auto=start
left=203.0.113.1 # ローカルパブリックIP
leftsubnet=192.168.1.0/24 # ローカルネットワーク
right=52.10.20.30 # AWS VGW IP
rightsubnet=10.0.0.0/16 # AWS VPC CIDR
ike=aes256-sha256-modp2048
esp=aes256-sha256
keyexchange=ikev2
authby=secret
EOF
# /etc/ipsec.secrets
echo '203.0.113.1 52.10.20.30 : PSK "MyPreSharedKey123"' | sudo tee /etc/ipsec.secrets
# 起動
sudo ipsec restart
sudo ipsec status
IPsec vs WireGuard vs OpenVPN
| 項目 | IPsec (IKEv2) | WireGuard | OpenVPN |
|---|---|---|---|
| レイヤー | L3 | L3 | L3/L4 |
| コード量 | 約40万行 | 約4千行 | 約10万行 |
| 速度 | 高速 | 最速 | 普通 |
| 設定 | 複雑 | 簡単 | 普通 |
| モバイル | 優秀 (IKEv2) | 優秀 | 普通 |
| エンタープライズ | 標準 | 新興 | 広く使用 |
クイズ — IPsec(クリックして確認!)
Q1. ESPとAHの違いは? ||ESP: 暗号化 + 認証(完全性)。AH: 認証のみ(暗号化なし)。実務ではESPが99%使用される||
Q2. トンネルモードとトランスポートモードの違いは? ||トンネルモード: 元のIPパケット全体を新しいIPで包んで暗号化(ゲートウェイ間VPN)。トランスポートモード: 元のIPヘッダーを保持し、ペイロードのみ暗号化(ホスト間の直接通信)||
Q3. IKE Phase 1とPhase 2の役割は? ||Phase 1: IKE SA確立 — DH鍵交換 + 相互認証で安全なチャネルを確保。Phase 2: IPsec SA確立 — Phase 1チャネル内で実際のトラフィック保護ポリシーを合意||
Q4. SPI(Security Parameter Index)とは? ||32ビットの識別子で、受信側がどのSA(セキュリティ合意)を適用するか判断するために使用。ESPヘッダーに含まれる||
Q5. DH鍵交換が安全な理由は? ||離散対数問題 — 公開値(g, p, A, B)を知っていても秘密指数(a, b)を求めることが計算的に不可能。盗聴者がすべての公開通信を見ても共有秘密鍵を導出できない||
Q6. IPsecでSAが一方向である理由とその結果は? ||各SAは一方向のトラフィックのみ保護(セキュリティパラメータが方向ごとに異なりうる)。そのため双方向通信にはSA2つ(インバウンド + アウトバウンド)が必要||