ブログに戻る
解説·15 分で読める

WireGuard のハンドシェイクとは何か――プロトコルをステップごとに解説

「ハンドシェイク」とは、2 つの WireGuard ピア(あなたのスマートフォンと VPN サーバー)がお互いの身元を証明し、次の 2〜3 分間使用する暗号化キーを合意するプロセスです。2 つの UDP パケットで完了し、100ms 未満で終わり、Noise_IK + Curve25519 + ChaCha20-Poly1305 を使用します。現在稼働している暗号技術の中でも最も洗練された実装のひとつです。内部で何が起きているかを正確に解説します。

執筆: Casper's Cloak Security Team

要約: WireGuard は TLS を使用せず、OpenVPN や IPsec/IKEv2 のような複数往復のネゴシエーションも行いません。Noise Protocol Framework から派生した固定的・明快な暗号ハンドシェイク――具体的には Noise_IK パターン――を使用し、ちょうど 2 つのメッセージで完了します。両ピアは互いの長期公開鍵をあらかじめ知っています(SSH 鍵と同様に静的に設定されます)。そのためハンドシェイクの役割は身元を発見することではなく、対応する秘密鍵の所持を相互に証明し、前方秘匿性を備えた新鮮なセッションキーを導出することです。一連のやり取りは 2 つの UDP データグラムに収まり、それぞれ数百バイト程度です。通常の TLS ハンドシェイクよりも速く完了し、接続が続く間は 2 分ごとにセッションキーが更新されます。

ハンドシェイクがそもそも必要な理由

2 台のコンピューターが、制御できないネットワーク越しにプライベートに通信したいとします。根本的な暗号の問題:何かを暗号化する前に、ネットワークを監視している攻撃者が導出できない共有秘密に合意する必要があります。また、互いの身元を確認する必要もあります――そうしなければ攻撃者が相手側になりすまし、一方のピアを騙して攻撃者に向けてデータを暗号化させることができます。

ハンドシェイクはこの 2 つの問題を構造化した交換によって解決します:

  • 相互認証 ― 各ピアは、相手がすでに信頼している公開鍵に対応する秘密鍵を持っていることを実証します。秘密鍵そのものは送信されません。その鍵だけが生成できる暗号操作を実行することで、所持を証明します。
  • 前方秘匿型鍵合意 ― 両ピアはそのセッション専用の新鮮な一時(「エフェメラル」)鍵ペアを生成し、公開鍵半分を交換して、長期鍵と組み合わせて対称セッションキーを導出します。エフェメラル鍵はハンドシェイク後に破棄されます。長期鍵が後日盗まれても、過去のセッションキーを再構成することはできません。このプロパティを前方秘匿性といい、将来の鍵漏洩が起きても過去のトラフィックが保護されたままである理由です。
  • リプレイ保護の設定 ― 両ピアはカウンターとノンスを初期化し、攻撃者が以前のトラフィックを捕捉して後から再送するのを防ぎます。

VPN プロトコルによって実装方法は異なります。OpenVPN は TLS を通じて行い、複数往復・証明書検証・多数の設定可能な暗号スイートを伴います。IPsec/IKEv2 には同様の複雑さを持つ独自のネゴシエーションプロトコルがあります。WireGuard は 2 パケットで完結し、設定項目はなく、暗号スイートのネゴシエーションもありません――すべての WireGuard ハンドシェイクは同一の固定暗号プリミティブセットを使用します。このトレードオフは意図的なものです。複雑性を下げることで攻撃対象領域が小さくなり、監査が容易になります。その代償として、プロトコルレベルの俊敏性はゼロです。

Noise フレームワーク――その概要と WireGuard が Noise_IK を採用した理由

Noise Protocol Framework は、暗号ハンドシェイクを構築するための仕様です。Signal Protocol を設計した Trevor Perrin の作品であり、Signal・WhatsApp・および現代のほとんどのエンドツーエンド暗号化メッセンジャーを保護しています。Noise は単一のプロトコルではなく、構成要素(ハンドシェイク「パターン」、暗号プリミティブ、導出ルール)のキットです。プロトコル設計者が必要なハンドシェイクの形を選び、メッセージの内容と提供される保証の正確な仕様を得られます。

Noise ハンドシェイクパターンは、各側が事前に何を知っているかを示す 2 文字で命名されます:

  • N ― その側の静的鍵なし。エフェメラル鍵のみを使用。匿名。
  • K ― 静的鍵が相手に事前に「既知」。
  • X ― 静的鍵がハンドシェイク中に送信される。
  • I ― 静的鍵が最初のメッセージで「即座に」送信される。

Noise_IK は:イニシエーターが静的鍵を即座に(最初のメッセージで)送り、レスポンダーの静的鍵はイニシエーターに事前に既知であることを意味します。このパターンは WireGuard のユースケースに理想的です。両ピアが互いの静的鍵を事前に知っているからです――WireGuard 設定ファイルに設定されています。「IK」の選択により、ハンドシェイク全体を 2 メッセージで完了できます。これは相互認証された前方秘匿型鍵交換に必要な最小メッセージ数です。

Noise フレームワークの完全な仕様は noiseprotocol.org で公開されており、公開暗号技術の中でも特に洗練された成果のひとつです――監査を意識して書かれており、パターン自体の形式的検証も行われています。WireGuard が Noise_IK を採用したのは、このフレームワークの解析を継承するための意図的な設計判断です。

パケット 1:イニシエーター → レスポンダー

ハンドシェイクは、イニシエーター(スマートフォン、ラップトップ、クライアント側)がレスポンダー(VPN サーバー)と通信しようとするときに始まります。これはトンネルを待つトラフィックがキューに入ったとき、またはセッションキーが期限切れになって再鍵が必要なときに発生します。イニシエーターは以下を含む 1 つの UDP パケットを構築します:

  • 送信者インデックス ― このハンドシェイクセッションを識別するためにランダムに選ばれた 32 ビット整数。同じピア間で同時進行するハンドシェイクを区別するために使用されます。
  • エフェメラル公開鍵 ― 新たに生成された Curve25519 公開鍵。対応する秘密鍵はイニシエーターのメモリに保持されます。これが Noise パターンの「E」――新しいエフェメラル鍵の導入です。
  • 静的公開鍵(暗号化済み) ― イニシエーターの長期 Curve25519 公開鍵ですが、エフェメラル交換から導出されたキーで暗号化されています。これが IK の「I」の一部です――イニシエーターの静的鍵は最初のメッセージで送られますが、受動的な傍受者から保護されています。
  • タイムスタンプ(暗号化済み) ― 暗号化された TAI64N 形式のタイムスタンプ。リプレイ保護のアンカーとなります:レスポンダーはこのピアからすでに受け入れたタイムスタンプより古いハンドシェイクをすべて拒否します。攻撃者が有効なハンドシェイクメッセージを捕捉して後から再送することを防ぎます。
  • MAC フィールド ― 送信者がレスポンダーの公開鍵を知っていることを証明する 2 つの短い認証子(mac1、mac2)。mac2 フィールドは WireGuard の論文に記載されている cookie-reply DoS 軽減メカニズムにも使用されます。

このパケットが証明・達成すること:新鮮なエフェメラル鍵を含めることで、イニシエーターは最終的なセッションキーの半分にコミットします。レスポンダーの静的鍵に紐付けられたタイムスタンプと MAC を含めることで、イニシエーターは「少なくともレスポンダーの公開鍵を持っている」ことを実証します――つまり、経路外の無関係な攻撃者がフェイクのハンドシェイク開始を送りつけてサーバーリソースを無駄に消費させることが難しくなります。パケット全体は 148 バイトで、1 つの UDP データグラムに収まり、フラグメント化されません。

注目すべき細部:イニシエーターの静的公開鍵は含まれていますが暗号化されているため、通信路を監視する受動的な傍受者はどのクライアントが接続しているか識別できません。誰かがこのサーバーに WireGuard ハンドシェイクを開始していることはわかりますが、誰なのかはわかりません。このプライバシー特性をイニシエーター身元隠蔽といい、デフォルトの TLS が提供するものより強力です。

パケット 2:レスポンダー → イニシエーター

レスポンダーは開始パケットを受信し、イニシエーターの静的公開鍵を復号してピアテーブルで照合します。ピアが既知でタイムスタンプが許容範囲内(最後に受け入れたものより新しい)であれば、レスポンダーは自身のエフェメラル鍵ペアを生成して応答パケットを構築します:

  • 送信者インデックス ― レスポンダーがこのセッションのために選んだ 32 ビット識別子。
  • 受信者インデックス ― イニシエーターから折り返され、両側がどのセッションかを合意できます。
  • エフェメラル公開鍵 ― レスポンダーが新たに生成したエフェメラル公開鍵。
  • 空の暗号化ペイロード ― 認証済みだが空の暗号文。レスポンダーがセッションキーを導出できることを証明します。暗号学的な「あなたのメッセージを読み、正しい鍵を計算した」というシグナルです。
  • MAC フィールド ― 開始パケットと同じパターン。

イニシエーターがこの応答を受信すると、対応する鍵導出を実行し、認証された空ペイロードが導出した鍵で正しく復号されることを検証します。これで両側は同じ対称セッションキーに独立して到達します――そのキーがネットワーク上を流れることなく。セッションキーは完全前方秘匿性を持ちます。両エフェメラル鍵(まもなく破棄される)と両静的鍵の両方に依存しているからです。

応答パケットは 92 バイトです。ハンドシェイク全体――両パケット合計――は 240 バイト(IP/UDP ヘッダー除く)で、2 回の往復 UDP 交換に収まり、おおよそ 1 ネットワーク RTT(各方向 1 パケット)で完了し、アプリケーショントラフィックを暗号化する準備の整った完全認証済み前方秘匿セッションを生成します。証明書チェーンの検証も、暗号スイートのネゴシエーションも、バージョン比較も、拡張リストも――TLS ハンドシェイクを複雑にするものは何もありません。

その後のセッション

ハンドシェイクが完了すると、2 つのピアはよりシンプルな形式でデータパケットを交換します。各データパケットには以下が含まれます:

  • 受信者インデックス ― ハンドシェイク時に受信者が割り当てた 32 ビットセッション ID。
  • カウンター ― 送信パケットごとにインクリメントされる 64 ビットノンス。AEAD ノンスとリプレイ検出の両方に使用されます。
  • 暗号化ペイロード ― 元の IP パケット。セッションキーとカウンターをノンスとして ChaCha20-Poly1305 で暗号化されています。

ChaCha20-Poly1305 は AEAD(関連データ付き認証暗号)構造です:ペイロードを暗号化するとともに、改ざんを検出する認証タグを生成します。暗号文の 1 ビットが反転しただけで復号が失敗し、受信者はパケットを破棄します。「ソフト」な失敗モードはありません――改ざんされたパケットは、カウンターが間違っているパケットや受信者インデックスが間違っているパケットと同様、静かに破棄されます。

受信者は受け入れ済みカウンター値のスライディングウィンドウを維持します。ウィンドウ内に収まり、まだ見ていないカウンターのパケットは受け入れられ、ウィンドウより古いもの(期限切れ)やすでに見たものは拒否されます。これにより、受信者がこれまで見たすべてのカウンターを記憶することなく、直近のものだけを小さなビットマップで管理してアンチリプレイ保護を提供します。

ネットワーク側から見ると、データパケットはペイロードの内容に関係なく同一に見えます。すべて同じサーバー IP とポート宛の UDP パケットで、サイズも似通っており(暗号化ペイロードは元の IP パケットのサイズに 32 バイトのオーバーヘッドを加えたもの)、区別できる観測可能なパターンはありません。SNI も、証明書交換も、TLS ハンドシェイクとデータの構造も存在しません――最初の 2 パケットのハンドシェイク後は不透明な UDP だけです。

再鍵動作――2 分ごと、または 2^60 パケットごと

WireGuard はセッションキーを積極的に更新します。デフォルトの再鍵ポリシー:現在のセッションが 2 分間アクティブになった、2^60 パケットを送信したとき、どちらか早い方で新しいハンドシェイクが開始されます。実際には、ほとんどのセッションでは時間制限が再鍵のトリガーとなります――2^60 パケットは現実の接続では到達できない天文学的な数です。

なぜ 2 分なのか?2 つの理由があります。第一に、頻繁な再鍵は前方秘匿性を強化します。各セッションは特定の 1 組のエフェメラル鍵から導出されたキーで暗号化されます。セッションが短いほど、1 つのキーを流れるデータが少なくなり、将来の理論的な暗号解析攻撃でも意味のあるトラフィック量を読むには多くのキーを突破する必要があります。第二に、再鍵はアプリケーションに対して不可視です――新しいハンドシェイクは古いセッションキーがデータを暗号化し続ける間にバックグラウンドで行われ、切り替えはシームレスです。両ピアは古いキーと新しいキーのどちらで暗号化されたパケットも受け入れる短い重複ウィンドウを維持します。

再鍵はタイマーの期限切れに最初に気づいた側――通常はイニシエーター側――が開始します。プロトコルメッセージは上記で説明した同じ 2 パケットのハンドシェイクです。外部の観測者から見ると、2 分ごとに追加の UDP パケットが少量出現しますが、データストリーム自体に観測可能な変化はありません。

ハンドシェイクのフェーズ一覧

ハンドシェイク全体をフェーズ、パケット、処理を担う暗号プリミティブごとに整理します:

フェーズ パケット 送信内容 暗号プリミティブ
1. 開始 イニシエーター → レスポンダー(148 バイト) 送信者インデックス、エフェメラル公開鍵、暗号化済み静的鍵、暗号化済みタイムスタンプ、MAC Curve25519(ECDH)、BLAKE2s(ハッシュ/MAC)、ChaCha20-Poly1305(AEAD)、HKDF(鍵導出)
2. 応答 レスポンダー → イニシエーター(92 バイト) 送信者インデックス、受信者インデックス、エフェメラル公開鍵、空の暗号化ペイロード(鍵確認)、MAC Curve25519(ECDH)、BLAKE2s、ChaCha20-Poly1305、HKDF
3. データセッション 両方向、無期限(パケットごとに 32 バイトのオーバーヘッド) 受信者インデックス、カウンター、暗号化 IP パケット ChaCha20-Poly1305(AEAD)
4. 再鍵(2 分ごと) フェーズ 1+2 と同じ 2 パケット交換 新鮮なエフェメラル鍵、新しいセッションキーの導出 同じプリミティブ

使用される暗号プリミティブは 5 つで、いずれもネゴシエーション不可能です:楕円曲線ディフィー・ヘルマン鍵合意のための Curve25519、対称ストリーム暗号化のための ChaCha20、認証タグのための Poly1305、ハッシュ関数および MAC のための BLAKE2s、鍵導出のための HKDF。これが「アジリティなし」の設計判断です:プリミティブをハードコードすることでプロトコルの表面が劇的に縮小し、解析が大幅に容易になります。コストは、いずれかのプリミティブが破られた場合、プロトコル全体の新バージョンが必要になることです――帯域内でアップグレードする方法はありません。WireGuard の賭けは、これらのプリミティブが十分に研究されており、突然の突破は起こりにくいというものです。

起こりうる問題

暗号ハンドシェイクには既知の失敗モードがあります。WireGuard に適用されるものとされないものがあります。理解しておく価値があります:

リプレイ攻撃

開始パケット内の暗号化タイムスタンプがリプレイを防ぎます。攻撃者が有効な開始パケットを捕捉して後から再送しようとすると、レスポンダーはすでに受け入れたタイムスタンプ(または最後に受け入れたものより古いタイムスタンプ)を見てパケットを拒否します。これが WireGuard がピアごとに最新のハンドシェイクタイムスタンプの状態を維持する理由のひとつです――プロトコルがそれに依存しているからです。

長期鍵の漏洩

攻撃者がピアの長期秘密鍵を盗んだ場合、そのピアになりすまして将来のハンドシェイクを行えます。しかし、過去のセッションは復号できません――それらのセッションキーを導出したエフェメラル鍵は各ハンドシェイク後に破棄されているからです。これが前方秘匿性の保証です。継続的リスクへの対策は長期鍵の更新(新しいペアを生成し、新しい公開鍵をピアに配布する)ですが、古い鍵を無効化するプロトコルレベルのメカニズムはないため、帯域外で行う必要があります。

NAT タイムアウトとサイレント接続切断

WireGuard はコネクションレスの UDP 上で動作します。ネットワークアドレス変換器(ホームルーターや ISP のネットワーク内にあるもの)は、数分間の非活性後に UDP フローエントリを削除することがあります。これが起きると、レスポンダーの応答パケットはイニシエーターに届かなくなります――NAT がマッピングを忘れてしまったからです。対策はキープアライブ設定(通常 25 秒に設定)で、イニシエーターからレスポンダーへ小さなハートビートパケットを送り続けて NAT マッピングを維持します。キープアライブがなければ、アイドル接続は静かに切断され、復元には新しいハンドシェイクが必要になります。

ハンドシェイクフラッドによるサービス拒否

重大な攻撃を受けたレスポンダーは、多数の偽ハンドシェイク開始を受信する可能性があります。それぞれを処理するには Curve25519 演算が必要です。WireGuard は cookie-reply メカニズムでこれを軽減します:レスポンダーが高負荷下にあるとき、フルハンドシェイクにコミットする前にイニシエーターに小さなプルーフ・オブ・ワーク的な計算を要求できます。ハンドシェイクパケットの mac2 フィールドがこのメカニズムを担います。DoS 耐性は完全ではありません――どのプロトコルも完全ではありません――しかし、非認証 TCP ベースのサービスなどと比較して、フラッドはかなり困難です。

VPN ユーザーへの実際的な意味

このハンドシェイクアーキテクチャは、古い VPN プロトコルと比較してユーザーが実感できる具体的なメリットをもたらします:

  • クライアント側の TLS ハンドシェイクなし ― WireGuard は証明書を検証せず、CA にチェーンせず、システムのトラストストアに依存しません。これにより、TLS ベースの VPN に影響する一連の攻撃(侵害された CA、不正発行された証明書)が排除されます。
  • ステートフルな接続なし ― プロトコルは UDP 上で動作しコネクションレスです。ネットワーク変更時に壊れる TCP スタイルのステートマシンがありません。Wi-Fi からモバイルデータに切り替えたローミングデバイスは何も再確立する必要がありません。新しい IP アドレスからパケットを送り始めるだけで、レスポンダーがそれを受け入れます。
  • 即時ローミング ― 接続は送信元 IP アドレスではなく暗号鍵で識別されるため、レスポンダーは正しい暗号認証を生成するソース IP からのパケットをどのアドレスからでも受け入れます。これが、基礎となる IP が変わるときに再ネゴシエーションが必要になることの多い OpenVPN や IPsec と比べて、WireGuard がモバイルネットワーク移行をはるかにうまく処理できる理由です。
  • 小さな攻撃対象領域 ― リファレンス実装は約 4,000 行のコードです。OpenSSL は 50 万行以上です。これは公平な比較ではありませんが(作業量が異なります)、示唆することは本物です:コードが小さいほど監査しやすく、形式的に解析しやすく、脆弱性につながる実装バグの機会が少なくなります。
  • 予測可能なパフォーマンス ― すべてのハンドシェイクが同じ鍵サイズで同じプリミティブを使用します。ネゴシエートされた暗号スイートに基づくファストパス/スローパスの分岐はありません。ハンドシェイクは、接続先に関係なく、同じハードウェアでほぼ同じ時間がかかります。

ほとんどのユーザーにとって置き換えとなった古いプロトコルとのトレードオフ比較については、実際のパフォーマンスとセキュリティの違いをカバーする WireGuard vs OpenVPN の解説をご覧ください。VPN のトンネリングの選択がアプリに見えるものにどう影響するかについては、スプリットトンネリングとは何かをご覧ください。

Casper's Cloak はすべてのクライアント接続に WireGuard を使用し、Linux エンドポイントでは公式のカーネルモード実装を動かし、iOS、Android、macOS では標準のユーザースペースクライアント(wireguard-go リファレンスをベースに構築)を提供しています。上記で説明したハンドシェイクは、アプリをオンにしたときに実際に起きていることです。同じ Noise_IK パターン、同じ Curve25519/ChaCha20-Poly1305/BLAKE2s/HKDF プリミティブセット、同じ 2 分間の再鍵、同じ 240 バイトのハンドシェイクです。脅威保護機能は WireGuard トンネルの上に DNS レベルフィルタリングを重ねています――トンネルがトランスポートを担い、フィルターが宛先ポリシーを担います。

プロトコルの完全な仕様については、Jason Donenfeld による WireGuard の原論文が権威ある参考文献です:wireguard.com/papers/wireguard.pdf。この論文は暗号構造、脅威モデル、形式的解析、実装の根拠を網羅しています。現代のプロトコル設計の文章の中でも特に読みやすい部類に入ります――設計上のトレードオフを源流から理解したいなら、1 時間かけて斜め読みする価値があります。

結論:WireGuard ハンドシェイクは 2 パケットで構成され、実際には 100 ミリ秒未満で完了し、両ピアを相互に認証し、前方秘匿型セッションキーを導出し、設計によってすべての一般的な攻撃パターン(リプレイ、ダウングレード、身元開示、DoS)に耐性を持ちます。タイトで固定的、意見の強いプロトコルであり、その意見の強さこそが要点です。

監修: Casper's Cloak Security Team · 最終更新

適切に設定された WireGuard に、フィルタリングを内蔵

Casper's Cloak は上記で説明したハンドシェイクを持つ標準の WireGuard トンネルを実行します。さらに、既知の悪意ある宛先に対する DNS レベルフィルタリング、自動 IPv6 処理、そしてハンドシェイク間の漏洩を防ぐ OS レベルで強制されるキルスイッチを備えています。