TLS и OpenSSL: шпаргалка по отладке сертификатов

Практическая шпаргалка для понимания сертификатов, устранения ошибок TLS-рукопожатия и отладки реальных проблем с помощью OpenSSL, curl и Kubernetes.

Схема работы самоподписанного сертификата
Примечание

Ошибки TLS вроде «CERTIFICATE_VERIFY_FAILED» или «unable to get local issuer certificate» — одни из самых неприятных проблем в продакшн-системах.

🎯 В этом руководстве мы разбираем TLS от основ до реальной отладки с помощью OpenSSL, curl и Kubernetes.

🔎 Что вы узнаете

К концу руководства вы сможете:

  • понять, как работают удостоверяющие центры (Certificate Authorities, CA);

  • эффективно использовать OpenSSL для инспекции и отладки сертификатов;

  • создать собственный корневой CA и подписать им серверные сертификаты;

  • проверять TLS-рукопожатие локально;

  • разбираться в распространённых кодах TLS-оповещений (TLS alert) и кодах возврата x509;

  • работать с TLS в среде Kubernetes.

🔐 Что такое удостоверяющий центр (CA)?

Удостоверяющий центр — это доверенная организация, которая:

  • выдаёт и подписывает цифровые сертификаты (как публичные организации, например DigiCert, так и внутренние корпоративные системы);

  • верифицирует личность (по аналогии с паспортным столом);

  • обеспечивает защищённый обмен данными.

⚠️ Почему ошибки TLS встречаются так часто?

Большинство сбоев TLS происходит по следующим причинам:

  • отсутствие цепочки CA;

  • неправильная запись SAN (Subject Alternative Name);

  • некорректное использование ключа (Key Usage);

  • неверно настроенные секреты Kubernetes;

  • ошибки в ограничениях CA.

Всё это разбирается в руководстве с реальными командами.

🧰 Шпаргалка по командам OpenSSL

1. Команды для инспекции сертификатов

🔹 Просмотр полного сертификата

openssl x509 -in RootCA.pem -text
openssl x509 -in RootCA.pem -text -noout

🔹 Проверка срока действия

openssl x509 -noout -dates -in RootCA.pem
  1. Дата начала действия

openssl x509 -startdate -noout -in RootCA.pem
  1. Дата окончания действия

openssl x509 -enddate -noout -in RootCA.pem

🔹 Извлечение Subject

openssl x509 -noout -subject -in RootCA.pem

🔹 Извлечение Issuer

openssl x509 -noout -issuer -in RootCA.pem

🔹 Извлечение SAN

openssl x509 -in RootCA.pem -text -noout | grep -A1 "Subject Alternative Name"

🔹 Проверка типа ключа

openssl x509 -in RootCA.pem -text -noout | grep "Public Key Algorithm"

2. Операции с закрытым ключом

🔹 Генерация RSA-ключа длиной 4096 бит

openssl genrsa -out tls.key 4096

🔹 Генерация RSA-ключа с шифрованием AES-256

openssl genrsa -aes256 -out tls.key 4096

🔹 Просмотр сгенерированного RSA-ключа

openssl rsa -in tls.key -text
openssl rsa -in tls.key -text -noout

🔹 Удаление парольной фразы

openssl rsa -in encrypted.key -out decrypted.key

🔹 Проверка сгенерированного RSA-ключа

openssl rsa -check -in tls.key

🔹 Извлечение открытого ключа из закрытого

openssl rsa -in tls.key -pubout -out tls.pub

3. Проверка соответствия сертификата и закрытого ключа

Сравнение модулей (Modulus):

openssl x509 -noout -modulus -in tls.crt | openssl md5
openssl rsa -noout -modulus -in tls.key | openssl md5
Примечание

Хеши должны совпадать.

4. Проверка TLS-соединения с удалённым хостом

openssl s_client -connect <fqdn>:<port> -cert <path-to-client-cert> -key <path-to-client-key> -CAfile <path-to-root-ca-cert>

5. Загрузка корневого или серверного сертификата с удалённого хоста

Укажите FQDN или IP-адрес и порт:

openssl s_client -showcerts -connect <fqdn>:<port> </dev/null 2>/dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > RootCA.pem

6. Получение дополнительных сведений о корневом или серверном сертификате

openssl s_client -showcerts -connect <fqdn>:<port> </dev/null 2>/dev/null > RootCA.pem

🏛 Создание CA и самоподписанного серверного сертификата

Самоподписанный сертификат (self-signed certificate) — это сертификат, не выданный удостоверяющим центром: в нём поля Subject и Issuer совпадают. Важно отметить, что корневой сертификат тоже является самоподписанным.

  1. 🔐 Создайте собственный CA.

  2. 🔑 Сгенерируйте зашифрованный закрытый ключ сервера.

  3. 🗒 Сгенерируйте CSR (Certificate Signing Request, запрос на подпись сертификата).

  4. ✍️ Подпишите серверный сертификат с помощью созданного CA.

  5. ✅ Проверьте цепочку сертификатов.

Создание собственного CA

🔹 Сначала создайте файл rootca.cnf с конфигурацией CA — он используется при генерации корневого сертификата. Секция расширений помечена как критическая (Critical Section):

[ req ]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca

[ dn ]
C = IN
ST = GUJARAT
L = Ahmedabad
OU = TechInsightDev
CN = ROOTCA

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign

⚠️ Если basicConstraints не помечен как critical, строгие клиенты OpenSSL завершат проверку с ошибкой.

🔹 Генерация закрытого корневого ключа

openssl genrsa -out RootCA.key 4096

🔹 Генерация сертификата CA

openssl req -x509 -new -nodes -key RootCA.key -sha256 -days 1825 -out RootCA.pem -config rootca.cnf

🏁 Срок действия корневого сертификата задаётся флагом -days.

🔹 Конфигурация серверного сертификата с SAN. Файл server.cnf приведён для сервера в США:

default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no

[req_distinguished_name]
CN = techinsight.com
OU = TechInsightDev
L = Sacramento
ST = California
C = US

[req_ext]
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = techinsight.com
DNS.2 = techinsight.dev.com

# IP Address can be mapped as well
# [ alt_names ]
# DNS.1 = localhost
# IP.1  = 127.0.0.1

🔹 Генерация CSR и закрытого ключа сервера

openssl req -nodes -new -keyout tls.key -out tls.csr --config server.cnf

🔹 Подписание серверного сертификата корневым CA

openssl x509 -req -in tls.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -out tls.crt -days 1825 -sha256 -extensions req_ext -extfile server.cnf

🔹 Проверка цепочки сертификатов относительно CA

openssl verify -CAfile RootCA.pem tls.crt

tls.crt: OK

На этом этапе вы успешно создали доверенную цепочку сертификатов, обеспечивающую защищённое TLS-соединение между клиентом и сервером.

🧪 Локальное тестирование CA и серверного сертификата

  1. 🖥 Один процесс выступает в роли TLS-клиента.

  2. 💻 Другой процесс выступает в роли TLS-сервера.

  3. 🔐 Проверяется корректность TLS-рукопожатия.

  4. ✅ Цепочка сертификатов проверяется с помощью созданного CA.

▪️Способ 1️⃣: Тестирование через командную строку OpenSSL (самый простой вариант)

Запустите TLS-сервер:

openssl s_server -cert tls.crt -key tls.key -CAfile RootCA.pem -accept 8080 -www

Сервер начнёт слушать на localhost:8080.

Откройте второй терминал и подключитесь как TLS-клиент:

openssl s_client -connect localhost:8080 -CAfile RootCA.pem -msg -debug

В консоли должен появиться вывод, подтверждающий ✅ успешное рукопожатие:

SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
....
Start Time: 1774086113
Timeout   : 7200 (sec)
Verify return code: 0 (ok)

▪️Способ 2️⃣: Тестирование через веб-браузер

Примечание: требуется установленный nginx.

  1. Отредактируйте nginx.conf в секции HTTPS-сервера.

Укажите порт, на котором будет работать сервер, и пути к ssl_certificate и ssl_certificate_key.

Конфигурационный файл nginx с настройками HTTPS
  1. Добавьте имя хоста в файл /etc/hosts.

Файл /etc/hosts с добавленной записью хоста
  1. Откройте браузер и перейдите по адресу https://localhost:8081.

Браузер показывает ошибку ERR_CERT_AUTHORITY_INVALID

Браузер выдаст ошибку: ERR_CERT_AUTHORITY_INVALID.

Это происходит по следующим причинам:

  • Созданный приватный CA не является публично доверенным, и его сертификаты не включены в стандартные хранилища браузеров и операционных систем.

  • Браузеры не доверяют ему по умолчанию.

Решения:

  • Импортировать CA в системное хранилище доверия (trust store), или

  • использовать команду curl с явным указанием RootCA.pem — она отработает успешно:

curl https://techinsight.dev.com:8081 --cacert RootCA.pem
  1. Добавьте приватный CA в системное хранилище доверия.

macOS:

sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" RootCA.pem

Linux/Ubuntu:

sudo cp rootCA.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

После добавления сертификат появится в хранилище системных корневых сертификатов (System Roots). Пример из приложения Keychain Access на macOS:

Keychain Access на macOS с добавленным корневым CA
  1. Перейдите по адресу https://techinsight.dev.com:8081 — должна отобразиться стандартная страница nginx.

Страница nginx

▪️Способ 3️⃣: Тестирование через командную строку curl

Запустите TLS-сервер:

openssl s_server -cert tls.crt -key tls.key -accept 8080 -www

В другом терминале выполните запрос через curl:

curl https://techinsight.dev.com:8081 --cacert RootCA.pem

🚨 Распространённые коды возврата x509 Verify

Таблица кодов возврата x509

🚨 Распространённые коды TLS-оповещений (Protocol Level)

Таблица кодов TLS-оповещений

Пример:

sslv3 alert certificate unknown (46)

Означает, что удалённая сторона отклонила ваш сертификат.

Наиболее частые сбои в продакшне

🔴 Несоответствие имени хоста (Hostname mismatch) — в SAN отсутствует нужная запись DNS/IP.

🔴 CA не является доверенным (CA Not Trusted) — у клиента отсутствует файл CA.

🔴 Неверное расширенное использование ключа (Extended Key Usage).

Отсутствует:

extendedKeyUsage = serverAuth

🔴 У CA отсутствует критическое ограничение Basic Constraints.

Должно быть:

basicConstraints = critical, CA:TRUE

🔴 Unable to get local issuer certificate — отсутствует цепочка CA.

🔴 Bad certificate — неверный ключ или несоответствие между ключом и сертификатом.

☸️ Работа с TLS-секретами в Kubernetes

⚠️ Kubernetes не поддерживает закрытые ключи, защищённые парольной фразой, при создании TLS-секретов.

Создание секрета из сертификата:

kubectl create secret tls my-tls \
--key tls.key \
--cert tls.crt \
--dry-run=client -o yaml | kubectl apply -f -

🔴 Создание завершится ошибкой, если ключ зашифрован парольной фразой.

Просмотр секрета:

kubectl get secret my-tls -o jsonpath='{.data.tls\.crt}' | base64 -d > tls.crt

⚙️ Чеклист отладки (реальный инцидент)

  1. Проверить срок действия сертификата.

  2. Проверить записи SAN.

  3. Проверить критический флаг CA.

  4. Убедиться, что ключ соответствует сертификату.

  5. Проверить полную цепочку.

  6. Протестировать с флагом -verify_return_error.

  7. Сравнить коды TLS-оповещений и коды возврата x509.

  8. Проверить расширенное использование ключа (EKU).

  9. Убедиться, что секрет Kubernetes обновлён.

📓 Рекомендации по SAN

Всегда включайте:

DNS.1 = service.namespace.svc.cluster.local
DNS.2 = service
DNS.3 = localhost
IP.1 = 127.0.0.1

Современные реализации TLS полностью игнорируют поле CN.

🏁 Заключение

Отладка TLS поначалу кажется запутанной, но становится предсказуемой, когда вы понимаете:

  • как работают цепочки сертификатов;

  • почему SAN обязателен;

  • как проверяется доверие к CA;

  • в чём разница между TLS-оповещениями и ошибками x509.

С командами и реальными сценариями из этой статьи у вас теперь есть готовый к продакшну инструментарий для отладки:

  • неправильно настроенных сертификатов;

  • проверки TLS/SSL-рукопожатия;

  • проблем с TLS в Kubernetes.

Если это руководство помогло вам разобраться с TLS или устранить ошибки — поставьте 👏 и поделитесь с командой! Возможно, это сэкономит кому-то часы поиска неисправностей.

© 2026 meganuke