Сообщение об ошибке
SSL routines:ssl3_read_bytes:ssl/tls alert bad certificate
Также выводится как
- SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
- error:0A000412:SSL routines::ssl/tls alert bad certificate
OpenSSL 3.3 и новее выводят «ssl/tls alert bad certificate», более ранние версии — «sslv3 alert bad certificate» (код 0A000412; в 1.1.1 был 14094412), а затем «SSL alert number 42». Сами клиенты OpenSSL в тех же случаях отправляют unknown_ca (48) или certificate_expired (45).
OpenSSL — TLS alert 42 (bad_certificate) received from the peer
Что это означает
Другая сторона отправила оповещение TLS 42 (bad_certificate): она отвергла сертификат, который предъявила эта сторона. Пакет crypto/tls в Go отправляет это оповещение для любого сертификата сервера, который не прошёл проверку (неизвестный центр сертификации, истёкший срок действия, неверное имя), поэтому серверы на базе OpenSSL (nginx, HAProxy, Postfix, s_server) записывают его в журнал каждый раз, когда клиент на Go отвергает их сертификат. При взаимной TLS-аутентификации сервер отправляет его, когда отвергает клиентский сертификат.
Почему это происходит
Клиент на Go отверг сертификат сервера
частоПрограммы на Go (компоненты Kubernetes, Terraform, Prometheus, Docker, многие CLI) отправляют bad_certificate, когда проверка не проходит. Настоящую причину показывает сторона Go, например «x509: certificate signed by unknown authority».
Взаимная TLS-аутентификация: сервер отверг клиентский сертификат
частоСрок действия клиентского сертификата истёк, сертификат выпущен CA, которого сервер не принимает, или по другой причине неприемлем для сервера.
Использование ключа в сертификате не допускает роль, в которой он применяется
иногдаСертификат, в extendedKeyUsage которого нет serverAuth (или clientAuth для клиентских сертификатов), отвергают строгие проверяющие стороны, например Go.
Как исправить
- 1.
Найдите клиента, который отвергает сертификат, и выясните причину
В строке журнала указан адрес клиента; на этом клиенте сообщение об ошибке называет настоящую причину. Проверка цепочки извне часто сразу её показывает.
bash # Server log: which client rejected the certificate, and does the chain verify from outside? openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \ | grep -E 'Verify return code' - 2.
Клиенты на Go: добавьте CA в доверенные или исправьте цепочку
Отдавайте полную цепочку от доверенного CA или добавьте частный CA в RootCAs клиента на Go; никогда не используйте InsecureSkipVerify как решение.
go // Go clients: trust the server's CA explicitly (keeps verification on) pool, _ := x509.SystemCertPool() caPEM, _ := os.ReadFile("internal-ca.pem") pool.AppendCertsFromPEM(caPEM) client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{RootCAs: pool}}} - 3.
Взаимная TLS-аутентификация: проверьте клиентский сертификат
Сроки действия, издатель и использование ключа клиентского сертификата должны соответствовать тому, что ожидает сервер.
bash # Mutual TLS: check the client certificate the server rejected openssl x509 -in client.pem -noout -issuer -dates -ext extendedKeyUsage,keyUsage