V2RayのTLSハンドシェイク失敗・証明書エラーの対処法|時刻ずれとSNI設定を確認

v2rayNなどでTLSハンドシェイクに失敗し、証明書エラーが出る場合の原因と確認方法を解説。端末の時刻、SNIとホスト名、証明書チェーン、検証スキップ設定を順に確認します。

この記事の要点

v2rayN、v2rayNG、v2flyNGで通常のTLSを有効にしたノードが対象です。まずコアのログに表示されたエラーを保存し、システム時刻、サーバーアドレスとSNI、証明書チェーン、転送設定の順に確認します。読み終えると、端末側の設定とサーバー側の証明書のどちらに問題があるかを切り分け、サーバー管理者が確認できる記録をまとめられます。証明書検証の無効化は解決策として扱いません。

1. ログ全体を確認し、どの段階で失敗したかを特定する

「TLSハンドシェイク失敗」は問題が起きた範囲を示すもので、原因を特定するものではありません。一般的な接続では、サーバーアドレスの名前解決、TCP接続、TLSネゴシエーションと証明書検証の後に、プロキシプロトコルや上位の転送処理が行われます。VMessとVLESSはプロキシプロトコルの名称であり、プロトコル名だけでは証明書の正しさは判断できません。

名前解決 ポート接続 TLSネゴシエーション 証明書検証 プロキシ通信

v2rayNのメイン画面にあるログ欄を確認します。必要に応じて「設定」→「パラメータ設定」を開き、コアのログレベルを一時的にinfoに設定してください。項目の場所や名称はバージョンによって異なる場合があります。v2rayNG、v2flyNGでは、アプリのメニューにあるログ画面でコアの出力を確認できます。古いログを消してからノードを1つだけ再試行し、その接続で発生したエラーを記録してください。他のアプリの接続エラーが混ざらないようにします。

エラー:x509: certificate has expired or is not yet valid

原因と対処:端末の時刻が証明書の有効期間から外れているか、証明書自体が期限切れ、または有効期間前です。まずシステムの日付と時刻を確認し、サーバー管理者に実際に適用されている証明書の有効期間を確認してもらいましょう。

エラー:x509: certificate signed by unknown authority

原因と対処:証明書チェーンがクライアントの信頼するルート証明書につながっていません。中間証明書の不足、プライベート証明書の使用、信頼ストアの古さなどが考えられます。まずサーバーが送信する証明書チェーンを確認し、検証スキップを安易に有効にしないでください。

エラー:tls: first record does not look like a TLS handshake

原因と対処:接続先からTLS以外のデータが返されています。ポートやサービスの種類を誤って設定した場合によく発生します。サブスクリプションの提供元が指定するアドレス、ポート、TLSの設定を確認してください。ポートを443に変えただけで直ったと判断するのは避けましょう。

ログにあるi/o timeout、connection refused、EOFだけでは、証明書の問題とは断定できません。最初の2つは、ネットワークの到達性とサービスの待ち受け状態を先に確認してください。EOFだけが表示される場合は接続が途中で終了したことを示すため、サーバー側のログと合わせて原因を特定します。ログを共有する際は、サブスクリプションURL、ユーザー識別情報、アクセストークンを隠してください。

2. システム時刻のずれ:同期してから証明書の期限を確認

証明書には有効期間の開始日時と終了日時が記録されています。システムの日付が1年ずれていれば、有効期間の範囲外となります。数分のずれでも、証明書の発行直後や期限間近には検証に失敗することがあります。タイムゾーンの表示が異なっていても、時計が間違っているとは限りません。タイムゾーン名だけで判断せず、端末が記録する実際の時刻が正確かを確認してください。

  1. Windows:「設定」→「時刻と言語」→「日付と時刻」を開き、「時刻を自動的に設定する」を有効にして「今すぐ同期」をクリックします。日付、年、タイムゾーンを確認し、クライアントのコアを再起動してください。
  2. macOS:「システム設定」→「一般」→「日付と時刻」を開き、「日付と時刻を自動的に設定」を有効にします。長時間のスリープから復帰した後も、現在の時刻を確認してください。
  3. Android:システム設定で「日付と時刻」を検索し、「日時を自動設定」と「タイムゾーンを自動設定」を有効にします。ネットワークに接続できることを確認し、v2rayNGまたはv2flyNGに戻って同じノードを再試行してください。
  4. Linux:timedatectl statusで時刻同期の状態を確認します。systemdを使用するシステムでは、sudo timedatectl set-ntp trueを実行してネットワーク時刻同期を有効にできます。

Windowsでは、まずターミナルで同期状態を確認できます。2つ目のコマンドは管理者として実行してください。同期に失敗した場合は、システムの案内に従って時刻サービスとネットワークを確認し、ノードの設定を次々に変更しないようにします。

w32tm /query /status
w32tm /resync
443
HTTPSでよく使われるTCPポート
80
HTTPでよく使われるTCPポート
123
NTPでよく使われるUDPポート

これらは各プロトコルで一般的に使われるポートであり、ノードが必ずそのいずれかを使用するとは限りません。ネットワークでUDP 123が遮断されると、従来のNTP同期に影響することがありますが、プロキシのポートも遮断されているとは限りません。システム時刻が正確なのにログに証明書の期限切れが明記されている場合は、サーバー側で証明書を更新して再読み込みしてください。端末の日付を変更しても解決しません。

3. SNI、サーバーアドレス、Host:3つの項目を個別に確認

SNIは、TLSハンドシェイク時に送信するサーバー名です。同じアドレスで複数のサービスを運用している場合に、適切な証明書を選択するために使われます。v2rayNのノード編集画面にある「SNI」または「サーバー名」は、通常、コアのTLS設定にあるserverNameに相当します。クライアントは設定されたサーバー名を使って証明書の相手先を検証するため、表示できそうなWebサイトのドメインを適当に入力してはいけません。

項目役割確認方法
サーバーアドレス接続先のドメイン名またはIPアドレスを指定します。現在有効なサブスクリプションと一致していることを確認します。スキームやパスは含めません。
ポート接続先でどのサービスに接続するかを指定します。サーバー側の設定に従って入力します。443とは限りません。
SNI / serverNameTLS接続先の選択と証明書名の検証に使われます。サービス提供元が指定し、証明書の対象となっているドメイン名を使います。
WebSocket HostTLS確立後のHTTPリクエストヘッダーに含まれます。リバースプロキシの設定に合わせます。SNIの代わりにはなりません。
WebSocketパスリバースプロキシがリクエストを適切なバックエンドに転送できるようにします。パスと大文字・小文字を正確に照合し、証明書のドメイン名とは分けて確認します。

たとえば、サーバーアドレスにIPアドレスを指定し、SNIには証明書の対象となるドメイン名を指定できます。両者が同じである必要はありません。ただし、IPアドレスで証明書を検証する場合は、証明書のSANに該当するIPアドレス形式の項目が必要です。ドメイン名の項目だけでは不十分です。通常のドメイン証明書では、証明書画面のタイトルだけでなく、SAN(Subject Alternative Name、サブジェクト代替名)を確認してください。

結論:証明書名が一致しない場合は、DNSより先にSNIを修正

ログに受信した証明書と期待する名前が一致しないと表示されている場合、接続は少なくとも何らかのTLSサービスに到達しています。まずSNI、接続先アドレス、証明書のSANを確認してください。誤ったサービスに名前解決されている疑いがある場合に限り、DNSの結果を調べます。

4. 証明書チェーンの不足:ブラウザーで開けてもコアで検証できるとは限らない

サーバーは通常、サイト証明書と必要な中間証明書を送信し、クライアントは自身の信頼するルート証明書を使って検証します。ブラウザーは中間証明書をキャッシュしていたり、異なる信頼の仕組みを使っていたりする場合があります。そのため、「同じドメインをブラウザーで開ける」ことは手掛かりにはなりますが、サーバーの証明書チェーン設定に問題がない証明にはなりません。

  1. まず影響範囲を確認:時刻が正確な複数の端末で同じノードに「不明な認証局」のエラーが出る場合は、管理者に適用中の証明書と中間証明書を確認してもらいましょう。
  2. 次に端末ごとの差を確認:古いシステムでのみ失敗する場合は、システムの更新状況と信頼するルート証明書を確認し、クライアントと実際に使用しているコアのバージョンを記録します。
  3. ネットワークによる介入を確認:社内ネットワークやHTTPS検査機能を有効にしたセキュリティソフトの環境でのみ発生する場合は、ネットワーク管理者に証明書の差し替えが行われていないか確認してください。
  4. プライベート証明書への対応:管理下にあることが明確な環境でのみ、管理方針に従って信頼できる証明書を設定してください。入手元が不明なルート証明書をインポートしないでください。

コマンドライン環境がある場合は、OpenSSLでサーバーが返す証明書を確認できます。以下では予約済みのサンプルドメインを使い、コマンドの形式だけを示します。実際の調査では、接続先アドレスと想定するSNIにそれぞれ置き換え、ポートもノードの設定に合わせてください。

openssl s_client -connect example.com:443 -servername example.com -showcerts -verify_hostname example.com -verify_return_error

-servernameはSNIの送信に使い、-verify_hostnameは検証する名前の指定に使います。用途は異なります。このコマンドが参照する信頼ストアは、クライアントのコアと異なる場合があります。ローカルのOpenSSLに信頼できるCAが正しく設定されていなければ、コマンドがエラーになってもサーバー側の障害とは断定できません。確認時は、返された証明書のSAN、有効期間、証明書チェーンを記録し、最後の1行だけを切り取らないでください。

5. 証明書検証のスキップ:無効のままにして設定の問題を修正

「安全でない接続を許可」「証明書検証をスキップ」は、一般にallowInsecureに対応します。有効にすると、信頼できない証明書や名前が一致しない証明書をクライアントが拒否しなくなる場合がありますが、証明書の問題が直るわけではありません。接続先の身元を通常どおり検証できなくなります。また、TCPタイムアウト、ポートの誤設定、WebSocketパスの誤り、プロキシ認証の失敗も解決しません。

以下は通常のTLSアウトバウンドにおけるstreamSettingsの一部を示したものです。インポートして使える完全な設定ではなく、REALITYにも適用できません。example.comは項目間の関係を説明するための例です。実際の値はサーバー設定に基づいて入力し、例のドメインでノードに接続しないでください。

{
  "streamSettings": {
    "security": "tls",
    "tlsSettings": {
      "serverName": "example.com",
      "allowInsecure": false
    }
  }
}

6. 証明書検証後も失敗する場合:転送層とサーバー側の記録を確認

証明書検証に成功しても、プロキシ接続が成功したとは限りません。WebSocketのパスとHost、gRPCのサービス名、プロキシプロトコルのユーザー識別情報は、それぞれの段階で通信に使われます。ログがx509エラーからHTTPステータスエラーや認証エラーに変わった場合は、新しいエラーに沿って調査してください。再び検証スキップを有効にする必要はありません。

エラー:websocket: bad handshake

原因と対処:WebSocketのアップグレードリクエストに期待した応答がありません。Host、パス、リバースプロキシの転送設定が一致していない可能性があります。ノードの転送設定を確認し、管理者に該当時刻のHTTPアクセスログを調べてもらいましょう。

エラー:remote error: tls: handshake failure

原因と対処:リモート側がTLSネゴシエーションを中断しています。SNIのポリシー、プロトコルの互換性、サーバー側の認証要件などが関係する可能性があります。クライアントのログ1行だけでは原因を特定できません。サーバー側のハンドシェイクログと照合してください。端末の証明書ストアだけが原因と決めつけないでください。

一度に変更する項目は1つにし、同じノードで再試行してください。DNS、SNI、コア、ポートを同時に変えると、接続が回復してもどの変更が有効だったか判断できません。クライアントやコアを更新する前に設定を保存し、ノードに必要なプロトコルと転送方式がサポートされていることを確認します。エラーを消すためだけに、むやみにコアを切り替えないでください。

7. よくある質問:エラーの範囲に応じて次の確認項目を選ぶ

時刻を同期しても証明書の期限切れと表示されるのはなぜ?

まず、エラーが直前に再試行したノードのものか確認してください。クライアントが実際に受信した証明書の有効期間を調べます。サーバー側では証明書ファイルを更新済みでもサービスを再読み込みしていない場合や、一部の接続先だけ更新されている場合があります。失敗した時刻、接続先アドレス、証明書の有効期限を管理者に伝えて確認してもらいましょう。

サーバーアドレスがIPアドレスなら、SNIにもIPアドレスを入力する必要がありますか?

その必要はありません。接続アドレスはデータの送信先を決め、SNIには通常、サービス提供元が指定する証明書のドメイン名を入力します。検証名としてIPアドレスを使う場合は、証明書のSANに一致するIPアドレス形式の項目が必要です。WebSocketのHostがSNIを自動的に置き換える設定だと考えないでください。

検証スキップを有効にすると接続できます。このまま有効にしてもよいですか?

証明書検証に関連する問題だと推測できるだけで、接続先が信頼できることの証明にはなりません。設定を無効に戻し、有効期間、名前の一致、証明書チェーンの順に確認してください。元のサブスクリプションに誤った設定が繰り返し書き込まれる場合は、検証を回避し続けず、提供元に修正を依頼してください。

すべてのノードでハンドシェイクがタイムアウトします。証明書が一斉に壊れたのでしょうか?

タイムアウトだけで原因を判断しないでください。まずシステムのネットワークと時刻を確認し、クライアントが実際に接続を開始しているか、接続先ポートに到達できるか、ログにx509エラーが明記されているかを調べます。利用が許可された別のネットワークで同じノードを再テストすると、ネットワークの違いから原因を絞り込めます。

クライアントのダウンロードとインストールv2rayN · v2rayNG