V2RayのTLSハンドシェイク失敗・証明書エラー対策|時刻同期とSNI設定

x509証明書エラーやTLSハンドシェイク失敗は、端末の時刻ずれ、SNIと証明書ドメインの不一致、allowInsecureの設定ミスが主な原因です。順番に確認し、各クライアントでの設定場所を解説します。

この記事の概要

設定をインポート済みで、接続ログにTLSまたはx509エラーが表示される場合が対象です。まずエラーがサブスクリプション取得時とノード接続時のどちらで発生したか確認し、次にシステム時刻、サーバーアドレス、SNI、証明書のドメイン名を確認します。最後に証明書チェーンとサーバー側の接続設定を調べます。変更は一度に1項目にとどめ、証明書検証は有効のままにして、何が改善につながったか判断できるようにしましょう。

エラーの発生箇所とハンドシェイク段階を確認

「TLSハンドシェイク失敗」は結果であり、原因を示すものではありません。HTTPSのサブスクリプションURLを使っている場合、更新時にもTLSハンドシェイクが行われます。ノードでTLSを有効にしている場合は、プロキシのコアもノードサーバーとハンドシェイクします。前者の失敗ならサブスクリプションのドメインや配信サービスを確認し、後者の場合にノードのSNIを調べます。同じエラー文言だからといって、両方の設定を同時に変更しないでください。

まず接続を停止し、エラーが発生した操作を記録してから接続を再開し、Webページに一度アクセスします。v2rayNGとv2flyNGではメイン画面のサイドメニューから「ログ」を開きます。v2rayNではメイン画面のログ欄を確認してください。バージョンによって表示は多少異なりますが、エラーの前後数行、接続先ドメイン、ポートを保存することが重要です。「接続失敗」の一行だけを切り取らないでください。ログを共有する前に、サブスクリプションURL、ユーザー識別情報、認証情報を伏せてください。

x509: certificate has expired or is not yet valid

原因:端末の時刻が証明書の有効期間から外れているか、サーバー証明書の有効期限が切れています。対処:まず端末の時刻を同期し、証明書の有効期間を確認します。時刻が正しいのにエラーが続く場合は、サーバー側で証明書を更新して再読み込みします。

x509: certificate signed by unknown authority

原因:証明書チェーンが現在の環境で信頼されているルート証明書につながっていません。中間証明書が不足しているか、プライベート証明書が使われている可能性があります。対処:サーバーが完全な証明書チェーンを送信しているか、端末の信頼ストアに問題がないか確認し、検証を無効にしないでください。

remote error: tls: handshake failure

原因:リモート側でハンドシェイクが終了しています。SNI、TLSパラメーター、クライアント証明書の要件、接続先の設定などが関係する可能性があります。対処:まずサブスクリプションの元の設定を確認し、サーバーログと照らし合わせてください。このログ行だけで証明書の期限切れと判断することはできません。

ログにi/o timeout、connection refused、EOFだけが表示されている場合、証明書検証エラーが起きたとは限りません。まずサーバーアドレスを名前解決できるか、接続先ポートに到達できるか、そのポートで実際にTLSが提供されているかを確認してください。以降の手順は、通常のTLSを明示的に有効にした接続が対象です。REALITYは設定項目と検証方法が異なるため、そのまま当てはめないでください。

システム時刻を同期して証明書の有効期間エラーを切り分ける

証明書には有効開始日時と有効期限があります。端末の日付が未来にずれていると、有効な証明書が期限切れと判定されることがあります。過去にずれている場合は、まだ有効ではないと判定される可能性があります。時刻の時・分だけでなく、年と日付、自動時刻同期が正常かどうかも確認してください。タイムゾーン表示が異なっていても、必ずしもエラーの原因ではありません。検証に使われるのは、システムが示す実際の時刻です。

  1. 時刻の設定を開く

    Androidでは「設定」→「システム」→「日付と時刻」を開きます。端末によっては「その他の設定」や「一般管理」にあります。設定画面の検索欄で「日付と時刻」と検索する方法もあります。

  2. 自動同期を有効にする

    「日付と時刻の自動設定」と「タイムゾーンの自動設定」を有効にし、ネットワーク経由の時刻同期が完了するまで待ちます。日付が正しくならない場合は、端末のネットワーク接続とシステムの時刻サービスを先に確認してください。誤った日付のままノード接続を何度も試さないでください。

  3. パソコンの時刻を確認する

    Windowsでは「設定」→「時刻と言語」→「日付と時刻」を開き、「時刻を自動的に設定する」を有効にして「今すぐ同期」をクリックします。macOSでは「システム設定」→「一般」→「日付と時刻」を開き、日付と時刻の自動設定を有効にします。

  4. 再接続してログを保存する

    クライアントの接続を停止してから再起動し、同じWebページにアクセスします。有効期間に関するエラーが続く場合は、ログに表示された現在時刻と証明書の有効期間を記録し、ノードの管理者に証明書の更新状況を確認してもらってください。

Linuxではtimedatectl statusでシステム時刻と同期状態を確認できます。systemdを使うシステムでは、sudo timedatectl set-ntp trueでネットワーク時刻同期を有効にできます。このコマンドはすべてのディストリビューションで使えるわけではありません。別の時刻同期サービスを利用している場合は、システムの設定に従い、競合するサービスを同時に起動しないでください。

SNI・サーバーアドレス・偽装ドメインの違い

サーバーアドレスは接続先のIPアドレスまたはドメインを指定し、ポートは接続する待ち受け先を選びます。SNIはTLSハンドシェイクで送信されるサーバー名です。サーバー側ではSNIを使って証明書やバックエンドのサイトを選ぶ場合があります。クライアント側でも、通常は設定されたサーバー名を使って証明書の名前を検証します。SNIにはドメイン名だけを指定し、https://、ポート、パスは含めないでください。

解説で使われる「偽装ドメイン」が、常に同じ設定項目を指すとは限りません。WebSocketのHost、HTTPリクエストのパス、TLSのSNIはそれぞれ別の層にあります。サーバーの設計によって同じドメインを使う場合も、異なるドメインを使う場合もあります。Hostにドメインを設定したからといって、証明書がそのドメインをカバーしているとは限りません。また、WebページのパスをSNIに入力しないでください。

項目用途確認するポイント
サーバーアドレス基盤となるネットワーク接続を確立する提供元から指定された接続先と一致していること。IPアドレスまたはドメインを指定できます
サーバーポート接続先の待ち受けサービスを選択する443は一般的なTLSポートですが、ポート番号だけではTLSが有効かどうかは判断できません
SNI / serverNameTLSのサーバー名を送信し、通常は証明書の名前検証にも使用するサーバー側の指定と、証明書のSANに含まれる名前が一致していること
Host転送層でHTTPを使う場合のHostフィールドWebSocketなど、転送方式の要件に合わせて設定する。SNIの代わりにはならない
allowInsecure証明書検証をスキップするかどうかを制御する通常はfalseのままにする。証明書やドメイン名の誤りは解決できない

まとめ:接続先がIPアドレスなら、まずSNIを確認

ノードがIPアドレスに接続し、証明書がドメイン名のみをカバーしている場合は、サーバー側が指定する証明書対象のドメインをSNIに設定してください。有名サイトのドメインを適当に指定したり、エラーを消すためだけにサーバーアドレスを証明書のドメインに変更したりしないでください。まったく別の接続先につながる可能性があります。

名前の照合では、主に証明書のSANを確認します。ワイルドカード*.example.comは通常、node.example.comには一致しますが、ルートドメインのexample.comやa.node.example.comには一致しません。ここに記載したドメインは照合ルールの説明用で、接続可能なノードではありません。

各クライアントでSNIと証明書検証を設定する場所

まずノードの元のアドレス、ポート、転送方式、TLSの種類、SNI、Hostを控えてください。サブスクリプションから生成されたノードは、次回更新時に手動変更が上書きされる場合があります。先に独立したテスト用設定を複製しておくのがおすすめです。画面に複製機能がない場合は、元の設定を保存してから手動で新規作成してください。リリースによって項目名が英語または日本語で表示されることがあります。項目名ではなく意味を確認し、すべてのノードに共通するSNI設定をグローバル設定から探さないでください。

  1. v2rayNGの設定を確認

    メイン画面で対象ノードの鉛筆アイコンをタップし、設定編集画面を開きます。TLSの項目でセキュリティ方式がtlsになっていることを確認し、「SNI」をチェックします。「証明書検証をスキップ」またはallowInsecureは無効のままにしてください。保存後に対象ノードを選び直し、接続を再起動します。

  2. v2rayNの設定を確認

    サーバー一覧で対象ノードをダブルクリックし、サーバー編集画面を開きます。「トランスポート層セキュリティ(TLS)」の項目で、TLSの種類、「SNI」、「証明書検証をスキップ(allowInsecure)」を確認します。後者はfalseに設定し、保存後に対象サーバーを有効にして再接続してください。

  3. v2flyNGの設定を確認

    メイン画面で対象設定の編集ボタンをタップし、ノード設定画面を開きます。TLS関連の項目でserverNameまたはSNIを確認し、allowInsecureを無効にします。カスタムJSONをインポートした場合は、その設定内のTLS項目を確認してください。通常のノード設定画面で上書きできるとは限りません。

  4. 元の設定と照合する

    編集画面の内容を提供元の設定説明と一項目ずつ照合します。特に、サブスクリプション更新後にSNIが消えていないか、TLSが別のセキュリティ方式に誤設定されていないか確認してください。修正は一度に一項目だけにし、保存して再接続した後、新しいログを確認します。

v2rayNGはXrayコア、v2flyNGはv2flyコアを使用します。両者で対応する設定の範囲は完全には同じではありません。特定の拡張機能を含む設定を、テスト目的でそのまま入れ替えないでください。一般的なVMessまたはVLESSのTLS接続では、証明書検証を調べる前に、現在のコアがプロトコルと転送方式に対応していることを確認します。ユーザー識別情報の誤りは、通常SNIを変更しても解決しません。

証明書チェーンとサーバー側の接続先を確認

時刻とSNIが正しいのに信頼されていないというエラーが続く場合は、サーバーが送信する証明書チェーンを確認します。サイト証明書だけが設定され、中間証明書が不足しているケースがあります。また、証明書の更新ファイルは新しくなっていても、TLSを処理するリバースプロキシが再読み込みされていないことがあります。手前に中継サーバーやCDNがある場合は、オリジンサーバーのファイルだけでなく、クライアントが実際に接続している層を確認してください。

OpenSSLをインストール済みのmacOSまたはLinux環境では、以下のコマンドでTLS証明書チェーンと名前検証の結果を確認できます。例では予約済みのサンプルドメインに接続するため、実際のノードの検証には使えません。実際に調査する際は、接続先アドレス、ポート、2か所のドメインを自分の環境の値に置き換えてください。このコマンドで接続を確立できますが、VMessやVLESSの認証、後続の転送経路までは検証できません。

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

出力に表示された証明書の有効期間、名前、検証エラーを確認します。Verify return code: 0 (ok)と表示されても、そのテスト環境で証明書検証に成功したことを示すだけで、プロキシ設定全体が正常とは限りません。通常のTLSハンドシェイクが成功してもクライアントで失敗する場合は、転送経路、Host、プロトコル認証、クライアントが実際に接続しているアドレスが同じかどうかを引き続き確認してください。

allowInsecureに関する注意点

allowInsecure=trueにすると、重要な証明書検証がスキップされ、接続先サーバーの身元を確実に確認できなくなります。証明書の期限を延長したり、証明書チェーンを補完したり、SNIを自動修正したりする機能ではありません。有効にして一時的に接続できても、問題が証明書検証に関係する可能性を絞り込めるだけで、設定が修復されたとは判断できません。

証明書検証を無効にしたら接続できました。このまま使い続けても大丈夫ですか?

おすすめしません。allowInsecure=falseに戻し、時刻、名前、証明書チェーンの順に確認してください。プライベート証明書を使う自前の環境では、管理された方法で信頼設定や証明書ピンニングを行い、現在のコアが対応していることを確認してください。身元確認を長期的にスキップしないでください。

SNIが空欄の場合、サーバーアドレスが自動的に使われますか?

設定によってはアドレスからサーバー名が自動的に推定されますが、挙動はコアや設定の種類によって異なります。特にアドレスがIPの場合は、その推定に頼れません。提供元の指定に従い、証明書がカバーするドメインを明示して保存し、再接続してください。

ブラウザーでドメインを開けるのに、ノードでエラーが出るのはなぜですか?

ブラウザーは別のポート、IPアドレス、接続先を利用している場合があり、信頼ストアも異なることがあります。まずノードの接続先アドレス、ポート、SNIを照合してください。Webページが表示できたのは、そのページへのアクセスが成功したことを示すだけで、ノードのTLS経路も同じとは限りません。

サブスクリプションを更新すると元のエラーに戻ります。どうすればよいですか?

更新前後のSNI、TLSの種類、ポートを比較してください。一時的な変更は独立したテスト用設定に保存し、サブスクリプションのパラメーターに誤りがあることを提供元に伝えます。変更と更新を繰り返して設定を上書きしたり、ルーティングの問題だと決めつけたりしないでください。

ノードには接続できますが、サブスクリプション更新時だけx509エラーが表示されます。

サブスクリプションURL自体のHTTPS証明書とシステム時刻を確認してください。通常、ノード編集画面のSNIはサブスクリプション取得には影響しません。プロキシ経由でサブスクリプションを更新している場合はプロキシ経路も確認しますが、まずノードの証明書検証設定を変更する必要はありません。

修正後に再テストする手順

調査が終わったら、システム時刻が正しく、証明書検証が有効になっていることを確認してから接続を再起動します。遅延テストの結果だけで判断しないでください。テスト方法によって確認できる接続段階が異なり、結果が表示されても認証、転送、Webリクエストがすべて成功したとは限りません。変数を減らすため、同じノード、同じネットワーク、同じ接続先で再テストしてください。

まとめ:次の対応は最新のログを見て決める

時刻を修正した後に名前不一致のエラーへ変わった場合は、引き続きSNIを確認します。名前のエラーが消えて転送エラーに変わった場合は、転送層を調べます。DNS、ポート、プロトコル、証明書設定を一度に変更するより、最新の具体的なエラーに沿って調べるほうが原因を特定しやすくなります。

クライアントをダウンロードプラットフォームに合ったインストーラーを選択