TLSハンドシェイクと証明書エラーの対処:システム時刻・証明書の有効期間・SNI

時刻同期、証明書の有効期間、ドメイン名の一致、SNI設定を順に確認し、証明書検証の無効化を常用すべきでない理由を解説します。

この記事の要点

v2rayNやXrayのログにあるTLSハンドシェイク、証明書の期限切れ、ドメイン名の不一致、unknown authorityなどを確認する記事です。まずWindowsの時刻、次に証明書の有効期間と接続先ドメイン、最後にSNI・サブスクリプションの項目・サーバー設定を確認し、検証無効化で本当の原因を隠さないようにします。

TLSエラーが発生する層

TLSハンドシェイクは、プロキシコアがリモートサーバーと暗号化接続を確立する段階で行われます。v2rayNはサブスクリプションのインポート、サーバー編集、設定生成を担当し、Xrayコアが実際の接続を処理します。システムプロキシまたはTUNは、どのアプリの通信をクライアントに通すかを決めます。この3層は分けて判断する必要があります。ブラウザーがプロキシに接続していなければ、ノード設定が正しくてもコアは経由しません。アプリが接続済みでもコアがTLSハンドシェイクを完了できない場合に、証明書やハンドシェイク関連のエラーがログに記録されます。

VMessとVLESSはプロキシプロトコルの名称で、TLSはトランスポートのセキュリティ層です。VLESSを使うからといって必ずTLSが有効になるわけではなく、VMessだから証明書の検証方法が異なるわけでもありません。確認すべきなのは、ノードのトランスポート設定にあるセキュリティ方式、接続先ポート、サーバーアドレス、SNI、証明書が対象とするドメインです。443は一般的なTLS用ポートにすぎず、必須ではありません。サーバーが別のポートで設定されている場合、クライアントも同じ値にする必要があります。

アプリ接続コアが発信SNI送信証明書受信証明書検証接続確立
443
一般的なTLSサーバーポート
TLS 1.2
一般的なプロトコルバージョン
TLS 1.3
一般的なプロトコルバージョン
2つの日付
notBeforeとnotAfter

トラブル対処では、後続の接続終了、EOF、再試行失敗だけを見るのではなく、最初に出たTLSエラーを確認します。証明書検証に失敗すると、コアは通常その接続を終了するため、その後の読み取り失敗は結果にすぎません。同じサブスクリプションの全ノードが同時に異常になった場合は、まず端末の時刻、ネットワークの中間装置、クライアントの全体設定を確認します。1つのドメインだけに問題がある場合は、そのノードの証明書、SNI、サーバー側の構成である可能性が高くなります。

手順1:Windowsの時刻・タイムゾーン・同期状態を確認する

証明書には有効開始時刻notBeforeと有効期限notAfterが含まれます。コアは端末のシステム時刻を使って証明書が現在有効かを判断するため、日付、タイムゾーン、時計のずれによって「まだ有効ではない」または「期限切れ」と判定されることがあります。たとえば実際の場所が日本標準時なのにタイムゾーンをUTCに設定し、さらに時計を手動で現地時刻に合わせると、表示上は正しく見えても内部の時刻基準にずれが生じる場合があります。

まずWindowsの「設定」→「時刻と言語」→「日付と時刻」を開き、「時刻を自動的に設定する」と「タイムゾーンを自動的に設定する」が現在の環境に合っているか確認してから、「今すぐ同期」をクリックします。社内ネットワークや管理対象端末では内部の時刻ソースが指定されていることがあるため、方針を勝手に変更せず、同期元とずれを記録して端末管理者に確認してください。

  1. システムの日付、時、分を照合し、タスクバーに表示された分だけを確認しない。
  2. タイムゾーン名が現在地と一致しているか確認し、夏時間の地域では現在の時差も確認する。
  3. 状態照会を実行し、直近の同期成功時刻と時刻ソースを確認する。
  4. 修正後はv2rayNを完全に終了し、コアを再起動して同じノードで再テストする。
w32tm /query /status
tzutil /g
powershell -NoProfile -Command "Get-Date -Format o"

手動確認では60秒を警戒ラインとして使えますが、証明書の標準における一律の許容値ではありません。端末と信頼できる時刻ソースの差が60秒を超えているなら、先に時計を修正してください。TLS実装やサーバー構成によって挙動は異なるため、特定の実装にあるかもしれない時刻の猶予に頼るべきではありません。時刻を修正した後に全ノードが同時に復旧したなら、原因は端末の時刻経路にあり、サブスクリプションのノードを1つずつ変更する必要はありません。

手順2:証明書の有効期間・発行チェーン・ドメインを確認する

時刻が正しいことを確認したら、次に証明書そのものを調べます。証明書の詳細では少なくとも、有効期間、Subject Alternative Name、発行チェーンがシステムの信頼するルート証明書までつながるかの3点を確認します。画面に表示された有効期限だけでは不十分です。証明書がまだ有効になっていない場合や、中間証明書の設定が不足している場合もあるためです。サーバー側で証明書を更新した際、リーフ証明書だけを差し替えて正しい中間証明書を提供していないと、環境によっては接続できても、別の環境では発行者不明と表示されることがあります。

ドメイン名の一致は、主に証明書のSubject Alternative Nameを基準に確認します。クライアントの接続先がIPアドレスで、証明書がドメイン名しか対象にしていない場合、IPアドレスで直接検証すると通常は失敗します。接続先があるドメインでも、SNIに証明書の対象外の別ドメインを指定すれば、名前の不一致になります。ワイルドカード証明書の対象範囲も限定されています。たとえば *.example.com は通常1階層下のサブドメインを対象にできますが、より深い階層やルートドメインまで対象になるとは限りません。

確認項目 確認する値 異常時の対処方針
有効開始時刻 現在時刻がnotBefore以降であること 端末の時刻を調整するか、サーバー側で有効な証明書を再配置する
有効期限 現在時刻がnotAfterより前であること サーバー側で更新し、新しい証明書が読み込まれていることを確認する
ドメインの対象範囲 実際のSNIが証明書の名前一覧に含まれていること SNIを修正するか、実際のドメイン用に証明書を発行する
証明書チェーン リーフ証明書、中間証明書、信頼されたルートでチェーンを構築できること 中間証明書を補い、システムの信頼ストアとネットワークによる検査を確認する

結論:単一ノードの異常と全ノードの異常は分けて対処する

1つのサーバー名だけでエラーが出る場合は、まずそのノードの証明書とSNIを確認します。異なるドメインの複数ノードで発行者不明や時刻エラーが同時に出る場合は、システム時刻、システムの信頼ストア、ネットワーク上のTLS検査装置を優先して確認してください。

「証明書の期限切れ」と「既存の接続がまだ使えること」も区別する必要があります。確立済みの長時間接続は、証明書の期限が切れた瞬間に必ず切断されるとは限りません。一方、新しい接続では再びハンドシェイクと検証が行われるため、クライアント起動直後だけ失敗したり、維持中の接続は一時的に正常だったり、ネットワークを切り替えて初めて問題が現れたりします。判断時はコアを完全に再起動して新しい接続を確立し、維持中のセッションだけを根拠にしないでください。

手順3:SNI・サーバーアドレス・サブスクリプション項目を確認する

SNIは、TLSハンドシェイク中にクライアントが送信するサーバー名で、サーバーはそれを使って証明書や仮想ホストを選択できます。DNSの名前解決結果と同じものではありません。サーバーアドレスは接続先のIPを決め、SNIはハンドシェイク時にアクセス先として通知する名前を決めます。特定の構成では両者を異なる値にできますが、サーバー側が対応している必要があり、返される証明書は検証に使うサーバー名を対象にしていなければなりません。

v2rayNでは、まず対象サーバーを選択し、右クリックメニューの「サーバーを編集」でアドレス、ポート、トランスポートのセキュリティ、SNIを確認します。全体設定を確認する場合は、「設定」→「パラメータ設定」に進みます。画面のバージョンによって項目のグループ分けは変わることがありますが、「アドレス」「Host」「SNI」を同じ値として機械的にコピーしないでください。WebSocketのHostはHTTPリクエストヘッダー、SNIはTLSハンドシェイクの項目です。サーバー側のリバースプロキシが両者に同じ値を求めることもあれば、異なる値を明示的に求めることもあります。

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

エラー:x509: certificate is valid for ..., not ...

原因と対処:実際の検証名が証明書の対象範囲に含まれていません。サーバーアドレスとSNIを確認し、証明書に含まれる正しいドメイン名を使うか、サーバー側で一致する証明書を再配置してください。

エラー:remote error: tls: handshake failure

原因と対処:サーバーがハンドシェイク中に接続を拒否しています。SNIに対応する仮想ホスト、TLSバージョン、ポート、サーバーの待ち受けを確認し、クライアント側でシステムプロキシを何度も切り替えるだけにしないでください。

ノードがサブスクリプション由来の場合は、まず元の値を記録してからサブスクリプションを一度更新し、項目を比較することをおすすめします。手動で修正すると一時的に直るものの、次回の更新後に再び失敗するなら、サブスクリプションの生成元が誤った値を配信し続けています。正しい対処は、インポートのたびに編集することではなく、サブスクリプションの生成側を修正することです。同じドメイン名のノードが複数ある場合も、それぞれのポートとトランスポート設定を確認してください。名前が同じだからといって、サーバー側の入口まで完全に同じとは限りません。

手順4:エラー原文から範囲を絞り込む

TLSログの文言はコアのバージョンやエラー経路によって変わりますが、重要な情報は通常、時刻、名前、信頼チェーン、ハンドシェイクのネゴシエーションの4種類に整理できます。ログを共有する際は、エラー前後の接続先と時刻を残し、UUID、サブスクリプションURL、完全な設定を公開する必要はありません。確認の重点は、最初の失敗がどの層で発生したかであり、再試行ログが何件積み重なったかではありません。

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

原因と対処:端末の時刻が証明書の有効期間外にあるか、サーバー側の証明書が実際に期限切れです。まずWindowsの時刻を同期し、次に証明書のnotBeforeとnotAfterを確認してください。

エラー:x509: certificate signed by unknown authority

原因と対処:証明書チェーンが信頼されたルートまでつながっていません。サーバーが中間証明書を完全に送信しているか確認し、システムの信頼ストアまたはネットワーク検査装置によって証明書が変更されていないか確認してください。

エラー:tls: failed to verify certificate

原因と対処:証明書の検証段階で失敗しています。同じログにある具体的なx509の原因を続けて確認し、時刻、名前、発行チェーンのいずれかに応じて対処してください。この一文だけで原因を判断してはいけません。

エラー:unexpected EOF

原因と対処:接続先またはネットワーク上の中間装置がハンドシェイク中に接続を閉じています。まず、その前に明確な証明書エラーが出ていないか確認し、次にポート、SNI、サーバーの待ち受け、ネットワークによる検査を確認してください。

ログに証明書情報がなくタイムアウトだけが出る場合、接続がまだ証明書検証の段階に到達していない可能性があります。この場合は、まずドメイン名の解決、接続先IP、TCPポート、ネットワーク到達性を確認します。たとえば通常のHTTPだけを提供するポートをTLSの入口として指定するなど、誤ったポートに接続すると、接続終了や認識できないハンドシェイクエラーになることがあります。ポートに到達できることは、プログラムが待ち受けていることを示すだけで、想定したTLSサービスであることまでは保証しません。

社内、学校、または管理対象ネットワークでTLS検査が行われている場合、クライアントに見える証明書の発行者が直接接続時と異なることがあります。許可を得たうえで、別の適切なネットワークに切り替えて比較できます。同じ端末、同じノードでネットワークだけを変更して挙動が変わるなら、中間ネットワークを引き続き確認します。ネットワークを変えてもまったく同じドメイン名または有効期間のエラーが出るなら、ノード設定またはサーバー証明書の問題である可能性が高くなります。

証明書検証を長期的に無効化すべきでない理由

「証明書検証をスキップする」設定は通常、クライアントが証明書名と信頼チェーンを確認しないようにするだけです。期限切れの証明書、誤ったSNI、誤ったポート、サーバー側の仮想ホストを修正するものではありません。この設定で表面的には接続できるようになる場合がありますが、接続先の身元を確認する重要な手順が失われるため、誤設定や中間ネットワークによる証明書の置き換えを発見しにくくなります。管理された環境で短時間の比較に使う場合を除き、日常設定の結論にしてはいけません。

より有効な比較方法は、一度に1つの条件だけを変更することです。たとえばサーバーアドレス、ポート、ネットワークを変えずにシステム時刻だけを修正します。時刻に問題がなければSNIだけを修正し、その後にサーバー側の証明書チェーンを確認します。各手順でコアを再起動して新しい接続を確立し、エラーが「まだ有効ではない」から「名前が一致しない」に変わったか、後続のプロトコル段階に進んだかを記録します。エラーの変化自体が原因を絞る手がかりになります。

  1. allowInsecure: false に戻し、テスト結果に完全な証明書検証が含まれるようにする。
  2. システムの日付、タイムゾーン、同期元を確認してから、v2rayNを完全に再起動する。
  3. 証明書のnotBefore、notAfter、ドメイン名一覧を確認する。
  4. サーバーアドレス、ポート、SNI、Host、サブスクリプションの元の項目を確認する。
  5. サーバー側の仮想ホスト、証明書チェーン、実際の待ち受け入口を確認する。
  6. 最後に異なるネットワークを比較し、中間装置の影響があるか判断する。

結論:完全な検証を有効にした接続成功を修復基準にする

修正後の設定は、証明書検証を有効にした状態で新しい接続を確立でき、サブスクリプション更新後も正しい状態を維持できる必要があります。検証を無効にした場合だけ接続できるなら、根本原因は解決していません。

最後は、順序を固定した判断手順で確認できます。全ノードが失敗する場合は時刻とネットワーク、単一ドメインだけが失敗する場合は証明書とSNI、名前のエラーは項目の対応関係、発行者不明は証明書チェーン、タイムアウトだけならDNS・ポート・待ち受けを確認します。これにより、システムプロキシ、ルーティング、TLSパラメータの間で闇雲に試すことを避け、問題がクライアント設定、プロキシコアのログ、サブスクリプションデータ、サーバー構成のどこにあるかを明確にできます。

クライアントをダウンロード