Выберите продукт

TLS trust store и ERR_CERT_AUTHORITY_INVALID: как починить цепочку сертификатов и verify error

ERR_CERT_AUTHORITY_INVALID и openssl verify error обычно сводятся к двум причинам: сервер отдает неполную цепочку (intermediate missing) или у клиента устаревший trust store. Разберем диагностику через openssl, сборку fullchain.pem, настройку Nginx/Apache и обновление CA.
TLS trust store и ERR_CERT_AUTHORITY_INVALID: как починить цепочку сертификатов и verify error

ERR_CERT_AUTHORITY_INVALID в браузере и openssl verify error в консоли — это не «магия TLS», а конкретная история про доверие: клиент строит цепочку сертификатов до корневого УЦ (Root CA) из своего trust store. Если цепочку построить не получается, появляются ошибки вроде unable to get local issuer certificate или self signed certificate in certificate chain.

Ниже — практический чек-лист: как быстро понять, что именно сломано (серверная цепочка или клиентское хранилище доверенных CA), как правильно собрать fullchain.pem, и как указать его в nginx ssl_certificate или apache SSLCertificateFile.

Как устроено доверие TLS: trust store, цепочка и кто что обязан отдавать

Упрощенно TLS-проверка выглядит так:

  • Сервер отправляет «листовой» сертификат домена (Leaf) и обычно — один или несколько промежуточных (intermediate certificate).
  • Клиент строит цепочку: Leaf → Intermediate → (возможен еще Intermediate) → Root.
  • Root сертификаты сервер почти никогда не отправляет: они уже лежат у клиента в trust store (системный пакет ca-certificates, хранилище Windows/macOS, Android, Firefox и т. п.).

Критическая деталь: сервер обязан отдавать промежуточные сертификаты. Если вы отдали только Leaf, часть клиентов «достроит» цепочку (из кэша intermediates), а часть — нет. Отсюда классический эффект «у меня открывается, у пользователей падает».

Типовые симптомы и что они обычно значат

Ориентируйтесь на формулировку:

  • ERR_CERT_AUTHORITY_INVALID (Chrome/Edge): чаще всего неполная цепочка или корпоративный/антивирусный MITM, подменяющий сертификат.
  • unable to get local issuer certificate (OpenSSL): клиент не нашел issuer (обычно intermediate) в присланной цепочке и не смог найти его локально.
  • self signed certificate in certificate chain: сервер отдает самоподписанный сертификат вместо нормального intermediate, либо в цепочку попал «чужой» root.
  • certificate has expired: истек Leaf или intermediate (промежуточные тоже истекают).
  • hostname mismatch: сертификат не на тот домен (CN/SAN), это отдельный класс проблем (не trust store).

Быстрая диагностика: что видит клиент и что реально отдает сервер

Начинайте с проверки того, что реально уходит по сети. Самый быстрый инструмент — openssl s_client.

1) Смотрим цепочку, которую присылает сервер

openssl s_client -connect example.com:443 -servername example.com -showcerts

Что смотреть в выводе:

  • В блоке Certificate chain должно быть несколько элементов: минимум Leaf и intermediate.
  • Если chain состоит из одного сертификата — вы почти наверняка отдаете не fullchain.pem.
  • Строка Verify return code показывает результат на стороне именно этой машины с ее trust store (на другом клиенте результат может отличаться).

2) Проверяем локально: собирается ли цепочка при верификации

Если у вас есть файлы сертификатов на сервере, удобно проверить цепочку локально:

openssl verify -CAfile root-ca.pem -untrusted intermediate.pem cert.pem

Где:

  • cert.pem — листовой сертификат домена
  • intermediate.pem — промежуточный сертификат (или несколько, если склеить их в один файл)
  • root-ca.pem — корневой (для теста можно указать явно, хотя обычно он уже есть в системе)

Если видите unable to get local issuer certificate, OpenSSL не смог построить цепь: intermediate не тот, не хватает звена, неверный порядок, или файл вообще не PEM.

3) Уточняем, какой именно issuer нужен

Проверьте Subject/Issuer у Leaf:

openssl x509 -in cert.pem -noout -subject -issuer

Issuer Leaf должен совпасть с Subject нужного intermediate. Если не совпадает — вы пытаетесь «пристегнуть» чужой intermediate (частая ошибка при ручной сборке).

Вывод openssl s_client с цепочкой сертификатов: leaf и intermediate

Правильная сборка fullchain.pem: порядок, формат и частые ловушки

fullchain.pem — это файл, где сертификаты идут «сверху вниз»:

  • сначала Leaf (сертификат домена)
  • затем intermediate certificate (иногда несколько)

Root обычно не добавляют в fullchain.pem для веб-сервера. Отправка root редко помогает и иногда создает проблемы, когда клиент «выбирает» не тот trust anchor.

Собрать fullchain вручную

Если УЦ выдал отдельные файлы, соберите так:

cat cert.pem intermediate.pem > fullchain.pem

Проверьте, что в fullchain.pem действительно два (или больше) PEM-блока BEGIN CERTIFICATE.

Проверить, что fullchain соответствует приватному ключу

Частый сценарий после перевыпуска: сертификат новый, а ключ в конфиге старый. Тогда TLS может не подняться вовсе или будет отдаваться другой vhost/сертификат.

openssl x509 -in cert.pem -noout -modulus | openssl md5
openssl rsa -in privkey.pem -noout -modulus | openssl md5

Хэши должны совпасть. Для ECDSA ключей используйте проверку по публичному ключу:

openssl pkey -in privkey.pem -pubout -outform pem | openssl md5
openssl x509 -in cert.pem -pubkey -noout | openssl md5

Ловушка: “bundle” не тот или не полный

Разные УЦ и панели называют файлы по-разному: ca-bundle.pem, chain.pem, intermediate.pem. Важно не название, а содержимое и соответствие Issuer/Subject.

Правило: цепочка для сервера должна соответствовать Issuer вашего Leaf. Не подбирайте intermediate «по бренду УЦ» — подбирайте по совпадению Issuer/Subject.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Настройка Nginx: ssl_certificate и почему нельзя подсовывать только cert.pem

В Nginx в параметре ssl_certificate должен быть файл с цепочкой (то есть ваш fullchain.pem), а ssl_certificate_key — приватный ключ.

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/ssl/example/fullchain.pem;
    ssl_certificate_key /etc/ssl/example/privkey.pem;
}

Проверка конфигурации и перезагрузка:

nginx -t
systemctl reload nginx

Если после исправления цепочки браузер все равно ругается, проверьте, не отдается ли другой сертификат на этом же IP:443 (SNI/дефолтный vhost). Здесь ключевой момент — всегда указывать -servername в openssl s_client.

Настройка Apache: SSLCertificateFile, SSLCertificateChainFile и нюансы версий

В Apache исторически использовались разные подходы:

  • в новых версиях чаще кладут fullchain в SSLCertificateFile
  • в старых конфигурациях intermediate указывали отдельно через SSLCertificateChainFile

Практичный вариант (одним файлом):

<VirtualHost *:443>
    ServerName example.com

    SSLEngine on
    SSLCertificateFile /etc/ssl/example/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/example/privkey.pem
</VirtualHost>

Проверка и reload:

apachectl configtest
systemctl reload apache2

Если у вас legacy-конфиг с SSLCertificateChainFile, следите, чтобы intermediate не дублировался: когда fullchain уже включает chain, отдельное указание chain иногда приводит к «лишним» сертификатам в цепочке.

Когда виноват не сервер: trust store клиента и обновление ca-certificates

Бывает обратная ситуация: сервер отдает корректную цепочку, но конкретная система все равно не доверяет УЦ. Типичные причины:

  • очень старая ОС или контейнерный образ с древним пакетом CA
  • минималистичный образ без набора CA
  • корпоративная прокси/антивирус подменяет сертификаты, а его корневой сертификат не установлен в систему/браузер

Debian/Ubuntu: обновить trust store

apt-get update
apt-get install -y ca-certificates
update-ca-certificates

Это и есть тот самый ca-certificates update сценарий, который часто решает unable to get local issuer certificate в утилитах (curl, git, языковые рантаймы) внутри контейнеров и на старых серверах.

RHEL/Alma/Rocky/CentOS: актуализировать CA

yum install -y ca-certificates
update-ca-trust

Контейнеры и CI

Если ошибка проявляется только в CI, Docker build или в рантайме приложения, проверьте базовый образ. Часто лечится установкой ca-certificates и обновлением хранилища внутри образа. Если используется внутренний корпоративный CA, его сертификат нужно добавлять в trust store явно: одна серверная цепочка это не «починит».

По смежной теме (HSTS и выпуск/обновление сертификатов без сюрпризов) полезно держать под рукой разбор: HTTPS, Certbot и HSTS: типовые ошибки внедрения и проверки.

Сравнение настроек TLS в Nginx и Apache: указание fullchain.pem и ключа

Мини-рунбук: как локализовать причину за 10 минут

  1. Посмотрите, что отдает сервер:

    openssl s_client -connect example.com:443 -servername example.com -showcerts
  2. Если в chain только один сертификат — соберите fullchain.pem и укажите его в веб-сервере.

  3. Если chain выглядит полным, но конкретная машина падает с unable to get local issuer certificate — обновляйте ca-certificates на этой машине/в контейнере.

  4. Проверьте соответствие ключа и сертификата (modulus/public key).

  5. Если проблема «плавающая», проверьте SNI и дефолтный vhost: часть запросов может попадать в другой конфиг/контейнер/балансер.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Частые ошибки, из-за которых ERR_CERT_AUTHORITY_INVALID возвращается снова

  • Перепутали файлы местами: в ssl_certificate положили intermediate без leaf, или наоборот.

  • Загрузили «не тот» chain: intermediate не соответствует issuer leaf.

  • Дублирование цепочки: fullchain уже содержит intermediate, но вы добавили его еще раз отдельной директивой/вторым файлом.

  • Не перезагрузили прокси-слой: CDN/балансер/ingress продолжает отдавать старую цепочку.

  • Старый trust store: особенно в контейнерах и минимальных образах.

Что считать «правильным результатом»

После исправлений вы должны увидеть:

  • в openssl s_client — цепочку минимум из Leaf + intermediate
  • в конце — Verify return code: 0 (ok) на машине с актуальным trust store
  • в браузере — отсутствие ERR_CERT_AUTHORITY_INVALID и корректно определяемый Issuer

Если на одном устройстве все ок, а на другом нет — почти всегда это различие trust store или «вмешивающаяся» прокси. Тогда фокус смещается с сервера на клиентскую инфраструктуру.

Короткий вывод

Для веб-сервера главное — отдавать полную certificate chain (через fullchain.pem), а для клиентов — иметь актуальный trust store (ca-certificates). Свяжите эти две части диагностикой через openssl s_client и openssl verify, и ошибки вида ERR_CERT_AUTHORITY_INVALID и unable to get local issuer certificate перестанут быть загадкой, превращаясь в повторяемый рутинный сценарий исправления.

Поделиться статьей

Вам будет интересно

Linux shutdown/reboot hangs: systemd debug, umount busy и зависшие процессы OpenAI Статья написана AI (GPT 5)

Linux shutdown/reboot hangs: systemd debug, umount busy и зависшие процессы

Если Linux зависает на shutdown или reboot, чаще всего виноваты stop jobs в systemd, busy-размонтирование, NFS-таймауты, Docker ил ...
IO wait и steal time на VDS: как диагностировать задержки vmstat, iostat, pidstat OpenAI Статья написана AI (GPT 5)

IO wait и steal time на VDS: как диагностировать задержки vmstat, iostat, pidstat

Когда VDS начинает «тормозить», чаще всего причина в iowait, steal time или очередях диска. Ниже — практичная схема диагностики на ...
Nginx proxy_cache: cache_key, Vary и как поднять hit ratio без утечек приватных данных OpenAI Статья написана AI (GPT 5)

Nginx proxy_cache: cache_key, Vary и как поднять hit ratio без утечек приватных данных

Низкий hit ratio в Nginx proxy_cache чаще связан не с размером кеша, а с неправильным proxy_cache_key и игнорированием Vary. Разбе ...