Самоподписанный сертификат или подписанный CA — это вопрос о том, кто ручается за связь между открытым ключом и именем. Самоподписанный сертификат подписан собственным ключом, поэтому доказывает лишь то, что у его владельца есть этот ключ; клиент доверяет такому сертификату, только если клиенту прямо указали доверять именно ему. Сертификат, подписанный CA, выпущен центром сертификации (certificate authority, CA), корневому сертификату которого клиент уже доверяет: публичным CA — для интернета или частным CA, которым вы управляете и корневой сертификат которого распространяете сами.
Коротко о главном
| Самоподписанный | Частный CA | Публичный CA | |
|---|---|---|---|
| Кто подписывает конечный сертификат | Собственный ключ этого сертификата | Ваш корневой или промежуточный CA | CA из хранилищ корневых сертификатов браузеров и ОС |
| Как клиенты начинают ему доверять | Каждый клиент закрепляет или устанавливает именно этот сертификат | Установка одного корневого сертификата (MDM, система управления конфигурацией, образ контейнера) | Доверие уже есть по умолчанию |
| Ротация конечного сертификата | Нужно обновить каждого клиента | Незаметна для клиентов | Незаметна для клиентов |
| Отзыв | Не предусмотрен; сертификат нужно удалить с каждого клиента | Собственные CRL, OCSP или короткие сроки действия | CRL, которые публикует CA |
| Какие имена может содержать | Любые | Любые в пределах ограничений корневого сертификата | Только публичные имена доменов и публичные IP-адреса |
| Ограничения срока действия | Со стороны браузеров нет; платформы Apple ограничивают TLS-сертификаты, выпущенные с июля 2019 года, сроком в 825 дней | Тот же предел в 825 дней на платформах Apple | Сейчас 200 дней, с марта 2029 года — 47 дней |
| Certificate Transparency | Не попадает в журналы | Не попадает в журналы | Публикуется в журналах вместе с каждым именем хоста |
| Затраты и усилия | Заранее никаких; высокие при каждой ротации | Эксплуатация CA и защита его ключа | Бесплатно с ACME; нужна проверка через DNS или HTTP |
| Типичные инструменты | openssl req -x509, mkcert для локальной разработки | step-ca, HashiCorp Vault PKI, облачные частные CA, cert-manager | Let's Encrypt и другие CA с поддержкой ACME |
Как клиент решает, доверять ли сертификату
Проверка цепочки сертификатов (RFC 5280, § 6) выстраивает цепочку от сертификата сервера до якоря доверия в хранилище корневых сертификатов клиента и по пути проверяет каждую подпись, срок действия, основные ограничения, использование ключа и ограничения имён. У самоподписанного сертификата издатель и субъект совпадают, а подписан он собственным ключом, поэтому единственная возможная цепочка состоит из самого этого сертификата. Проверку он проходит, только если сам является для клиента якорем доверия, — именно это и называют «закреплением» или «установкой» самоподписанного сертификата.
Конечный сертификат, выпущенный CA, подписан промежуточным CA, цепочка которого, в свою очередь, ведёт к корневому CA. Клиенту никогда не нужно знать конечный сертификат заранее: ему достаточно корневого. Именно это опосредованное доверие делает возможными ротацию, отзыв и автоматизацию.
Скрипт ниже, запущенный с OpenSSL 3.6.5, создаёт самоподписанный сертификат, частный корневой CA, ограниченный именами в зоне .internal, и 2 конечных сертификата, подписанных этим корневым CA.
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
P256="-newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc"
# 1. Self-signed server certificate
openssl req -x509 $P256 -keyout self.key -out self.pem -days 30 \
-subj "/CN=dev.internal" \
-addext "subjectAltName=DNS:dev.internal,IP:127.0.0.1" \
-addext "basicConstraints=critical,CA:FALSE" 2>/dev/null
openssl verify self.pem
openssl verify -CAfile self.pem self.pem
# 2. Private root CA limited to .internal names
openssl req -x509 $P256 -keyout ca.key -out ca.pem -days 3650 \
-subj "/CN=Example Internal Root" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-addext "nameConstraints=critical,permitted;DNS:.internal" 2>/dev/null
# 3. Two server certificates signed by that CA
for name in api.internal www.example.com; do
openssl req -x509 $P256 -CA ca.pem -CAkey ca.key -keyout "$name.key" \
-out "$name.pem" -days 90 -subj "/CN=$name" \
-addext "subjectAltName=DNS:$name" \
-addext "basicConstraints=critical,CA:FALSE" \
-addext "extendedKeyUsage=serverAuth" 2>/dev/null
done
openssl verify api.internal.pem
openssl verify -CAfile ca.pem api.internal.pem
openssl verify -CAfile ca.pem www.example.com.pem
cd / && rm -rf "$dir"
CN=dev.internal
error 18 at 0 depth lookup: self-signed certificate
error self.pem: verification failed
self.pem: OK
CN=api.internal
error 20 at 0 depth lookup: unable to get local issuer certificate
error api.internal.pem: verification failed
api.internal.pem: OK
CN=www.example.com
error 47 at 0 depth lookup: permitted subtree violation
error www.example.com.pem: verification failed
Самоподписанный сертификат проходит проверку, только если передать его как собственный якорь доверия. Конечный сертификат частного CA проходит проверку, как только клиент начинает доверять корневому сертификату, а ограничения имён в корневом сертификате не дают ему ручаться за www.example.com, и это ограничивает ущерб, если ключом CA когда-нибудь злоупотребят. Учтите, что openssl req -x509 по умолчанию помечает сертификаты как сертификаты CA через свой файл конфигурации; явное CA:FALSE не позволяет использовать самоподписанный сертификат сервера в качестве издателя.
Что сообщают клиенты, когда не доверяют сертификату
Мы подключили 5 клиентов к локальному серверу, который по очереди предъявлял каждый из сертификатов, и ничего не добавляли в их хранилища доверенных сертификатов (20 октября 2026 года, macOS).
| Клиент | Самоподписанный сертификат | Конечный сертификат частного CA, которому нет доверия |
|---|---|---|
| Node.js 26.10 | DEPTH_ZERO_SELF_SIGNED_CERT | UNABLE_TO_VERIFY_LEAF_SIGNATURE |
| Python 3.14 (OpenSSL 3.6.5) | certificate verify failed: self-signed certificate | certificate verify failed: unable to get local issuer certificate |
| Go 1.25 (системная проверка macOS) | x509: certificate signed by unknown authority | x509: “localhost” certificate is not trusted |
| curl 8.7.1 (LibreSSL) | (60) SSL certificate problem: self signed certificate | (60) SSL certificate problem: unable to get local issuer certificate |
OpenSSL 3.6.5 s_client | Verify return code: 18 (self-signed certificate) | Verify return code: 20 (unable to get local issuer certificate) |
Браузеры показывают NET::ERR_CERT_AUTHORITY_INVALID (Chrome) или MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT (Firefox). Отключение проверки (NODE_TLS_REJECT_UNAUTHORIZED=0, verify=False, InsecureSkipVerify, curl -k) никогда не решает проблему: оно убирает аутентификацию у каждого соединения, которое устанавливает процесс. Вместо этого передайте клиенту сертификат или корневой сертификат, которому он должен доверять (ca: в Node.js, cafile= в Python, собственный пул RootCAs в Go, --cacert в curl), или установите корневой сертификат в хранилище операционной системы. Сертификат без DNS-имени в расширении Subject Alternative Name (альтернативное имя субъекта) не проходит проверку по другой причине: Chrome перестал сверять общее имя (CN) в версии 58 (2017), а платформы Apple — начиная с iOS 13 и macOS 10.15.
Золотая середина: частный CA
Частный CA сохраняет главное преимущество самоподписанных сертификатов (никаких сторонних участников, любые внутренние имена, никакой публикации в журналах) и устраняет их главный недостаток (повторное распространение доверия при каждой ротации). Практические правила:
- Держите корневой CA офлайн, а сертификаты выпускайте от промежуточного; скомпрометированный промежуточный CA можно заменить, не трогая каждого клиента.
- Ограничьте имена своими внутренними зонами, как в примере выше, и задайте строгие
pathlenи использование ключа. - Автоматизируйте выпуск конечных сертификатов: step-ca, Vault PKI и cert-manager умеют выпускать их через ACME или собственные API, так что конечные сертификаты могут жить часы или дни, а отзыв становится не нужен.
- Всё равно соблюдайте требования клиентов: для TLS-сертификатов, выпущенных с 1 июля 2019 года, платформы Apple требуют DNS-имя в SAN, расширенное использование ключа
serverAuthи срок действия не более 825 дней, в том числе для частных корневых сертификатов; ограничение в 398 дней действует только для публично доверенных корневых сертификатов. - Спланируйте распространение корневого сертификата: он нужен хранилищам операционных систем, хранилищам ключей Java, образам контейнеров, браузерам с собственными хранилищами (Firefox) и программам, которые поставляются со своим набором сертификатов (пакет
certifi, на который опираетсяrequestsв Python, и Node.js, который использует встроенный набор Mozilla, если не запущен с--use-system-ca).
Где публичный CA не поможет
Базовые требования (Baseline Requirements, § 4.2.2) запрещают публичным CA выпускать сертификаты с внутренними именами или зарезервированными IP-адресами. Поэтому такие имена, как printer.local, однокомпонентные имена хостов, 10.0.0.5 или любые имена в зоне .internal, которую ICANN 29 июля 2024 года навсегда зарезервировала для частного использования, могут быть только в сертификатах частного CA или в самоподписанных сертификатах. Внутренние хосты с публичными именами (например, db.corp.example.com) могут получить публичные сертификаты через проверку DNS-01, оставаясь недоступными из интернета, но тогда каждое имя попадает в публичные журналы Certificate Transparency, и любой, кто ищет по этим журналам, видит топологию вашей внутренней сети.
Что когда выбирать
- Публичные сайты, API и почтовые серверы: публичный CA с ACME.
- Внутренние сервисы, сервисные сети (service mesh), mTLS между рабочими нагрузками: частный CA с краткосрочными конечными сертификатами.
- Локальная разработка: локальный CA для разработки, например
mkcert, который устанавливает свой корневой сертификат в ваши собственные хранилища доверенных сертификатов, а не самоподписанные сертификаты, которые каждый инструмент отклоняет по-своему. - Устройства и аппаратные комплексы при первой загрузке: самоподписанный сертификат, отпечаток которого пользователь или система подготовки устройств один раз проверяет, а затем — регистрация в частном CA.
- Тестовые фикстуры: самоподписанные сертификаты, которые генерирует сам тест и явно передаёт тестируемому клиенту.
- Партнёры и B2B-интеграции через mTLS: отдельный частный CA для каждой такой интеграции, и никогда не ваш общий внутренний корневой CA.
Заметки о миграции
- Проведите инвентаризацию используемых самоподписанных сертификатов: просканируйте внутренние эндпоинты и соберите сертификаты, закреплённые в клиентах; декодер сертификатов показывает издателя, SAN и срок действия.
- Разверните частный CA, ограниченный вашими внутренними доменами, с корневым CA, который хранится офлайн, и промежуточным CA для выпуска сертификатов; запросы создавайте в генераторе CSR.
- Распространите корневой сертификат до того, как переключать какой-либо сервер, затем заменяйте конечные сертификаты сервис за сервисом; сборщик цепочки сертификатов подтвердит, что каждый сервер отдаёт свой промежуточный сертификат.
- Уберите небезопасные обходные решения (
-k,verify=False,InsecureSkipVerify), как только клиенты начнут доверять корневому сертификату, и поищите их по кодовой базе, чтобы они не вернулись. - Переведите публично доступные имена на публичный CA с ACME, а закрепление (если оно есть) делайте по ключам, а не по сертификатам; генератор пинов SPKI вычисляет пины, которые переживают продление сертификата с тем же ключом.
Источники
- RFC 5280, § 6: проверка пути сертификации и § 4.2.1.10: ограничения имён
- Базовые требования CA/Browser Forum, § 4.2.2
- Apple: требования к доверенным сертификатам в iOS 13 и macOS 10.15
- Apple: о предстоящих ограничениях для доверенных сертификатов (398 дней)
- Резолюция совета ICANN 2024.07.29.06 о резервировании .INTERNAL
- RFC 9162: Certificate Transparency 2.0
- RFC 8555: ACME