El mensaje de error
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
PKIX es el método con el que Java valida cadenas de certificados. Java toma el certificado del servidor y sigue a los emisores hasta una CA raíz del truststore. Si no lo consigue, corta la conexión. Ocurre igual con clientes HTTP, drivers JDBC, LDAP, Maven, Gradle o Jenkins.
Las causas más frecuentes
- El servidor no envía el certificado intermedio. Los navegadores suelen compensarlo descargando los certificados que faltan. Java no lo hace por defecto. Por eso funciona en el navegador y no en Java.
- Una CA interna o de empresa. El servidor usa un certificado de una CA propia que no está en el truststore de Java.
- Un proxy intercepta TLS. Proxys corporativos como Zscaler o Netskope sustituyen el certificado por uno propio. Java no conoce su raíz; el navegador sí, porque TI la distribuyó en el sistema.
- Un truststore propio sustituye a cacerts. Con
-Djavax.net.ssl.trustStore=…, Java usa solo ese archivo. Si no contiene las raíces públicas, falla cualquier otra conexión. - Un JDK antiguo. Las versiones muy antiguas de Java no conocen raíces nuevas.
Encontrar la causa
Comprueba primero el servidor. El comprobador SSL muestra qué certificados entrega el servidor y si la cadena está completa. En la línea de comandos:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null
Si solo aparece un certificado (depth=0) emitido por una CA intermedia, falta la cadena. Después comprueba qué truststore usa realmente Java:
java -Djavax.net.debug=ssl:trustmanager -jar app.jar 2>&1 | grep -i "truststore"
La salida indica el archivo, por ejemplo trustStore is: /usr/lib/jvm/java-21/lib/security/cacerts. Con -Djavax.net.debug=ssl:handshake ves además qué certificados envió el servidor.
Solución 1: reparar la cadena en el servidor
Si falta el certificado intermedio, hay que añadirlo en el servidor. Es la única solución que ayuda a todos los clientes a la vez. En nginx el intermedio va en el mismo archivo, detrás del certificado del servidor (fullchain.pem); igual en SSLCertificateFile de Apache. El conversor de certificados completa automáticamente los intermedios que faltan.
Solución 2: añadir la CA al truststore
Con CA internas o un proxy TLS tienes que dar a conocer la CA raíz a Java. Usa la CA raíz o intermedia, no el certificado del servidor; si no, vuelve a fallar en el siguiente cambio de certificado.
# Java 9 o superior: directamente en el cacerts del JDK en uso keytool -importcert -cacerts -alias raiz-empresa -file raiz-empresa.crt -storepass changeit -noprompt # truststore propio para una sola aplicación keytool -importcert -keystore truststore.p12 -storetype PKCS12 -alias raiz-empresa -file raiz-empresa.crt java -Djavax.net.ssl.trustStore=truststore.p12 -Djavax.net.ssl.trustStorePassword=secreto -jar app.jar
Cuidado con el truststore propio: sustituye por completo a cacerts. Si la aplicación también debe llegar a servidores públicos, copia primero cacerts y añade ahí la CA. Más en el artículo importar un certificado en cacerts.
Sin keytool, en el navegador: en el comprobador SSL pulsa «Añadir al truststore de Java». La cadena se abre en el asistente de keystores Java, donde eliges la CA y guardas el truststore como JKS o PKCS#12.
Solución 3: usar el almacén del sistema
Si TI ya ha distribuido la CA de empresa en el sistema operativo, Java puede usar ese almacén directamente:
- Windows:
-Djavax.net.ssl.trustStoreType=Windows-ROOT - macOS:
-Djavax.net.ssl.trustStore=NONE -Djavax.net.ssl.trustStoreType=KeychainStorelee el llavero del usuario. - Debian, Ubuntu, RHEL: cacerts ya se genera a partir del almacén del sistema. Añade la CA ahí y ejecuta
update-ca-certificatesoupdate-ca-trust.
Lo que no deberías hacer
Circulan fragmentos de código con un TrustManager que lo acepta todo u opciones como -Dmaven.wagon.http.ssl.insecure=true. La conexión queda «cifrada», pero cualquiera en la red puede hacerse pasar por el servidor. Como mucho para una prueba rápida, nunca en producción.
FAQ
¿Por qué la web funciona en el navegador y no en Java?
Los navegadores descargan los intermedios que faltan mediante la dirección del certificado (AIA) y usan el almacén del sistema. Java no hace ninguna de las dos cosas por defecto. Normalmente falta un intermedio en el servidor o la CA de empresa solo está en el sistema.
¿Puede Java descargar por sí mismo los intermedios que faltan?
Sí, con -Dcom.sun.security.enableAIAcaIssuers=true. Es un parche: necesita acceso a Internet hacia la CA y ralentiza la primera conexión. Mejor reparar la cadena en el servidor.
¿Tengo que volver a importar la CA tras actualizar Java?
Con certificados importados a mano en el cacerts del JDK, a menudo sí, porque las actualizaciones sustituyen el archivo. En Linux con el almacén del sistema o con un truststore propio fuera del JDK, no.
¿Qué diferencia hay con «unable to get local issuer certificate»?
Es el mismo error visto desde OpenSSL, curl o Python. Causas y soluciones son las mismas.