Skip to content
EN
← Все статьи

Российские SSL-сертификаты и УЦ: цепочка сертификатов и установка корня на сервер

Коротко. В русском поиске «УЦ» означает две разные вещи: удостоверяющий центр, который выпускает TLS-сертификаты для сайтов (НУЦ Минцифры, он же Russian Trusted CA), и аккредитованный УЦ электронной подписи (КриптоПро, токен, Госуслуги). К сертификату сайта относится только первое. Главная практическая проблема российских TLS-сертификатов — цепочка доверия: корня нет в стандартных хранилищах, и ставить его надо не только в браузер, но и на сервер.

Сначала разведём два разных «УЦ» — иначе вы читаете не ту статью

Запросы «удостоверяющий центр» и «цепочка сертификатов УЦ» приводят людей с двумя совершенно разными задачами. Одни хотят, чтобы у их сайта в адресной строке был замок и https://. Другие пришли за электронной подписью, чтобы сдать отчётность или подписать договор. Слова совпадают, технологии — нет. Найдите свою строку в таблице и не тратьте время на чужую половину.

Что вам нужноКакой это УЦКуда идти
Замок и https:// в адресной строке вашего сайтаУЦ для web-TLS — выпускает серверные сертификаты. В России это НУЦ Минцифры, он же Russian Trusted CAЭто ваша статья. Проверить готовый сертификат — SSL-чекер
Браузер пишет, что сертификат сайта не является довереннымweb-TLS: проблема в цепочке доверия, а не в сертификатеВаша статья + разбор ошибки ERR_CERT_AUTHORITY_INVALID
Ваш сервер или скрипт падает при обращении к российскому APIweb-TLS, но проблема на стороне клиента: нет корня в системном хранилищеВаша статья, раздел про установку корня на сервер
Подписать декларацию, отчётность, договор, участвовать в торгахАккредитованный УЦ электронной подписи (ЭП, в обиходе ЭЦП)Не эта статья. Нужен УЦ ФНС либо аккредитованный коммерческий УЦ, средство подписи (КриптоПро CSP и аналоги) и носитель — Рутокен, JaCarta
Войти на Госуслуги или в личный кабинет налоговой по подписиЭПНе эта статья: плагин браузера, драйвер токена, сертификат подписи
Поднять TLS с ГОСТ-алгоритмамиОтдельная история на стыке двух мировТребует ГОСТ-криптографии с обеих сторон соединения; обычный браузер без дополнительного ПО такое соединение не установит
Если вы пришли за электронной подписью, ни одна openssl-команда ниже вам не пригодится. Сертификат ЭП живёт на токене и в хранилище криптопровайдера, а не на веб-сервере, и в адресной строке браузера он не появляется никогда. Дальше в статье — только про TLS-сертификаты сайтов.

Почему их вообще путают

Путаница не на пустом месте. Оба объекта — сертификаты X.509. Оба выпускает организация, которая по-русски называется «удостоверяющий центр». У обоих есть срок действия, отпечаток, издатель и цепочка до корня. Совпадает даже словарь: «отозвать сертификат», «продлить сертификат», «цепочка доверия».

Расходятся они в трёх местах, и этого достаточно:

  • Назначение внутри сертификата. У серверного TLS-сертификата в расширении Extended Key Usage стоит serverAuth. У сертификата подписи — назначения, связанные с подписанием документов. Веб-сервер не примет сертификат подписи как серверный, и наоборот.
  • Кто его читает. TLS-сертификат читает браузер и любой HTTP-клиент — curl, мобильное приложение, чужой сервер. Сертификат ЭП читает криптопровайдер в момент подписания файла.
  • Где он хранится. TLS-сертификат лежит файлом на сервере рядом с приватным ключом. Сертификат ЭП — как правило, на аппаратном носителе, откуда приватный ключ принципиально не выгружается.

Что говорит закон — и чего в этой статье нет

Электронная подпись опирается на закон об электронной подписи (63-ФЗ) — именно он вводит понятия аккредитованного УЦ и квалифицированной подписи. TLS-сертификаты сайтов этим законом напрямую не регулируются: сайт с международным сертификатом не становится от этого «незаконным», а с российским — «одобренным».

Состав аккредитованных УЦ, порядок получения сертификатов и технические требования меняются. Номеров приказов, дат вступления в силу, сумм и сроков аккредитации в этой статье намеренно нет — они устаревают быстрее самого текста. Перед любыми действиями сверяйтесь с официальным источником: сайтом ведомства или самого удостоверяющего центра.
Схема: два разных удостоверяющих центра — УЦ web-TLS для сертификата сайта и аккредитованный УЦ электронной подписи
Два непересекающихся мира за одним словом «УЦ»: сертификат сервера и сертификат электронной подписи.

Что такое цепочка сертификатов УЦ — коротко

Сервер присылает клиенту листовой сертификат и, как правило, один-два промежуточных. Клиент пытается построить путь от листа вверх до корня, который уже лежит в его собственном хранилище доверия. Если путь не строится — соединение считается недоверенным, каким бы корректным ни выглядел сам сертификат.

Общее устройство цепочки, построение пути и типовые обрывы разобраны отдельно: неполная цепочка сертификатов. Дальше здесь — только российская специфика.

Ключевое правило: сертификат не бывает «валидным вообще». Он валиден относительно конкретного хранилища доверия конкретного клиента. У браузера, у curl, у Java-приложения и у вашего мобильного клиента это четыре разных хранилища, и они не синхронизируются между собой.

Российский корень доверия: почему браузер ругается на валидный сертификат

Сертификат, выпущенный НУЦ Минцифры, — это обычный X.509. Срок действия в порядке, домен в SAN совпадает, подпись корректна. Проблема ровно одна: корневой сертификат, которым эта цепочка заканчивается, не входит в корневые программы Mozilla, Chrome, Apple и Microsoft. Значит, «из коробки» его нет ни в Firefox, ни в Chrome, ни в macOS, ни в Windows, ни в большинстве Linux-дистрибутивов, ни в дефолтном образе Docker.

Клиенту неоткуда взять доверие к вершине цепочки — и он честно говорит об этом пользователю:

  • Chrome и Chromium-браузерыERR_CERT_AUTHORITY_INVALID. Подробный разбор именно этой ошибки: как её чинить.
  • FirefoxSEC_ERROR_UNKNOWN_ISSUER. Firefox использует собственное хранилище и не смотрит в системное на Windows и Linux по умолчанию, так что установка корня в систему его не лечит.
  • curl и серверные клиентыSSL certificate problem: unable to get local issuer certificate, код выхода 60.
  • JavaPKIX path building failed: unable to find valid certification path to requested target.
  • PythonCERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate.

Все пять сообщений — про одно и то же: путь до доверенного корня не построен. Разными словами, потому что хранилища разные.

Что делает пользователь и что делает владелец сайта

Пользователь может поставить корневой сертификат себе — в систему или в браузер. Это его выбор и его действие, и вы им не управляете. Владелец сайта может сделать три вещи: отдавать полную цепочку, честно предупредить аудиторию с инструкцией по установке и — главное — заранее решить, какая доля аудитории этот корень никогда не поставит.

Часть российских браузерных сборок поставляет корень предустановленным — так делают, в частности, Яндекс.Браузер и Атом. Формулировка «часть сборок» здесь не осторожность ради осторожности: состав предустановленных корней меняется от версии к версии, и единственный надёжный способ узнать — открыть в конкретной сборке список доверенных корневых центров и поискать там строку Russian Trusted.

Не полагайтесь на «у всех уже стоит». Аудитория сайта — это не только десктопные браузеры. Это ещё мобильные приложения, платёжные шлюзы, агрегаторы, поисковые роботы, чужие серверные интеграции и ваши собственные скрипты. Каждый из них носит своё хранилище доверия, и ни к одному из них вы не приложите инструкцию «поставьте корень».

Чем российский сертификат отличается от международного

ПараметрРоссийский УЦ (НУЦ Минцифры)Международный CA
Корень довериянациональный корень плюс промежуточныйкорень в программах Mozilla, Chrome, Apple, Microsoft
Предустановка в клиентахне гарантирована; часть российских сборок поставляетпрактически везде «из коробки»
ФорматX.509, стандартные расширенияX.509, стандартные расширения
Проверка срока действиястандартная, теми же командамистандартная
Автоматизация выпуска и продлениякак правило, выдача через портал, а не по ACME — привычный сценарий с certbot не работаетACME повсеместно, продление в фоне
Публичные CT-логирассчитывать на попадание в публичные логи прозрачности не стоитлогирование фактически обязательно для доверия в Chrome
Инвентаризация через crt.sh и аналогиможет не находиться — ведите учёт самиработает
Кому подходитроссийской аудитории и контурам, где вы контролируете хранилища доверияуниверсальной и международной аудитории

Про алгоритмы важное уточнение. Российский TLS-сертификат и ГОСТ-криптография — не синонимы. Сертификаты НУЦ выпускаются в обычном X.509 с распространёнными алгоритмами подписи, и именно поэтому обычный браузер принимает их сразу, как только появляется корень. Проверить, что у вас на руках, можно за одну команду:

# алгоритм подписи и параметры ключа в конкретном сертификате
openssl x509 -in russian_trusted_root_ca.cer -inform DER -noout -text \
  | grep -E 'Signature Algorithm|Public Key Algorithm|Public-Key'

# то же самое для сертификата, который реально отдаёт сервер
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -text | grep -E 'Signature Algorithm|Issuer:|Subject:'

Если в выводе видны ГОСТ-алгоритмы, вы имеете дело не с обычным web-TLS, а с ГОСТ-контуром — и тогда одного корня в хранилище будет мало, потребуется криптопровайдер на клиенте.

Установка корневого сертификата на сервер, а не в браузер

Самая дорогая ошибка в этой теме — считать, что российский корень нужен только конечным пользователям. На практике первым ломается не браузер, а ваш собственный сервер. Браузер хотя бы показывает человеку понятное окно с предупреждением; серверная интеграция просто падает в лог, часто молча и часто ночью.

Что ломается типично:

  • cron-скрипт, который забирает курс валют или справочник с российского портала;
  • PHP-интеграция с банковским или платёжным API;
  • Python-выгрузка отчётов по HTTPS;
  • Java-коннектор к системе ЭДО или к внутреннему сервису;
  • вебхуки, которые вы отправляете на адрес контрагента с российским сертификатом;
  • сборка в CI, которая тянет зависимость или артефакт с российского зеркала.

Симптом всегда один и тот же: unable to get local issuer certificate в логе. Браузер тут вообще ни при чём — у него своё хранилище, и то, что сайт открывается у вас на ноутбуке, ничего не говорит о сервере.

AlmaLinux, RHEL, Rocky, CentOS, Fedora

# кладём корневой И промежуточный в каталог якорей доверия
sudo cp russian_trusted_root_ca.cer /etc/pki/ca-trust/source/anchors/
sudo cp russian_trusted_sub_ca.cer  /etc/pki/ca-trust/source/anchors/

# пересобираем системный бандл (принимает и PEM, и DER)
sudo update-ca-trust extract

# проверяем, что якорь реально попал в извлечённое хранилище
trust list --filter=ca-anchors | grep -i -B2 'Russian Trusted'

# запасной способ проверки — поиск по собранному бандлу
grep -c 'BEGIN CERTIFICATE' /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

Каталог source/anchors/ принимает файлы и в PEM, и в DER — конвертировать заранее не обязательно. Команда update-ca-trust без аргументов делает то же, что update-ca-trust extract.

Debian и Ubuntu

# сюда кладут ТОЛЬКО PEM и ТОЛЬКО с расширением .crt — иначе файл молча игнорируется
sudo cp russian_trusted_root_ca.pem /usr/local/share/ca-certificates/russian_trusted_root_ca.crt
sudo cp russian_trusted_sub_ca.pem  /usr/local/share/ca-certificates/russian_trusted_sub_ca.crt
sudo update-ca-certificates

# если файл пришёл в DER — сначала конвертируем
openssl x509 -inform DER -in root.cer -outform PEM -out russian_trusted_root_ca.crt

# проверяем
awk -v cmd='openssl x509 -noout -subject' \
  '/BEGIN/{c=cmd} c{print | c} /END/{close(c); c=0}' \
  /etc/ssl/certs/ca-certificates.crt | grep -i 'russian'

Здесь два подводных камня, на которых теряют время чаще всего. Первый: update-ca-certificates подхватывает только файлы с расширением .crt. Файл с именем root.pem или root.cer будет лежать в каталоге и не делать ничего — никакой ошибки вы не увидите. Второй: содержимое должно быть в PEM, DER под именем .crt тоже не сработает. Успешный запуск печатает строку вида 1 added, 0 removed — если там ноль, значит ничего не установилось.

Alpine и контейнеры

# внутри контейнера на Alpine
apk add --no-cache ca-certificates
cp russian_trusted_root_ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

# в Dockerfile — чтобы фикс пережил пересборку образа
# COPY russian_trusted_root_ca.crt /usr/local/share/ca-certificates/
# RUN update-ca-certificates
Установка корня внутри работающего контейнера живёт ровно до первого пересоздания. Кладите сертификат в образ через COPY в Dockerfile либо монтируйте каталог с бандлом снаружи. Иначе интеграция сломается в самый неудобный момент — при плановом обновлении образа, когда никто не будет искать причину в сертификатах.
Схема установки корневого сертификата в системное хранилище доверия Linux-сервера и отдельные хранилища Java, Node.js и Python
Системное хранилище закрывает curl, PHP и Go, но не покрывает Java, Node.js и Python — у них свои списки корней.

Рантаймы со своим хранилищем: Java, Node.js, Python

Системная установка корня — необходимый шаг, но не достаточный. Часть популярных рантаймов принципиально не смотрит в системное хранилище. Это и есть источник классического «на сервере curl работает, а приложение падает».

КлиентИспользует системное хранилищеЧто делать дополнительно
curl, wgetдадостаточно системной установки
PHP (cURL и потоки OpenSSL)обычно дапроверить curl.cainfo и openssl.cafile в php.ini: если там прописан свой бандл, дополнять надо его
Go на Linuxдадостаточно системной установки
.NET на Linuxдадостаточно системной установки
Java (JVM)нет — свой файл cacertsимпорт через keytool
Node.jsнет — вкомпилированный список корнейпеременная NODE_EXTRA_CA_CERTS
Python: requests, httpxнет — бандл certifiSSL_CERT_FILE, REQUESTS_CA_BUNDLE или явный параметр verify
Python: ssl, urllibобычно даберёт пути OpenSSL, отдельных действий не требует
БраузерChrome и Edge — да; Firefox — нетдля Firefox корень ставится в его собственное хранилище

Java: импорт в cacerts

# Java 9 и новее: хранилище лежит в $JAVA_HOME/lib/security/cacerts
sudo keytool -importcert -trustcacerts -noprompt \
  -alias russian-trusted-root \
  -file /etc/pki/ca-trust/source/anchors/russian_trusted_root_ca.cer \
  -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit

# проверяем, что алиас появился
keytool -list -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit \
  | grep -i russian

Пароль по умолчанию — changeit; если его меняли, подставьте свой. В Java 8 путь другой: $JAVA_HOME/jre/lib/security/cacerts. И главное: обновление JDK перезаписывает cacerts целиком, вместе с вашим импортом. Если сервис критичен, держите отдельный truststore и подключайте его флагом -Djavax.net.ssl.trustStore — тогда апдейт JDK ничего не сломает.

Node.js и Python

# Node.js: переменная читается один раз при старте процесса, только PEM
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/russian_trusted_root_ca.pem node app.js

# Python: где лежит бандл certifi, который использует requests
python3 -c "import certifi; print(certifi.where())"

# правильный способ — не править файл certifi, а указать свой бандл
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
python3 -c "import requests; print(requests.get('https://example.ru').status_code)"
Не дописывайте корень прямо в файл certifi. Первое же обновление пакета сотрёт правку, и падение вернётся без единого изменения в вашем коде — искать причину будут долго. Задавайте бандл переменной окружения в unit-файле сервиса или в переменных контейнера.

Как НЕ надо проверять установку: грабля с gosuslugi.ru

Не проверяйте установку корня запросом на gosuslugi.ru. Портал ограничивает обращения с адресов дата-центров, и ваш curl с сервера получит отказ, который выглядит как проблема с сертификатом, хотя сертификат и хранилище в полном порядке. Берите для проверки ресурс, который не режет адреса ЦОД, — например sberbank.ru.

Эта ошибка стоит нескольких часов, потому что она выглядит убедительно: вы только что поставили корень, идёте проверять на самый очевидный государственный сайт и получаете отказ. Дальше человек начинает переустанавливать сертификат, пересобирать бандл и искать опечатку в пути — а проблема совсем в другом слое.

Разделитель простой: смотрите, дошло ли дело до TLS-хендшейка вообще.

Что вы видитеTLS-хендшейкДиагноз
SSL certificate problem: unable to get local issuer certificate, код выхода curl 60не завершёнкорня нет в хранилище или клиент читает не то хранилище
SSL certificate problem: certificate has expired, код выхода 60не завершёнистёк сертификат сервера; к установке корня отношения не имеет
Пришёл HTTP-ответ 403, 429 или страница с проверкой, код выхода curl 0завершён успешносертификат в порядке, вас ограничили по IP или завернул WAF
Connection timed out или Connection reset, код выхода 28, 35 или 56не начался или оборвалсясеть, фильтрация, отсутствие egress — не сертификат
openssl s_client печатает Verify return code: 0 (ok), а приложение всё равно ругаетсязавершёнразные хранилища: openssl взял системное, приложение — своё (Java, Node.js, certifi)
# 1. только TLS, без HTTP: проверяем сертификат и путь доверия
echo | openssl s_client -connect sberbank.ru:443 -servername sberbank.ru 2>/dev/null \
  | grep -E 'Verify return code|subject=|issuer='

# 2. подробный лог curl: видно, на каком шаге всё оборвалось
curl -sv -o /dev/null https://sberbank.ru 2>&1 \
  | grep -Ei 'SSL certificate|issuer|subjectAltName|HTTP/'

# 3. код выхода curl — самый быстрый разделитель: 0 против 60
curl -sS -o /dev/null https://sberbank.ru; echo "curl exit=$?"

Логика чтения: если openssl s_client дошёл до строки Verify return code — TLS отработал, и дальше вопрос уже не про сертификат. Если curl вернул 0 и вы получили HTTP-код, пусть даже 403, — сертификат тем более принят, потому что HTTP-ответ невозможен без завершённого хендшейка.

Диагностика цепочки: openssl s_client и Verify return code

# сколько сертификатов реально прислал сервер
echo | openssl s_client -connect example.ru:443 -servername example.ru -showcerts 2>/dev/null \
  | grep -c 'BEGIN CERTIFICATE'

# кто кого подписал: subject и issuer по уровням цепочки
echo | openssl s_client -connect example.ru:443 -servername example.ru -showcerts 2>/dev/null \
  | grep -E '^ *[0-9]+ s:|^ *[0-9]+ i:'

# итоговый вердикт проверки пути
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | grep 'Verify return code'

Что означают коды, которые встречаются в этой теме чаще других:

  • 0 (ok) — путь построен от вашего системного хранилища. Всё хорошо именно на этой машине.
  • 20 (unable to get local issuer certificate) — для верхнего присланного сертификата не нашлось издателя в локальном хранилище.
  • 21 (unable to verify the first certificate) — как правило, сервер прислал только листовой сертификат.
  • 2 (unable to get issuer certificate) — то же семейство: издатель не найден.
  • 10 (certificate has expired) — истёк срок, к удостоверяющему центру отношения не имеет.
  • 19 (self signed certificate in certificate chain) — в цепочке самоподписанный сертификат.

Как отличить «нет промежуточного» от «нет корня»

Два разных диагноза с почти одинаковым текстом ошибки. Разделяются они за две команды.

  1. Посчитайте сертификаты в ответе сервера. Если grep -c 'BEGIN CERTIFICATE' вернул 1 — сервер отдаёт только лист. Это ваша проблема как владельца сайта, и чинится она в конфиге сервера, а не у клиента.
  2. Если сертификатов два и больше, а ошибка остаётся — значит, верхний присланный сертификат подписан корнем, которого нет в хранилище. Это чинится установкой корня на стороне клиента.
  3. Проверьте гипотезу в лоб — подставьте корень явно и посмотрите, изменится ли вердикт.
# явно указываем корневой сертификат файлом
echo | openssl s_client -connect example.ru:443 -servername example.ru \
  -CAfile /etc/pki/ca-trust/source/anchors/russian_trusted_root_ca.cer 2>/dev/null \
  | grep 'Verify return code'

Если с флагом -CAfile стало 0 (ok), а без него — нет, диагноз однозначный: сертификат и цепочка в порядке, корень просто не установлен в систему. Это самая быстрая проверка во всём разборе, и она снимает большую часть спорных случаев.

Если же сервер отдаёт только лист — вот как это чинится на стороне владельца сайта:

# nginx: ssl_certificate должен указывать на файл «лист + промежуточные»
cat leaf.crt sub_ca.crt > /etc/nginx/ssl/example.ru.fullchain.crt
nginx -t && systemctl reload nginx

# Apache: лист в SSLCertificateFile, промежуточные в SSLCertificateChainFile
apachectl configtest && systemctl reload httpd

Порядок в файле важен: сначала листовой сертификат, затем промежуточные снизу вверх. Корень в файл добавлять не нужно — он должен быть у клиента, а не в ответе сервера. Подробнее про сбор цепочки — в статье про неполную цепочку, а общий порядок проверки сертификата — в материале как проверить SSL-сертификат.

Терминал с выводом openssl s_client и расшифровкой кодов Verify return code при проверке цепочки российского УЦ
Verify return code — самый короткий путь от симптома к диагнозу: 21 обычно значит «нет промежуточного», 20 — «нет корня».

Что это значит для владельца сайта

Выбор удостоверяющего центра — это не техническое, а продуктовое решение. Технически оба варианта работают одинаково; отличается только то, сколько ваших пользователей увидят предупреждение.

Когда российский сертификат оправдан

  • Аудитория целиком российская, и вы готовы держать понятную инструкцию по установке корня.
  • Внутренний или корпоративный контур: вы управляете рабочими станциями и можете раскатать корень централизованно.
  • Международный CA по каким-либо причинам недоступен для вашей организации или домена.
  • Сервис уже требует от пользователя установки дополнительного ПО — тогда корень встраивается в ту же инструкцию без нового трения.

Когда не оправдан

  • Есть зарубежная аудитория, партнёрские интеграции или платёжные шлюзы.
  • Продукт — публичный API, который дёргают чужие серверы: корня у них не будет, а повлиять на их хранилище вы не сможете в принципе.
  • Сайт живёт на органическом поиске: часть роботов и внешних проверялок просто не дойдёт до контента.
  • Вы не готовы держать поддержку по сценарию «поставьте корень» — это постоянная нагрузка, а не разовая.
Правило простое: чем больше у сайта потребителей, чьё хранилище доверия вы не контролируете, тем дороже обходится нестандартный корень. Для публичного API это почти всегда неприемлемо, для внутреннего портала — почти всегда нормально.

Двойная выдача: международный плюс российский

Идея звучит логично: пусть сервер отдаёт российский сертификат тем, у кого есть корень, и международный всем остальным. На практике так не получится, и причину стоит понимать до того, как вы начнёте это проектировать.

Клиент в TLS-хендшейке не сообщает серверу, каким корням он доверяет. Он присылает имя домена в расширении SNI и список поддерживаемых алгоритмов — и всё. Сервер физически не может выбрать сертификат «по наличию корня у клиента», потому что этих данных у него нет. Несколько директив ssl_certificate в nginx выбирают сертификат по типу ключа, RSA или ECDSA, а не по доверию клиента.

Значит, двойная выдача на практике — это всегда разные точки входа, а не автоматика внутри одного сервера:

  • Разные хосты. Основной домен под международным сертификатом, отдельный поддомен или зеркало — под российским, с явной ссылкой и объяснением.
  • Разные адреса по географии. DNS отдаёт разным пользователям разные IP, на каждом фронте свой сертификат. Работает, но добавляет целый слой инфраструктуры и новый класс инцидентов.
  • Разные каналы. Публичный сайт — международный сертификат, внутренние и партнёрские интеграции — российский. Обычно это самый дешёвый и самый честный вариант.

Срок действия и мониторинг истечения

Истёкший сертификат рвёт защищённое соединение независимо от того, какой УЦ его выпустил. Но у российских сертификатов риск пропустить продление выше по двум причинам: обычно нет ACME с автопродлением в фоне, и обычно нет записи в публичных CT-логах, по которым многие незаметно для себя инвентаризуют свой парк сертификатов. То есть отваливаются сразу два привычных страховочных механизма.

# срок действия, издатель и субъект одной командой
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -dates -subject -issuer

# истечёт ли в ближайшие 14 дней: код возврата 1 — да, 0 — нет
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -checkend 1209600

Команду с -checkend удобно ставить в cron и завязывать на неё уведомление: она возвращает готовый код возврата, парсить даты не нужно.

На enterno.io SSL-монитор проверяет срок по расписанию и присылает алерт заранее — по умолчанию за 14 дней предупреждение и за 3 дня критический уровень. Настраивается в разделе мониторинга, уведомления уходят в Telegram и на почту.

Важный нюанс именно для российских сертификатов: разделяйте в мониторинге «истекает» и «не доверен». Внешний чекер, у которого нет вашего корня, покажет ошибку доверия — и она замаскирует настоящий срок действия. Смотрите на дату из сертификата отдельно от вердикта проверки пути, иначе вы получите ложную тревогу вместо реального предупреждения за две недели.

Смежные материалы: мониторинг SSL-сертификатов и что делать, если сертификат уже истёк.

Таймлайн мониторинга срока действия SSL-сертификата с предупреждением за 14 дней и критическим алертом за 3 дня
Без ACME продление становится календарной задачей — алерт за две недели превращает аврал в плановую работу.

Симптом, причина, проверка, фикс

СимптомВероятная причинаПроверкаФикс
Сайт открывается в Яндекс.Браузере, но не в Chromeкорень есть только в части сборокнайти Russian Trusted в списке доверенных корней каждого браузераинструкция по установке корня; для публичного сайта — пересмотреть выбор УЦ
curl на сервере: unable to get local issuer certificateкорня нет в системном бандлеtrust list или поиск по ca-certificates.crtupdate-ca-trust или update-ca-certificates
curl работает, Java-сервис падает с PKIX path building failedу JVM собственный cacertskeytool -list с грепом по aliaskeytool -importcert в cacerts или отдельный truststore
Python: CERTIFICATE_VERIFY_FAILED, хотя curl проходитrequests читает certifi, а не системуpython3 -c "import certifi; print(certifi.where())"задать SSL_CERT_FILE и REQUESTS_CA_BUNDLE
После обновления образа контейнера всё сломалось сновакорень ставили внутри работающего контейнеразаглянуть в каталог якорей внутри нового контейнераперенести установку в Dockerfile
После обновления JDK Java снова не доверяетапдейт перезаписал cacertskeytool -list — алиаса нетповторный импорт либо свой truststore через флаг JVM
Verify return code: 21, сертификат в ответе одинсервер отдаёт только листgrep -c 'BEGIN CERTIFICATE'собрать fullchain и перечитать конфиг веб-сервера
С сервера 403, с ноутбука 200ограничение по адресам ЦОД, а не сертификатсравнить код выхода curl: 0 против 60тестировать на ресурсе, который не режет ЦОД
Сертификат «не найден» в crt.shвыпуск не логируется в публичных CTсверить с реальным ответом сервера через s_clientвести реестр сертификатов самостоятельно

Как проверить на enterno.io

  • SSL-чекер — цепочка, издатель, срок действия, протокол и набор шифров. Первое, что стоит открыть: поле issuer сразу показывает, российский это корень или международный.
  • Разбор ошибок SSL — если браузер уже показывает конкретный код ошибки.
  • Сканер безопасности — заголовки, HSTS, редирект на HTTPS и смешанный контент.
  • Мониторинг — SSL-монитор со сроком действия, предупреждение за 14 дней и критический алерт за 3 дня, уведомления в Telegram.
  • Проверка HTTP-заголовков — цепочка редиректов с http на https и что реально отдаёт сервер.

Смежные разборы: не удаётся проверить сертификат сервера, типы сертификатов DV, OV и EV, самоподписанные сертификаты, доступность сайта из России и из-за рубежа.

Частые вопросы

Российский УЦ и УЦ электронной подписи — это одно и то же?

Нет. Это разные организации, разные сертификаты и разные технологии, совпадает только русское слово «удостоверяющий центр». УЦ для web-TLS выпускает серверные сертификаты для сайтов. Аккредитованный УЦ электронной подписи выдаёт сертификаты для подписания документов — они работают с криптопровайдером и токеном и на веб-сервер не ставятся.

Мне выдали сертификат электронной подписи на токене — можно поставить его на сайт?

Нет. У сертификата подписи другое назначение в расширении Extended Key Usage, приватный ключ не выгружается с носителя, и веб-сервер такой сертификат как серверный не примет. Для сайта нужен отдельный TLS-сертификат с назначением serverAuth.

Почему curl на сервере ругается, а в браузере сайт открывается?

Потому что это два разных хранилища доверия. В вашем браузере корень уже есть — сам поставил или пришёл вместе со сборкой. На сервере системный бандл никто не трогал. Ставьте корень на сервер отдельно и проверяйте, не использует ли ваш рантайм собственное хранилище.

Достаточно ли поставить только корневой сертификат, без промежуточного?

Если сервер сам присылает промежуточный — да, корня достаточно. Если не присылает, клиенту неоткуда его взять. Надёжнее сделать два действия сразу: положить в якоря и корень, и промежуточный, и одновременно починить fullchain на сервере, чтобы не зависеть от настроек каждого клиента.

Как понять, что корень реально попал в системное хранилище?

На RHEL-подобных: trust list --filter=ca-anchors с грепом по имени. На Debian и Ubuntu — поиск по собранному файлу /etc/ssl/certs/ca-certificates.crt. Самая надёжная проверка — сравнить openssl s_client с флагом -CAfile и без него: если с флагом вердикт 0 (ok), а без флага нет, корень в системе не установлен.

Стоит ли ставить российский сертификат на публичный API?

Как правило, нет. Публичный API вызывают чужие серверы, чьи хранилища доверия вы не контролируете и не можете исправить. Каждый такой клиент увидит ошибку проверки пути, а починить её сможет только своими руками. Для публичных интеграций это дорогое решение.

Помогут ли CT-логи отследить выпуск российского сертификата?

Рассчитывать на это не стоит. Требование логировать выпуск в публичных логах прозрачности связано с корневыми программами браузеров, и для сертификатов вне этих программ оно не действует. Инвентаризацию через crt.sh и аналогичные сервисы придётся заменить собственным реестром и мониторингом по факту ответа сервера.

Чеклист

  • Определили, какой именно УЦ вам нужен: web-TLS для сайта или электронная подпись для документов.
  • Понимаете, какая доля вашей аудитории и каких интеграций не имеет российского корня.
  • Сервер отдаёт полную цепочку: листовой сертификат плюс промежуточные, счётчик BEGIN CERTIFICATE больше единицы.
  • Корневой и промежуточный сертификаты установлены в системное хранилище на всех серверах, которые ходят к российским сервисам.
  • Установка корня зафиксирована в Dockerfile или в конфигурации образа, а не сделана руками внутри работающего контейнера.
  • Проверены рантаймы со своим хранилищем: Java, Node.js, Python с requests.
  • Проверку установки делали не на gosuslugi.ru, а на ресурсе, который не ограничивает адреса дата-центров.
  • Умеете отличать код выхода curl 60 от HTTP 403 при коде выхода 0.
  • Есть мониторинг срока действия с алертом минимум за две недели, и он отделяет «истекает» от «не доверен».
  • Продление занесено в календарь: автопродления по ACME здесь, как правило, нет.

Проверьте свой сертификат и цепочку прямо сейчас: запустите SSL-проверку на enterno.io и включите мониторинг срока действия с алертами в Telegram. Если браузер уже показывает ошибку — начните с разбора кодов SSL-ошибок.

Проверьте ваш сайт прямо сейчас

Проверить SSL своего сайта →
Другие статьи: SSL/TLS
SSL/TLS
SSL Handshake Failed: причины ошибки и пошаговая диагностика
15.04.2026 · 1 444 просм.
SSL/TLS
ERR_CERT_AUTHORITY_INVALID: причины и как исправить
13.07.2026 · 695 просм.
SSL/TLS
Слабые cipher suites: как найти и отключить небезопасные шифры TLS
15.04.2026 · 600 просм.
SSL/TLS
TLS 1.3 vs TLS 1.2: что изменилось и как правильно мигрировать
15.04.2026 · 577 просм.