TLSハンドシェイクの失敗、証明書名の不一致、証明書の期限切れ、unknown authorityに遭遇したv2rayN、v2rayNG、v2flyNGユーザー向けの記事です。確認は本体の時刻、ノードのアドレスとSNI、証明書の有効期限、証明書チェーン、通信パラメータの順に行い、最後に検証スキップで短時間の比較テストを実施します。
TLSハンドシェイクはどの段階で失敗するのか
TLSはTCP接続の確立後、プロキシプロトコルが実際のデータを交換する前に位置します。TLSを使用するVMessまたはVLESSノードでは、まずクライアントがサーバーアドレスを解決してリモートポートへ接続します。一般的なポートは443です。続いてClientHelloを送信し、そこにはSNI、対応するTLSバージョン、ALPNなどが含まれる場合があります。サーバーが証明書チェーンを返すと、本体のシステムが証明書の有効期間、発行関係、名前を確認し、検証に成功して初めて暗号化セッションが確立されます。
そのため、「遅延テストに失敗した」ことをそのまま「ノードが使えない」と判断することはできません。DNS解決の失敗、TCPポートに接続できない、TLS証明書の検証失敗、プロキシプロトコルのパラメータエラーは、いずれもテスト結果をタイムアウトや利用不可にしますが、問題の箇所はそれぞれ異なります。まずログに記録された最初の明確なエラーを確認し、変更するパラメータを決めるほうが、ノードを何度も切り替えるより効果的です。
リモートアドレスへ接続する前にログへ「no such host」と出る場合は、まずDNSまたはアドレスの入力ミスを確認します。x509、certificate、handshakeなどのキーワードが出ている場合に、証明書とTLSの確認へ進みます。TLS完了後にinvalid user、authentication failed、protocol responseなどが出るなら、問題はUUID、パスワード、プロトコル種別、通信設定にあり、証明書が原因ではありません。
ログの原文から証明書エラーを特定する
v2rayN 7.xでは、まずメイン画面下部のログ欄でカーネルの出力を確認し、「設定」→「パラメータ設定」でログレベルが低すぎないことを確認します。テスト時は現在のカーネルを停止してから再起動し、HTTPSページへアクセスすると、古いログの影響を減らせます。ノードはサーバー一覧で右クリックし、「サーバーを編集」を選択してTLSとSNIの項目を確認します。
Androidでも、まず現在の設定を確認します。v2rayNGはXrayカーネル、v2flyNGはv2flyカーネルを使用します。両者の証明書の有効期限とホスト名に関する基本的な検証原則は同じですが、ログの表現には多少の違いがあります。「起動に失敗しました」という最後の1行だけを切り取らず、上へさかのぼって最初に現れたx509、tls、certificateの記録を探してください。
エラー:x509: certificate has expired or is not yet valid
原因と対処:本体の時刻が証明書の有効期間外になっているか、サーバーの証明書が実際に期限切れです。まずシステムの日付、時刻、タイムゾーンを同期し、ログに示されたNot BeforeとNot Afterの時刻を確認します。
エラー:x509: certificate is valid for example.net, not edge.example.net
原因と対処:クライアントが検証している名前が、証明書で許可された範囲に含まれていません。SNIをサブスクリプションで指定された証明書のドメイン名に変更し、ノードのメモ、CDNアドレス、IPアドレスをこの項目へ適当に入力しないでください。
エラー:x509: certificate signed by unknown authority
原因と対処:サーバーが中間証明書を含む完全な証明書チェーンを送信していないか、本体が信頼していない発行チェーンを使用している可能性があります。まず別のネットワークで再テストし、その後サーバー側で中間証明書を補完してください。検証スキップに長期的に依存してはいけません。
エラー:remote error: tls: handshake failure
原因と対処:リモート側がハンドシェイクを拒否しています。SNI、ALPN、TLSの有効化、ポート、通信方式が一式で一致しているか確認してください。特にWebSocket、gRPC、TCPのパラメータを混在させないことが重要です。
エラー:tls: failed to verify certificate
原因と対処:検証失敗をまとめて示すメッセージです。直後に出ている下位の原因をさらに確認し、時刻の不整合、名前の不一致、信頼チェーンの不足に分けて対処します。
エラー:context deadline exceeded
原因と対処:制限時間内にハンドシェイクが完了していません。まずドメイン解決とリモートポートをテストし、パケットロス、ファイアウォール、ポートの誤りでないことを確認してからTLSパラメータを調べます。
EOF、connection reset by peer、unexpected EOFは、通常、接続が途中で閉じられたことを示しますが、それだけで証明書エラーだとは限りません。同じノードがモバイル回線では使えるのに固定回線でリセットされ続ける場合は、DNSの結果、IPv4とIPv6の経路、リモートの443ポートへTCP接続できるかを優先的に比較してください。
curl -vI --connect-timeout 8 https://node.example.net/
openssl s_client \
-connect node.example.net:443 \
-servername node.example.net \
-showcerts
上記のコマンドは、通常のTLSサイトに対するハンドシェイクを個別に確認するためのものです。実行時は、例示のドメイン名をノード設定で指定された証明書のドメイン名に置き換えてください。OpenSSLの出力にあるsubject、issuer、証明書の開始・終了時刻、Verify return codeは、名前、期限、証明書チェーンの問題を切り分けるのに役立ちます。ただし、プロキシサービスが特定のPathやアプリケーション層プロトコルを要求する場合、通常のHTTPSリクエストが失敗しただけではプロキシ入口が利用できないとは断定できません。
システム時刻とタイムゾーンの確認方法
証明書には有効開始時刻と有効期限が含まれており、本体は自身の時計で現在時刻がその範囲内にあるかを判断します。日付が正しくてもタイムゾーンが間違っていると、表示上の時刻は近く見えても実際のUTC時刻が数時間ずれることがあります。デュアルブート、マザーボードの電源断、仮想マシンの一時停止からの復帰、自動時刻合わせの無効化などがよくある原因です。
- Windowsでは「設定」→「時刻と言語」→「日付と時刻」を開き、「時刻を自動的に設定する」と「タイムゾーンを自動的に設定する」を有効にしてから、「今すぐ同期」をクリックします。
- macOSでは「システム設定」→「一般」→「日付と時刻」を開き、日付と時刻の自動設定を有効にして、現在地のタイムゾーンを確認します。
- Androidでは「設定」→「システム」→「日付と時刻」を開き、ネットワークから提供された時刻とタイムゾーンを有効にします。システムによってメニュー名が多少異なる場合があります。
- Linuxでは
timedatectl statusを使ってUniversal time、RTC time、Time zone、System clock synchronizedを確認し、必要に応じてtimedatectl set-ntp trueを実行してネットワーク時刻同期を有効にします。 - 同期が完了したらクライアントを完全に終了し、カーネルを再起動します。古い接続で遅延テストを繰り返すだけでは不十分です。
トラブル対処では、目視ではなくずれの量を記録してください。本体の時刻が正確な時刻より24時間進んでいると、有効期間内の証明書でも期限切れと判定されることがあります。24時間遅れていると、新しく発行された証明書がまだ有効でないと判定される可能性があります。一部の環境ではハンドシェイク時のわずかな時刻差が許容されますが、数分の余裕を安定した基準と考えてはいけません。
| 確認項目 | 正常な状態 | 異常時の状態 |
|---|---|---|
| システムの日付 | 年月日が実際の日付と一致している | 証明書が期限切れ、またはまだ有効でないと表示される |
| システムのタイムゾーン | 現在地と一致している | UTC時刻が数時間ずれている |
| 自動時刻合わせ | 同期に成功している | 再起動後に大きなずれが再発する |
| 証明書の有効期間 | 現在時刻が開始時刻と終了時刻の間にある | 複数の端末で同じ時刻に失敗する |
結論:まず時計を同期し、その後でノードの項目を変更する
同じサブスクリプション内の複数のTLSノードで同時に「expired or is not yet valid」が出る場合、複数のサーバー証明書が同時に無効になるより、本体の時刻異常である可能性のほうが通常は高くなります。時刻を同期してカーネルを再起動することが、最も手軽な第一歩です。
SNI、アドレス、Host、証明書名の関係
ノードの「アドレス」はDNS解決とTCP接続に使われ、SNIはTLSのClientHelloでクライアントがアクセスしたい名前をサーバーへ伝えます。また、カーネルが証明書名を検証する際にも使われることがあります。両者は同じ場合もあれば、入口の構成によって異なる場合もあります。正しい値は有効なサブスクリプションまたはサーバー設定から取得し、ノードのメモだけを根拠に推測してはいけません。
WebSocket設定にはHostリクエストヘッダーが含まれる場合もあります。HostはTLS確立後のHTTP層、SNIはTLSハンドシェイク層に属します。両方に同じドメイン名を入力することが多くても、完全に同じものではありません。gRPCにはserviceNameも関係し、REALITY設定ではserverName、publicKey、shortIdが同時に関係する場合があります。これらの項目はそれぞれ異なる役割を担います。
| 項目 | 段階 | 代表的な用途 | 入力を誤った場合の症状 |
|---|---|---|---|
| アドレス | DNSとTCP | リモートサーバーを特定する | 解決失敗、接続拒否、タイムアウト |
| ポート | TCPまたはUDP | リモートの待受入口へ接続する | connection refusedまたはタイムアウト |
| SNI | TLS ClientHello | 証明書を選択して名前を検証する | 名前の不一致またはhandshake failure |
| Host | HTTPまたはWebSocket | アプリケーション層の仮想ホストを選択する | TLS成功後にエラーレスポンスが返る |
| ALPN | TLSネゴシエーション | h2またはhttp/1.1をネゴシエートする | リモート側の拒否またはアプリケーション層の不一致 |
v2rayNでは対象ノードを右クリックして「サーバーを編集」を選び、まずセキュリティタイプがTLSになっていることを確認してからSNIを確認します。サブスクリプションの更新後に手動変更が上書きされた場合は、更新のたびに値を変更し直すのではなく、サブスクリプショングループへ戻って元のデータを確認してください。VLESS REALITYでは、一般公開証明書の対処方法をそのまま当てはめてはいけません。ただし、serverName、システム時刻、各パラメータを一式で一致させることは重要です。
SNIを空欄にすると接続できる場合、ずっと空欄のままでよい?
まずサブスクリプションの元設定を確認します。項目が空欄の場合にサーバーアドレスへフォールバックするカーネルもありますが、これはアドレス自体が正しい証明書名である場合に限られます。サーバー側でSNIが明示されているなら、元の値を入力してください。
ノードのアドレスがIPの場合、SNIにもIPを入力する?
必ずしもそうではありません。公開証明書は通常ドメイン名に対して発行されます。ノードがIPで接続し、ドメイン名でSNIと名前の検証を行う場合もあります。サーバーが指定した証明書のドメイン名を使用し、IPをSNIへ自動的にコピーしないでください。
WebSocketのHostとSNIは同じでなければならない?
絶対的な決まりはありません。SNIはTLS段階の名前と証明書を決め、HostはHTTP層のルーティングを決めます。入口の設定によって同じ場合も異なる場合もあるため、サブスクリプションのパラメータを一式で維持してください。
ポートを443から8443へ変更すればハンドシェイク失敗を解決できる?
サーバーが実際に8443で待ち受けている場合に限り可能です。ポートは自由に試す最適化項目ではなく、誤入力すると通常は接続拒否またはタイムアウトになります。
サブスクリプション更新後に名前の不一致が発生した場合は?
更新前後のアドレス、SNI、通信方式、TLSの有効化状態を比較し、同名でもパラメータが異なるノードを選んでいないか確認します。その後、正しいグループを再更新し、古い手動コピーを使い続けないようにします。
証明書チェーンの不備とネットワーク中間装置
サーバーは通常、サイト証明書と必要な中間証明書を送信し、クライアントがシステムまたはカーネルの信頼ストアにあるルート証明書を使って検証します。サーバーがサイト証明書だけを送信している場合、中間証明書をキャッシュ済みの端末では一時的に使えても、新しい端末ではunknown authorityが表示されることがあります。「一部の端末では正常」という事実だけで、サーバーのチェーン設定が完全だとは判断できません。
同じ端末で固定回線とモバイル回線をそれぞれテストします。特定のネットワークだけで失敗する場合は、DNSの解決結果を比較し、ドメインが異なる入口へ解決されていないか確認します。すべてのネットワークと複数のプラットフォームで同じ時刻に同じ証明書チェーンエラーが出るなら、サーバー設定に問題がある可能性が高くなります。企業プロキシ、HTTPS検査装置、独自証明書の環境によって、実際に受け取る発行チェーンが変わる場合もあります。
openssl s_client -showcertsを使い、サーバーが実際に送信した証明書の枚数と発行関係を確認します。- 出力末尾の検証結果を確認し、最初の証明書のドメイン名だけを見て判断しないでください。
- ノードのアドレスと設定上のSNIをそれぞれ使ってテストし、2回の接続で異なる証明書が返されるか確認します。
- v2rayN、v2rayNG、v2flyNGのカーネルを終了して再起動し、古いセッションが再利用されていないことを確認します。
- サーバーから中間証明書が送信されていない場合は、各クライアントで検証を回避するのではなく、サーバー側に完全な証明書チェーンを設定します。
結論:複数端末の比較で本体側かサーバー側かをすばやく切り分ける
同じノードでWindows、Android、Linuxのすべてから同じ証明書チェーンエラーが返る場合は、クライアントの再インストールを繰り返すのをやめ、リモートが送信する完全なチェーンと入口設定を確認してください。
証明書検証のスキップは短時間の切り分けに限る
v2rayNと関連カーネルの設定にあるallowInsecureは、証明書検証に失敗してもTLS接続を継続することを意味します。期限切れの証明書、誤ったSNI、不足している中間証明書を修復するものではなく、該当する検証結果を回避するだけです。接続が復旧しても、問題箇所が証明書検証段階にある可能性を示すだけで、現在のパラメータが正しいとは限りません。
この項目は、条件を管理した比較テストに一度だけ使用します。アドレス、ポート、プロトコル、通信方式、ルーティングを変えず、証明書検証の設定だけを切り替えます。カーネルを起動して1回リクエストを実行したら、検証を元に戻し、元のログに基づいて時刻、SNI、証明書チェーンを修正します。SNI、Host、ポート、allowInsecureを同時に変更すると、テスト結果から判断できなくなります。
- 元のノードをコピーして保存し、現在のallowInsecureの状態を記録する。
- テスト対象の出所が明確で、ほかの接続パラメータを同時に変更していないことを確認する。
- 一時的に有効化してカーネルを再起動し、元のx509エラーが消えるか確認する。
- それでもタイムアウトする場合は、DNS、TCPポート、通信方式、サーバーの状態を引き続き確認する。
- 接続が復旧したら、すぐに証明書検証を元へ戻し、システム時刻、SNI、完全な証明書チェーンを修正する。
検証を長期的にスキップすると、リモートの身元を確認する重要な手段を失います。特にノードアドレスがDNSによって複数の入口へ向けられている場合、クライアントは証明書名と発行チェーンで、接続先が想定したサービスかどうかを判断できなくなります。サブスクリプションの項目をサーバー設定と一致させ、システムの信頼ストアと時刻同期を正常に保つほうが安全です。
| テスト結果 | 判断できること | 次の手順 |
|---|---|---|
| 有効化するとすぐ接続が復旧する | 問題は証明書検証段階に集中している | 検証を元に戻し、時刻、名前、証明書チェーンを確認する |
| 有効化してもタイムアウトする | 証明書検証だけの問題ではない | DNS、ポート、パケットロス、通信パラメータを確認する |
| 有効化後にプロトコルエラーが出る | TLSは通過したが上位設定が一致していない | UUID、プロトコル、Path、Host、serviceNameを確認する |
| 1つのネットワーク環境だけで失敗する | 経路または解決結果が異なる可能性がある | DNS、IPv4、IPv6、リモート入口を比較する |
再現可能な最終トラブル対処手順
証明書の問題は、複数のパラメータを同時に変更すると原因の手がかりを失いやすくなります。実際の作業ではノードとネットワークを固定し、時刻を記録して、ログに出た最初の低レベルエラーを基準にします。各手順の後にカーネルを再起動し、同じリクエストを繰り返してこそ、結果を比較できます。
- システム時刻を確認:日付、タイムゾーン、ネットワーク時刻を同期し、ずれを数分や数時間ではなく秒単位まで縮めます。
- 解決結果を確認:ノードのドメインを解決できるか確認し、IPv4とIPv6が想定した入口を指しているか判断します。
- TCPポートを確認:サブスクリプションに記載された443、8443などの明示的なポートを確認し、自己判断で置き換えないでください。
- TLSの有効化を確認:ノードがTLSを要求する場合は必ず有効にし、非TLSの入口へTLSを無理に適用しないでください。
- SNIを確認:サブスクリプションで指定された証明書名を使い、ノードアドレス、メモ、Host、serviceNameを区別します。
- 証明書の有効期間を確認:現在のUTC時刻と証明書の開始・終了時刻を比較し、本体の時計が原因かサーバー証明書の期限切れかを判断します。
- 証明書チェーンを確認:サーバーが必要な中間証明書を送信しているか確認し、複数のプラットフォームで比較します。
- 通信パラメータを確認:WebSocketのPathとHost、gRPCのserviceName、REALITYの関連項目が一式で一致していることを確認します。
- 短時間の比較検証:切り分けの段階に限ってallowInsecureを切り替え、テスト終了後は証明書検証を元に戻します。
- 元のサブスクリプションへ戻る:手動変更が増えすぎた場合は正しいグループを再インポートし、テスト設定を1つだけ残します。
ここまで確認すると、エラーは通常、次の3つに明確に分類できます。本体の時刻または信頼環境の異常、ノードの項目と入口の不一致、サーバーの証明書または待受設定の異常です。1つ目だけは本体側ですぐ修正できます。2つ目は正しいサブスクリプションのパラメータへ戻し、3つ目はサーバー側で証明書の更新、証明書チェーンの補完、入口設定の修正が必要です。
結論:証明書エラーは発生した経路の段階ごとに対処する
まずログでDNS、TCP、TLS、プロキシプロトコルを切り分け、「時刻 → SNI → 有効期限 → 証明書チェーン → 通信パラメータ」の順に確認します。検証スキップは変数を管理した診断に一度だけ使い、最終設定にはしないでください。