NET::ERR_CERT_AUTHORITY_INVALID 원인별 해결
NET::ERR_CERT_AUTHORITY_INVALID 는 원인을 알려주지 않는 에러다. 화면에는
“연결이 비공개로 설정되어 있지 않습니다”만 뜨고, 그 뒤에는 서로 완전히 다른
네다섯 가지 상황이 숨어 있다. 어떤 건 서버 문제고, 어떤 건 그 PC만의 문제이며,
어떤 건 회사 보안 장비가 정상 동작한 결과다.
이 에러의 의미는 하나로 좁혀진다 — 브라우저가 이 인증서에서 출발해 자신이 신뢰하는 루트까지 경로를 만들지 못했다. 인증서가 잘못됐다는 뜻이 아니다. “내가 아는 곳에서 나온 것임을 확인하지 못했다”는 뜻이다.
먼저: 옆자리 에러와 구분
Chrome의 인증서 에러는 코드마다 원인 범위가 다르다. 여기를 헷갈리면 엉뚱한 걸 고치게 된다.
| 에러 코드 | 의미 | 이 글의 대상 |
|---|---|---|
ERR_CERT_AUTHORITY_INVALID (-202) |
신뢰 경로를 못 만듦 | ✅ |
ERR_CERT_COMMON_NAME_INVALID (-200) |
접속한 도메인이 인증서에 없음 | ❌ SAN 문제 |
ERR_CERT_DATE_INVALID |
유효기간 밖 (만료 또는 시계) | ❌ |
ERR_CERT_REVOKED |
폐기된 인증서 | ❌ |
COMMON_NAME_INVALID 은 발급자는 신뢰되는데 도메인이 안 맞는 경우다. 요즘
브라우저는 Common Name을 보지 않고 subjectAltName(SAN)만 본다. CN에 도메인이
있어도 SAN에 없으면 실패한다. 이건 인증서를 다시 발급받아야 하는 문제고, 신뢰
경로와는 무관하다.
AUTHORITY_INVALID 만 아래로 이어진다.
원인 판별 — 30초
원인마다 처리가 완전히 다르므로 발급자부터 확인한다. 주소창 → 인증서 정보 →
발급 기관(Issued by).
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer
읽는 법:
issuer 가 이렇게 나오면 |
원인 |
|---|---|
subject 와 동일 |
자기서명 (원인 1) |
사내 CA 이름 (... Internal CA 등) |
루트 미배포 (원인 2) |
| 공개 CA인데 브라우저만 실패 | 중간 인증서 누락 (원인 3) |
보안 제품 이름 (Fortinet, Zscaler, 백신 등) |
TLS 검사 장비 (원인 4) |
issuer 가 예상과 다르면 그 지점에서 이미 답이 나온 것이다. 서버 설정을 뒤지기 전에 여기부터 본다.
원인 1 — 자기서명 인증서
subject 와 issuer 가 같으면 자기서명이다. 개발 서버, 장비 관리 페이지,
내부 도구에서 흔하다.
subject=CN = dev.internal
issuer=CN = dev.internal ← 동일
브라우저 입장에서는 정상 동작이다. 신뢰할 근거가 없는 인증서를 거부한 것뿐이다.
처리 — 상황에 따라 갈린다.
- 공개 서비스 — 자기서명을 쓰면 안 된다. Let’s Encrypt로 교체한다. 내부용 도메인이라 HTTP-01 챌린지가 어렵다면 DNS-01을 쓰면 공인 인증서를 받을 수 있다
- 내부 전용 — 사내 CA를 만들어 그 루트를 배포하는 쪽이 낫다(원인 2 참고). 장비마다 예외를 클릭하게 두면, 진짜 중간자 공격이 왔을 때 아무도 알아채지 못한다
- 잠깐 확인만 — 브라우저 예외 처리. 다만 이건 해결이 아니라 유예다
절대 하지 말 것: 사용자에게 “고급 → 계속 진행”을 안내하고 끝내는 것. 그 습관이 자리 잡으면 경고가 기능을 잃는다.
원인 2 — 사내 CA 루트 미배포
issuer 가 사내 CA인데 그 PC에 루트가 없는 경우다. 일부 PC만 실패하는 게
특징이다. 배포가 안 된 기기에서만 터진다.
Firefox가 특히 자주 튄다. OS 트러스트 스토어를 보지 않고 자체 스토어를 쓰기 때문이다. 그룹 정책으로 사내 루트를 밀어 넣어도 Firefox는 모른다.
처리 — 루트를 각 트러스트 스토어에 설치한다.
# Windows — 로컬 컴퓨터의 신뢰할 수 있는 루트
Import-Certificate -FilePath .\corp-root-ca.crt `
-CertStoreLocation Cert:\LocalMachine\Root
# Debian/Ubuntu
sudo cp corp-root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# RHEL 계열
sudo cp corp-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
Firefox는 별도로 처리한다. 두 방법 중 하나다.
about:config→security.enterprise_roots.enabled를true— OS 스토어를 같이 참조한다. 정책 파일(policies.json)로도 배포 가능하다- 또는 엔터프라이즈 정책의
Certificates.Install로 루트를 직접 배포
서버·컨테이너도 잊지 말 것. 사람이 쓰는 PC만 챙기고 배치 작업이나 컨테이너를 빼먹으면, 새벽에 도는 잡이 조용히 실패한다. 컨테이너 이미지는 빌드 단계에서 루트를 넣어야 한다.
원인 3 — 중간 인증서 누락
issuer 가 정상적인 공개 CA인데도 실패하는 경우다. 서버가 중간 인증서를 안
보내서 경로가 끊긴 것이다.
특징이 뚜렷하다. 크롬은 되는데 curl·자바·모바일 앱이 실패한다. 크롬은 AIA fetching으로 몰래 채워 넣기 때문이다.
# 서버가 보낸 것만으로 검증되는가
openssl s_client -connect example.com:443 -servername example.com \
-verify_return_error </dev/null 2>&1 | grep "Verify return code"
Verify return code: 0 (ok) 가 아니면 서버 체인이 불완전하다. 처리는 fullchain
배포다. 판별과 수정 절차는 인증서 체인 오류가 브라우저마다 다르게 보이는 이유에서 자세히 다뤘다.
원인 4 — TLS 검사 장비 (가장 오진하기 쉬움)
issuer 에 보안 제품 이름이 나오면 여기다.
issuer=CN = FortiGate CA, O = Fortinet
issuer=CN = Zscaler Intermediate Root CA
issuer=CN = AhnLab ...
방화벽·프록시·백신이 HTTPS 트래픽을 검사하려고 연결을 끊고 자기 인증서로 다시 맺은 것이다. 구조적으로는 중간자 공격과 동일하고, 그래서 그 장비의 루트가 설치돼 있지 않으면 브라우저가 정확히 이 에러를 낸다.
서버는 아무 잘못이 없다. 이 상태에서 서버 인증서를 재발급하면 시간만 버린다.
판별이 쉬운 이유는 서버에서 본 인증서와 클라이언트에서 본 인증서가 다르다는 것이다.
# 서버에서 (검사 장비를 안 거침)
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -issuer -fingerprint -sha256
문제가 나는 PC의 브라우저에서 본 지문과 이 값이 다르면 중간에 누가 끼어든 것이다.
처리
- 사내 정책상 정상 동작이면 → 그 장비의 루트 CA를 클라이언트에 배포 (원인 2와 동일)
- 특정 서비스가 검사 대상이면 안 되면 → 장비에서 해당 도메인을 검사 예외(bypass) 로 등록
- 개인 PC의 백신이 원인이면 → 그 제품의 HTTPS 스캐닝 설정 확인
- 인증서 피닝을 쓰는 앱은 예외 등록 외에 방법이 없다. 루트를 설치해도 실패한다
원인 5 — 시스템 시계
드물지만 고치기는 제일 쉽다. 시계가 크게 어긋나면 루트 인증서가 아직 유효하지
않거나 이미 만료된 것으로 판정된다. 보통 ERR_CERT_DATE_INVALID 로 나오지만,
경로 구성 단계에서 걸리면 AUTHORITY_INVALID 로도 뜬다.
CMOS 배터리가 죽은 PC, 초기화된 VM, 네트워크가 격리돼 NTP를 못 받는 서버에서 나온다.
timedatectl status # Linux
w32tm /query /status # Windows
sudo sntp -sS time.apple.com # macOS 강제 동기화
날짜가 몇 년 단위로 틀어져 있으면 이게 원인이다. 인증서를 건드리기 전에 시계부터 맞춘다.
원인 6 — 루트 자체가 만료·제거됨
서버도 정상, 체인도 완전, 시계도 맞는데 오래된 기기에서만 실패하는 경우다.
트러스트 스토어 업데이트가 멈춘 기기는 새 루트를 모른다. 알던 루트는 만료됐다.
2021년 Let’s Encrypt DST Root CA X3 만료 때 구형 안드로이드가 일제히 끊긴 게
이 경우다.
처리 방향
- 서버에서 내보낼 체인을 골라 구형 기기를 살릴 수 있는지 확인 (CA가 대체 체인을 제공하는 경우)
- 안 되면 그 기기군을 지원 대상에서 제외할지 판단해야 한다. 서버 설정으로 해결되는 문제가 아니다
판별 순서 정리
1. issuer 확인
└ subject와 같음 → 자기서명 (원인 1)
└ 사내 CA → 루트 배포 (원인 2)
└ 보안 제품 이름 → TLS 검사 장비 (원인 4)
└ 정상 공개 CA → 2번으로
2. openssl -verify_return_error
└ 0 (ok) 아님 → 중간 인증서 누락 (원인 3)
└ 0 (ok) → 3번으로
3. 실패하는 기기의 범위
└ 특정 PC만 → 트러스트 스토어 / 시계 (원인 2, 5)
└ 오래된 기기만 → 루트 만료 (원인 6)
서버 인증서 재발급은 이 순서의 어디에도 없다. AUTHORITY_INVALID 의 원인
대부분은 서버가 아니라 검증하는 쪽에 있기 때문이다. 재발급부터 하고 보는 습관이
가장 흔한 시간 낭비다.
원인이 중간 인증서 누락으로 좁혀졌다면 고치는 절차는 Nginx 인증서 교체와 무중단 리로드에 있다.