Dlaczego mój certyfikat nie jest zaufany? Systematyczna checklista rozwiązywania problemów
“Niezaufany”, “niebezpieczne połączenie”, “Twoje połączenie nie jest prywatne” — każdy klient formułuje to inaczej, ale pytanie w tle zawsze jest to samo: czy klient zdołał zbudować poprawny łańcuch zaufania od otrzymanego certyfikatu do roota, któremu już ufa? Kiedy się nie uda, to jedyny komunikat, jaki dostajesz. Nie mówi Ci, dlaczego.
To checklista pomagająca znaleźć rzeczywistą przyczynę, w mniej więcej takiej kolejności, w jakiej zwykle się okazuje, że to właśnie ona.
TL;DR: najszybszy sposób, żeby sprawdzić dlaczego
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep -A2 "Verify return code"
Podmień example.com na swój host. Linia Verify return code na końcu mówi dokładnie, który
test zawiódł. Przejdź do szybkiej ściągawki poniżej, żeby
rozszyfrować numer, i przejdź od razu do odpowiedniego kroku.
Krok 1: Sprawdź, czy łańcuch jest kompletny
Zdecydowanie najczęstsza przyczyna. Serwer musi wysłać certyfikat liściowy oraz każdy
certyfikat intermediate CA aż do (ale nie włącznie) roota, któremu klient już ufa. Brak
intermediate daje kod weryfikacji 20 (unable to get local issuer certificate) albo 21
(unable to verify the first certificate).
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
Policz certyfikaty w wyniku. Jeśli widzisz tylko jeden (para s: / i:), serwer wysyła tylko
certyfikat liściowy. Rozwiązanie: skonfiguruj serwer (albo load balancer, albo reverse proxy),
żeby serwował pełny łańcuch, nie tylko certyfikat końcowy.
To także powód, dla którego ten sam źle skonfigurowany serwer może “działać” w przeglądarce, a
zawodzić w curl albo backendowym kliencie HTTP: przeglądarki cache’ują intermediate’y widziane
na innych stronach i po cichu same dokańczają łańcuch. curl, większość bibliotek HTTP i
backendowe usługi z minimalnym trust store’em nie zrobią Ci tej przysługi.
Krok 2: Sprawdź terminy ważności — całego łańcucha, nie tylko swojego certyfikatu
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| openssl x509 -noout -dates
To sprawdza tylko certyfikat liściowy. Twój certyfikat może być ważny, podczas gdy intermediate
nad nim już wygasł — sprawdź każdy certyfikat, który wypisał s_client -showcerts, nie tylko
pierwszy. Dokładnie to spowodowało kilka głośnych publicznych incydentów, gdzie pozornie w pełni
poprawny certyfikat zaczynał zawodzić z dnia na dzień, bo wygasał intermediate CA.
Krok 3: Sprawdź zgodność hostname / SAN
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName
Od Chrome 58 przy walidacji hostname liczy się tylko Subject Alternative Name (SAN) — Common Name
jest ignorowane. Jeśli hostname, z którym się łączysz, nie znajduje się w subjectAltName
(włączając w to www. kontra goła domena — to różne hostname’y), walidacja zawiedzie niezależnie
od tego, że wszystko inne jest poprawne.
Krok 4: Sprawdź, czy to certyfikat self-signed albo z prywatnego CA, któremu klient nie ufa
Kody weryfikacji 18 (self-signed certificate) i 19 (self-signed certificate in the chain)
oznaczają, że root łańcucha nie znajduje się w trust store kliencie — spodziewane przy lokalnym
developmencie, spodziewane też przy wewnętrznych/prywatnych CA, dopóki jawnie nie
rozdystrybuujesz i nie zainstalujesz certyfikatu tego CA na każdej maszynie czy kontenerze, który
ma mu ufać. Jeśli to usługa wewnętrzna, rozwiązaniem nie jest “prawdziwy” certyfikat — tylko
upewnienie się, że Twoje prywatne CA jest faktycznie zaufane tam, gdzie powinno być.
Krok 5: Sprawdź zegar systemowy
Certyfikat jest ważny tylko w oknie notBefore / notAfter. Klient ze źle ustawionym zegarem —
częste na kontenerach, maszynach wirtualnych, które straciły synchronizację NTP, albo runnerach
CI — zgłosi całkowicie poprawny certyfikat jako wygasły albo “jeszcze nieważny”. Jeśli krok 2
mówi, że daty są w porządku, a błąd wciąż występuje, sprawdź date (albo w32tm /query /status
na Windowsie) na kliencie, nie na serwerze.
Krok 6: Sprawdź, czy faktycznie serwujesz certyfikat, który myślisz, że serwujesz
Na serwerze obsługującym wiele domen (wiele vhostów, wiele reguł ingress, load balancer
routujący po SNI) łatwo skończyć z serwowaniem złego certyfikatu dla danego hostname —
certyfikat poprawny, ale nie ta strona. Potwierdź, że subject i subjectAltName, które widzisz
w kroku 1, faktycznie odpowiadają certyfikatowi, który zamierzałeś tam serwować, a nie po
prostu jakiemuś ważnemu certyfikatowi.
Krok 7: Sprawdź status rewokacji
Najrzadsza przyczyna, ale warto wykluczyć ją na końcu: jeśli certyfikat został odwołany, część klientów aktywnie go odrzuci przez CRL albo OCSP zamiast po prostu zgłosić ogólny błąd. Sprawdź, czy certyfikat ma OCSP responder i czy jest osiągalny:
openssl x509 -in cert.pem -noout -ocsp_uri
Jeśli klient nie może połączyć się z tym URL-em (zablokowany firewallem OCSP responder, wygasły OCSP staple), część klientów działa w trybie “fail closed” i traktuje certyfikat jako niezaufany, mimo że technicznie wciąż jest ważny.
Szybka ściągawka: verify return codes
| Kod | Znaczenie | Przejdź do |
|---|---|---|
10 | Certyfikat wygasł | Krok 2 |
18 | Certyfikat self-signed | Krok 4 |
19 | Certyfikat self-signed w łańcuchu | Krok 4 |
20 | Unable to get local issuer certificate | Krok 1 |
21 | Unable to verify the first certificate | Krok 1 |
24 | Invalid CA certificate | Krok 1 albo Krok 2 |
Od reaktywnego rozwiązywania problemów do prewencji
Każdy krok powyżej to coś, co sprawdzasz po tym, jak ktoś już trafił na błąd — użytkownik, kolega z zespołu albo usługa, której właśnie nie udał się handshake. Problemy z łańcuchem, terminem ważności i hostname są szczególnie tego typu: oczywiste z perspektywy czasu i niewidoczne, dopóki czegoś nie zepsują. Dokładnie tę lukę wypełnia Certyfizer — śledzi łańcuch i termin ważności każdego certyfikatu we wszystkich środowiskach i sygnalizuje problem — brakujący intermediate, wygasający certyfikat liściowy, niepasujący SAN — zanim jakiś klient będzie musiał Ci o tym powiedzieć.
Najczęściej zadawane pytania
Co właściwie znaczy "certyfikat niezaufany"?
Znaczy to, że klient (przeglądarka, curl, biblioteka HTTP) nie zdołał zbudować poprawnego łańcucha zaufania od otrzymanego certyfikatu do root CA, któremu już ufa. Może się to zdarzyć z kilku niepowiązanych powodów — niekompletny łańcuch, wygasły certyfikat, niepasujący hostname, certyfikat podpisany przez nieznane CA albo źle ustawiony zegar — a komunikat błędu zwykle wygląda tak samo niezależnie od przyczyny.
Jaki jest najszybszy sposób, żeby sprawdzić dlaczego certyfikat nie jest zaufany?
Uruchom openssl s_client -connect host:443 -servername host </dev/null i przeczytaj linię "Verify return code" na końcu wyniku. Mówi ona dokładnie, który test zawiódł — wygasły, self-signed, unable to get local issuer certificate itd. — zamiast zgadywania.
Czym jest "verify return code" i co znaczą te najczęstsze?
To numeryczny wynik testu łańcucha zaufania w OpenSSL. Te, które widuje się najczęściej: 0 to sukces, 10 to wygasły certyfikat, 18 to certyfikat self-signed, 20 to unable to get local issuer certificate (brakujące intermediate), a 21 to unable to verify the first certificate (zwykle ta sama przyczyna co 20).
Dlaczego ważny, niewygasły certyfikat wciąż pokazuje się jako niezaufany?
Najczęstszym powodem jest niekompletny łańcuch — serwer wysyła certyfikat liściowy, ale nie wysyła certyfikatu intermediate CA, który łączy go z zaufanym rootem. Przeglądarki często maskują ten problem, cache'ując intermediate'y widziane na innych stronach — dlatego ten sam źle skonfigurowany serwer może wyglądać dobrze w przeglądarce, a zawodzić w curl albo backendowej usłudze.
Dlaczego mój certyfikat jest niezaufany tylko w niektórych przeglądarkach/klientach, a w innych nie?
Różne klienty mają różne trust store'y i różną tolerancję na niekompletne łańcuchy. Klient, który niedawno pobrał brakujący intermediate z innej strony, sam dokończy łańcuch; curl, aplikacja mobilna albo backendowa usługa z minimalnym trust store'em tego nie zrobi. Jeśli działa w jednym miejscu, a nie działa w innym — podejrzewaj najpierw niekompletny łańcuch.
Czy wygasłe root albo intermediate CA może spowodować ten problem, nawet jeśli mój certyfikat jest w porządku?
Tak. Twój certyfikat liściowy może być całkowicie poprawny i mimo to nie przejść walidacji, jeśli intermediate CA, które go podpisało, wygasło albo zostało odrzucone przez trust store — dokładnie to wydarzyło się, gdy przeglądarki wycofały zaufanie do rootów wystawionych przez Symanteca. Sprawdź termin ważności każdego certyfikatu w łańcuchu, nie tylko swojego.
Powiązane artykuły
Dlaczego mój certyfikat klienta nie działa? Rozwiązywanie problemów z mTLS po stronie klienta
Przewodnik po rozwiązywaniu problemów z certyfikatami klienta, które nie przechodzą uwierzytelnienia mTLS — niedopasowanie klucza i certyfikatu, brakujące Extended Key Usage, niezaufane CA klienta i jak to zdiagnozować.
Jak wygenerować certyfikat SSL: przewodnik po OpenSSL krok po kroku (self-signed i CSR)
Praktyczny przewodnik po generowaniu certyfikatów SSL/TLS za pomocą OpenSSL — klucze prywatne, certyfikaty self-signed, CSR i Subject Alternative Names.
Certyfikaty SSL/TLS: cichy fundament internetu, który zbyt często zawodzi
Dlaczego awarie SSL/TLS to nie problem kryptografii, lecz zarządzania. Przykłady incydentów i wnioski dla zespołów IT.