Перейти к содержимому
ecdsa.com

Самоподписанный сертификат или подписанный CA: что выбрать

Шифруют оба одинаково хорошо. Разница в том, кто ручается за ключ, как клиенты начинают ему доверять и что ломается при его ротации.

Самоподписанный сертификат или подписанный CA — это вопрос о том, кто ручается за связь между открытым ключом и именем. Самоподписанный сертификат подписан собственным ключом, поэтому доказывает лишь то, что у его владельца есть этот ключ; клиент доверяет такому сертификату, только если клиенту прямо указали доверять именно ему. Сертификат, подписанный CA, выпущен центром сертификации (certificate authority, CA), корневому сертификату которого клиент уже доверяет: публичным CA — для интернета или частным CA, которым вы управляете и корневой сертификат которого распространяете сами.

Коротко о главном

СамоподписанныйЧастный CAПубличный CA
Кто подписывает конечный сертификатСобственный ключ этого сертификатаВаш корневой или промежуточный CACA из хранилищ корневых сертификатов браузеров и ОС
Как клиенты начинают ему доверятьКаждый клиент закрепляет или устанавливает именно этот сертификатУстановка одного корневого сертификата (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-managerLet's Encrypt и другие CA с поддержкой ACME

Как клиент решает, доверять ли сертификату

Проверка цепочки сертификатов (RFC 5280, § 6) выстраивает цепочку от сертификата сервера до якоря доверия в хранилище корневых сертификатов клиента и по пути проверяет каждую подпись, срок действия, основные ограничения, использование ключа и ограничения имён. У самоподписанного сертификата издатель и субъект совпадают, а подписан он собственным ключом, поэтому единственная возможная цепочка состоит из самого этого сертификата. Проверку он проходит, только если сам является для клиента якорем доверия, — именно это и называют «закреплением» или «установкой» самоподписанного сертификата.

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

Скрипт ниже, запущенный с OpenSSL 3.6.5, создаёт самоподписанный сертификат, частный корневой CA, ограниченный именами в зоне .internal, и 2 конечных сертификата, подписанных этим корневым CA.

Самоподписанный сертификат, частный 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.10DEPTH_ZERO_SELF_SIGNED_CERTUNABLE_TO_VERIFY_LEAF_SIGNATURE
Python 3.14 (OpenSSL 3.6.5)certificate verify failed: self-signed certificatecertificate verify failed: unable to get local issuer certificate
Go 1.25 (системная проверка macOS)x509: certificate signed by unknown authorityx509: “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_clientVerify 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.

Заметки о миграции

  1. Проведите инвентаризацию используемых самоподписанных сертификатов: просканируйте внутренние эндпоинты и соберите сертификаты, закреплённые в клиентах; декодер сертификатов показывает издателя, SAN и срок действия.
  2. Разверните частный CA, ограниченный вашими внутренними доменами, с корневым CA, который хранится офлайн, и промежуточным CA для выпуска сертификатов; запросы создавайте в генераторе CSR.
  3. Распространите корневой сертификат до того, как переключать какой-либо сервер, затем заменяйте конечные сертификаты сервис за сервисом; сборщик цепочки сертификатов подтвердит, что каждый сервер отдаёт свой промежуточный сертификат.
  4. Уберите небезопасные обходные решения (-k, verify=False, InsecureSkipVerify), как только клиенты начнут доверять корневому сертификату, и поищите их по кодовой базе, чтобы они не вернулись.
  5. Переведите публично доступные имена на публичный CA с ACME, а закрепление (если оно есть) делайте по ключам, а не по сертификатам; генератор пинов SPKI вычисляет пины, которые переживают продление сертификата с тем же ключом.

Источники

Все сравнения