エラーメッセージ
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)を送信しました。こちら側が提示した証明書をピアが拒否したということです。Go の crypto/tls は、検証に失敗したサーバー証明書に対して、理由(不明な認証局、期限切れ、名前の不一致)を問わずこのアラートを送信します。そのため、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 を Go クライアントの RootCAs に追加してください。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