TLS 핸드셰이크 및 인증서 오류 해결: 시스템 시간, 인증서 유효 기간과 SNI

시간 동기화, 인증서 유효 기간, 도메인 일치 여부와 SNI 설정을 차례로 확인해 원인을 좁히고, 일상적인 해결책으로 인증서 검증을 건너뛰면 안 되는 이유를 설명합니다.

이 글 한눈에 보기

v2rayN 또는 Xray 로그에 TLS 핸드셰이크, 인증서 만료, 도메인 불일치, unknown authority 메시지가 표시될 때 유용합니다. 먼저 Windows 시간을 확인하고 인증서 유효 기간과 접속 도메인을 점검한 뒤 SNI, 구독 항목과 서버 설정을 확인해 검증 비활성화로 문제를 숨기지 않도록 합니다.

TLS 오류가 발생하는 계층

TLS 핸드셰이크는 프록시 코어와 원격 서버가 암호화된 연결을 설정하는 단계에서 수행됩니다. v2rayN은 구독을 가져오고 서버를 편집하며 설정을 생성하고, Xray 코어가 실제 연결을 담당합니다. 시스템 프록시 또는 TUN은 어떤 애플리케이션 트래픽을 클라이언트로 보낼지 결정합니다. 이 세 계층은 나누어 판단해야 합니다. 브라우저가 프록시에 연결되지 않았다면 노드 설정이 올바르더라도 코어를 거치지 않습니다. 애플리케이션이 이미 연결되었지만 코어가 TLS 핸드셰이크를 완료하지 못할 때 인증서나 핸드셰이크 관련 오류가 로그에 나타납니다.

VMess와 VLESS는 프록시 프로토콜 이름이고 TLS는 전송 보안 계층입니다. VLESS를 사용한다고 해서 반드시 TLS가 활성화되는 것은 아니며, VMess를 사용한다고 해서 인증서 검증 방식이 달라지는 것도 아닙니다. 실제로 확인해야 할 항목은 노드 전송 설정의 보안 유형, 대상 포트, 서버 주소, SNI와 인증서가 적용되는 도메인입니다. 443은 흔히 사용하는 TLS 서버 포트일 뿐 필수값은 아닙니다. 서버가 다른 포트로 설정되어 있다면 클라이언트도 같은 값을 사용해야 합니다.

애플리케이션 연결코어 연결 시도SNI 전송인증서 수신인증서 검증연결 설정
443
일반적인 TLS 서버 포트
TLS 1.2
일반적인 프로토콜 버전
TLS 1.3
일반적인 프로토콜 버전
날짜 2개
notBefore 및 notAfter

문제 해결 시에는 TLS 오류 중 가장 먼저 발생한 항목을 찾고, 뒤이어 나타나는 연결 종료, EOF 또는 재시도 실패만 보지 마세요. 인증서 검증에 실패하면 코어는 대개 현재 연결을 종료하며, 이후의 읽기 실패는 그 결과일 뿐입니다. 같은 구독의 모든 노드에서 동시에 문제가 발생한다면 먼저 로컬 시간, 네트워크 중간 장비와 클라이언트 전역 설정을 확인하세요. 특정 도메인 하나만 문제가 발생한다면 해당 노드의 인증서, SNI 또는 서버 배포 설정일 가능성이 높습니다.

1단계: Windows 시간, 시간대와 동기화 상태 확인

인증서에는 유효 시작 시간 notBefore와 만료 시간 notAfter가 포함됩니다. 코어는 로컬 시스템 시간을 기준으로 인증서가 현재 유효한지 판단하므로 날짜, 시간대 또는 시계 오차 때문에 “아직 유효하지 않음”이나 “이미 만료됨” 오류가 발생할 수 있습니다. 예를 들어 컴퓨터가 실제로 UTC+8 지역에 있지만 시간대가 UTC로 설정되어 있고 사용자가 시계를 현지 시간으로 수동 조정했다면 화면에 표시되는 시간은 비슷해 보여도 내부 시간 기준에는 오차가 생길 수 있습니다.

먼저 Windows의 「설정」→「시간 및 언어」→「날짜 및 시간」을 열고 “자동으로 시간 설정”과 “자동으로 시간대 설정”이 현재 환경에 맞는지 확인한 다음 지금 동기화를 실행하세요. 회사 네트워크나 관리되는 장치는 내부 시간 원본을 사용하도록 지정되어 있을 수 있습니다. 이 경우 정책을 임의로 바꾸지 말고 동기화 원본과 오차를 기록한 뒤 장치 관리자에게 확인을 요청하세요.

  1. 시스템의 날짜, 시, 분을 비교하고 작업 표시줄에 표시되는 분만 확인하지 마세요.
  2. 시간대 이름이 실제 위치와 일치하는지 확인하고, 서머타임 지역이라면 현재 오프셋도 점검하세요.
  3. 상태를 조회해 최근 성공한 동기화 시각과 시간 원본을 확인하세요.
  4. 수정한 뒤 v2rayN을 완전히 종료하고 코어를 다시 시작한 다음 같은 노드로 재시험하세요.
w32tm /query /status
tzutil /g
powershell -NoProfile -Command "Get-Date -Format o"

수동 점검에서는 60초를 인증서 표준의 일괄 허용 오차가 아니라 경고 기준으로 사용할 수 있습니다. 로컬 시간이 신뢰할 수 있는 시간 원본과 60초 넘게 차이 난다면 먼저 시계를 바로잡으세요. TLS 구현과 서버 배포 방식에 따라 결과가 다를 수 있으므로 특정 구현에 있을지도 모르는 시간 여유에 기대면 안 됩니다. 시간 수정 후 모든 노드가 동시에 복구된다면 문제는 로컬 시간 경로에 있으므로 구독 노드를 하나씩 수정할 필요가 없습니다.

2단계: 인증서 유효 기간, 발급 체인과 도메인 확인

시간이 정확하다면 인증서 자체를 확인하세요. 인증서 세부 정보에서 최소한 유효 기간, 주체 대체 이름과 발급 체인이 시스템이 신뢰하는 루트 인증서까지 연결되는지를 살펴봐야 합니다. 화면에 표시된 만료일만 확인해서는 부족합니다. 인증서가 아직 유효하지 않을 수도 있고 중간 인증서 설정이 누락되었을 수도 있기 때문입니다. 서버에서 인증서를 교체하면서 리프 인증서만 갱신하고 올바른 중간 인증서를 제공하지 않으면 일부 환경에서는 성공하지만 다른 환경에서는 발급자를 알 수 없다는 오류가 발생할 수 있습니다.

도메인 일치 여부는 인증서의 Subject Alternative Name을 주요 기준으로 판단합니다. 클라이언트의 접속 주소가 IP인데 인증서에는 도메인만 포함되어 있다면 IP로 직접 검증할 때 대개 실패합니다. 접속 주소는 한 도메인인데 SNI에 인증서가 적용되지 않는 다른 도메인을 입력해도 이름 불일치가 발생합니다. 와일드카드 인증서의 적용 범위도 제한적입니다. 예를 들어 *.example.com은 일반적으로 한 단계 하위 도메인을 포함하지만, 더 깊은 하위 도메인이나 루트 도메인까지 포함한다고 볼 수는 없습니다.

점검 항목 확인해야 할 값 오류 발생 시 대응 방향
유효 시작 시간 현재 시간이 notBefore보다 빠르지 않음 로컬 시간을 보정하거나 서버에서 유효한 인증서를 다시 배포
만료 시간 현재 시간이 notAfter보다 빠름 서버에서 갱신하고 새 인증서가 로드되었는지 확인
도메인 범위 실제 SNI가 인증서 이름 목록에 포함됨 SNI를 수정하거나 실제 도메인에 인증서를 발급
인증서 체인 리프 인증서, 중간 인증서와 신뢰할 수 있는 루트 사이에 체인이 구성됨 중간 인증서를 보완하고 시스템 신뢰 저장소와 네트워크 가로채기를 확인

결론: 단일 노드 오류와 전체 노드 오류를 나누어 처리

서버 이름 하나에서만 오류가 발생한다면 먼저 해당 노드의 인증서와 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 로그 문구는 코어 버전과 오류 경로에 따라 달라질 수 있지만, 핵심 정보는 대개 시간, 이름, 신뢰 체인과 핸드셰이크 협상의 네 가지로 분류할 수 있습니다. 로그를 복사할 때는 오류 전후의 접속 대상과 시각을 남기고 UUID, 구독 주소 또는 전체 설정은 공개하지 마세요. 중요한 것은 재시도 기록이 몇 줄 쌓였는지가 아니라 첫 실패가 어느 계층에서 발생했는지입니다.

오류: 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, 잘못된 포트 또는 서버 가상 호스트를 수정해 주지는 않습니다. 이 옵션을 사용하면 겉으로는 연결이 계속될 수 있지만 원격 신원을 확인하는 중요한 절차가 제거되어 잘못된 설정이나 중간 네트워크의 인증서 교체를 발견하기 어려워집니다. 따라서 통제된 환경에서 잠시 비교할 때만 사용하고 일상 설정의 결론으로 삼아서는 안 됩니다.

더 효과적인 비교 방법은 한 번에 조건 하나만 바꾸는 것입니다. 예를 들어 서버 주소, 포트와 네트워크는 유지한 채 시스템 시간만 수정하세요. 시간이 정확해진 뒤에는 SNI만 수정하고, 그다음 서버의 인증서 체인을 확인합니다. 각 단계마다 코어를 다시 시작하고 새 연결을 설정한 뒤 오류가 “아직 유효하지 않음”에서 “이름 불일치”로 바뀌었는지 또는 이후 프로토콜 단계로 진행했는지 기록하세요. 오류가 변했다는 사실 자체가 원인을 좁히는 단서입니다.

  1. allowInsecure: false로 되돌려 전체 인증서 검증이 포함된 테스트 결과를 확인하세요.
  2. 시스템 날짜, 시간대와 동기화 원본을 확인한 다음 v2rayN을 완전히 다시 시작하세요.
  3. 인증서의 notBefore, notAfter와 도메인 목록을 확인하세요.
  4. 서버 주소, 포트, SNI, Host와 구독 원본 항목을 확인하세요.
  5. 서버 가상 호스트, 인증서 체인과 실제 리스닝 진입점을 점검하세요.
  6. 마지막으로 다른 네트워크를 비교해 중간 장비의 영향을 판단하세요.

결론: 전체 검증 성공을 복구 기준으로 삼기

복구된 설정은 인증서 검증을 활성화한 상태에서 새 연결을 설정해야 하며, 구독을 업데이트한 뒤에도 올바른 상태를 유지해야 합니다. 검증을 끈 경우에만 연결된다면 근본 원인은 아직 해결되지 않은 것입니다.

마지막에는 일정한 순서의 판단 흐름으로 정리할 수 있습니다. 모든 노드가 실패하면 시간과 네트워크를 먼저 확인하고, 특정 도메인 하나만 실패하면 인증서와 SNI를 확인하세요. 이름 오류는 필드 대응 관계를, 발급자를 알 수 없는 오류는 인증서 체인을 점검하고, 시간 초과만 발생하면 DNS, 포트와 리스닝으로 돌아가세요. 이렇게 하면 시스템 프록시, 라우팅 규칙과 TLS 매개변수 사이를 무작정 바꾸는 일을 줄이고, 문제가 클라이언트 설정, 프록시 코어 로그, 구독 데이터 또는 서버 배포 중 어디에 속하는지도 명확히 파악할 수 있습니다.

클라이언트 다운로드