Mensaje de error
SSL routines:ssl3_read_bytes:ssl/tls alert bad certificate
También se muestra como
- SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
- error:0A000412:SSL routines::ssl/tls alert bad certificate
OpenSSL 3.3 y las versiones posteriores imprimen «ssl/tls alert bad certificate», y las anteriores, «sslv3 alert bad certificate» (código 0A000412; 1.1.1 usaba 14094412), seguido de «SSL alert number 42». Los propios clientes OpenSSL envían unknown_ca (48) o certificate_expired (45) ante los mismos rechazos.
OpenSSL — TLS alert 42 (bad_certificate) received from the peer
Qué significa
El otro extremo envió la alerta TLS 42 (bad_certificate): rechazó el certificado que presentó este lado. El paquete crypto/tls de Go envía esta alerta ante cualquier certificado de servidor que no consigue verificar (autoridad desconocida, caducado, nombre incorrecto), así que los servidores basados en OpenSSL (nginx, HAProxy, Postfix, s_server) la registran siempre que un cliente Go rechaza su certificado. En TLS mutuo, un servidor la envía cuando rechaza el certificado de cliente.
Por qué ocurre
Un cliente Go rechazó el certificado del servidor
frecuenteLos programas en Go (componentes de Kubernetes, Terraform, Prometheus, Docker, muchas CLI) envían bad_certificate cuando falla la verificación. El lado de Go muestra el motivo real, por ejemplo «x509: certificate signed by unknown authority».
TLS mutuo: el servidor rechazó el certificado de cliente
frecuenteEl certificado de cliente está caducado, no lo emitió una CA aceptada o, por algún otro motivo, el servidor no lo acepta.
El uso de clave del certificado no permite su función
ocasionalLos verificadores estrictos como Go rechazan un certificado cuyo extendedKeyUsage no incluye serverAuth (o clientAuth, en los certificados de cliente).
Cómo solucionarlo
- 1.
Encontrar el cliente que rechaza y el motivo
La línea del registro indica la dirección del cliente; en ese cliente, el mensaje de error da la causa real. Verificar la cadena desde fuera suele mostrarla directamente.
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.
Clientes Go: confiar en la CA o corregir la cadena
Sirve una cadena completa de una CA de confianza o añade la CA privada a los RootCAs del cliente Go; nunca uses InsecureSkipVerify como solución.
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 mutuo: comprobar el certificado de cliente
Las fechas de validez, el emisor y el uso de clave del certificado de cliente deben coincidir con lo que espera el servidor.
bash # Mutual TLS: check the client certificate the server rejected openssl x509 -in client.pem -noout -issuer -dates -ext extendedKeyUsage,keyUsage