Le message d’erreur
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
PKIX est la méthode qu’utilise Java pour valider les chaînes de certificats. Java part du certificat du serveur et suit les émetteurs jusqu’à une AC racine du truststore. S’il n’y parvient pas, la connexion est interrompue. Cela touche aussi bien les clients HTTP que les pilotes JDBC, LDAP, Maven, Gradle ou Jenkins.
Les causes les plus fréquentes
- Le serveur n’envoie pas de certificat intermédiaire. Les navigateurs compensent souvent en téléchargeant les certificats manquants. Java ne le fait pas par défaut. D’où « ça marche dans le navigateur, pas en Java ».
- Une AC interne ou d’entreprise. Le serveur utilise un certificat d’une AC maison absente du truststore Java.
- Un proxy intercepte TLS. Les proxys d’entreprise comme Zscaler ou Netskope remplacent le certificat par le leur. Java ne connaît pas leur racine, le navigateur si, car l’IT l’a déployée dans le système.
- Un truststore personnalisé remplace cacerts. Avec
-Djavax.net.ssl.trustStore=…, Java utilise uniquement ce fichier. S’il n’y a pas les racines publiques, toutes les autres connexions échouent. - Un JDK ancien. Les très vieilles versions de Java ne connaissent pas les racines récentes.
Trouver la cause
Vérifie d’abord le serveur. La vérification SSL montre quels certificats le serveur livre et si la chaîne est complète. En ligne de commande :
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null
S’il n’y a qu’un certificat (depth=0) émis par une AC intermédiaire, la chaîne manque. Vérifie ensuite quel truststore Java utilise vraiment :
java -Djavax.net.debug=ssl:trustmanager -jar app.jar 2>&1 | grep -i "truststore"
La sortie indique le fichier, par exemple trustStore is: /usr/lib/jvm/java-21/lib/security/cacerts. Avec -Djavax.net.debug=ssl:handshake, tu vois aussi les certificats envoyés par le serveur.
Solution 1 : réparer la chaîne sur le serveur
Si le certificat intermédiaire manque, il doit être ajouté sur le serveur. C’est la seule solution qui aide tous les clients à la fois. Dans nginx, l’intermédiaire va dans le même fichier, après le certificat du serveur (fullchain.pem), de même dans SSLCertificateFile d’Apache. Le convertisseur de certificats complète automatiquement les intermédiaires manquants.
Solution 2 : ajouter l’AC au truststore
Avec une AC interne ou un proxy TLS, il faut faire connaître l’AC racine à Java. Prends l’AC racine ou intermédiaire, pas le certificat du serveur, sinon tout recasse au prochain changement de certificat.
# Java 9 et plus : directement dans le cacerts du JDK utilisé keytool -importcert -cacerts -alias racine-entreprise -file racine-entreprise.crt -storepass changeit -noprompt # truststore séparé pour une seule application keytool -importcert -keystore truststore.p12 -storetype PKCS12 -alias racine-entreprise -file racine-entreprise.crt java -Djavax.net.ssl.trustStore=truststore.p12 -Djavax.net.ssl.trustStorePassword=secret -jar app.jar
Attention au truststore séparé : il remplace complètement cacerts. Si l’application doit aussi joindre des serveurs publics, copie d’abord cacerts et ajoute l’AC dedans. Plus de détails dans l’article importer un certificat dans cacerts.
Sans keytool, dans le navigateur : dans la vérification SSL, clique sur « Ajouter au truststore Java ». La chaîne s’ouvre dans l’assistant keystore Java, où tu choisis l’AC et enregistres le truststore en JKS ou PKCS#12.
Solution 3 : utiliser le magasin du système
Si l’IT a déjà déployé l’AC d’entreprise dans le système d’exploitation, Java peut utiliser ce magasin directement :
- Windows :
-Djavax.net.ssl.trustStoreType=Windows-ROOT - macOS :
-Djavax.net.ssl.trustStore=NONE -Djavax.net.ssl.trustStoreType=KeychainStorelit le trousseau de l’utilisateur. - Debian, Ubuntu, RHEL : cacerts est de toute façon généré depuis le magasin système. Ajoute l’AC à celui-ci puis lance
update-ca-certificatesouupdate-ca-trust.
Ce qu’il ne faut pas faire
On trouve en ligne des bouts de code avec un TrustManager qui accepte tout, ou des options comme -Dmaven.wagon.http.ssl.insecure=true. La connexion est alors « chiffrée », mais n’importe qui sur le réseau peut se faire passer pour le serveur. Acceptable pour un test rapide, jamais en production.
FAQ
Pourquoi le site fonctionne-t-il dans le navigateur mais pas en Java ?
Les navigateurs téléchargent les intermédiaires manquants via l’adresse du certificat (AIA) et utilisent le magasin du système. Java ne fait ni l’un ni l’autre par défaut. En général, il manque un intermédiaire sur le serveur ou l’AC d’entreprise n’est que dans le système.
Java peut-il télécharger lui-même les intermédiaires manquants ?
Oui, avec -Dcom.sun.security.enableAIAcaIssuers=true. C’est un pis-aller : il faut un accès Internet à l’AC et la première connexion est plus lente. Mieux vaut réparer la chaîne sur le serveur.
Faut-il réimporter l’AC après une mise à jour de Java ?
Pour les certificats importés à la main dans le cacerts du JDK, souvent oui, car les mises à jour remplacent le fichier. Sous Linux avec le magasin système ou avec un truststore séparé hors du JDK, non.
Quelle différence avec « unable to get local issuer certificate » ?
C’est la même erreur vue par OpenSSL, curl ou Python. Causes et solutions sont identiques.