Die Fehlermeldung
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
PKIX ist das Verfahren, mit dem Java Zertifikatsketten prüft. Java nimmt das Zertifikat des Servers und sucht über die Aussteller eine lückenlose Kette bis zu einer Root-CA im Truststore. Findet es keine, bricht die Verbindung ab. Das passiert bei HTTP-Clients, JDBC-Treibern, LDAP, Maven, Gradle oder Jenkins gleichermaßen.
Die häufigsten Ursachen
- Der Server schickt kein Zwischenzertifikat. Browser gleichen das oft aus, indem sie fehlende Zertifikate nachladen. Java tut das standardmäßig nicht. Deshalb geht es im Browser und nicht in Java.
- Eine interne oder Firmen-CA. Der Server nutzt ein Zertifikat einer eigenen CA, die nicht im Truststore von Java liegt.
- Ein Proxy bricht TLS auf. Firmen-Proxys wie Zscaler oder Netskope ersetzen das Zertifikat durch ein eigenes. Java kennt deren Root nicht, der Browser schon, weil die IT sie im Betriebssystem verteilt hat.
- Ein eigener Truststore ersetzt cacerts. Mit
-Djavax.net.ssl.trustStore=…nutzt Java nur diese Datei. Fehlen dort die öffentlichen Roots, schlägt jede andere Verbindung fehl. - Ein altes JDK. Sehr alte Java-Versionen kennen neuere Roots nicht.
Ursache finden
Prüfe zuerst den Server. Der SSL-Check zeigt, welche Zertifikate der Server ausliefert und ob die Kette vollständig ist. Auf der Kommandozeile:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null
Steht dort nur ein Zertifikat (depth=0) und der Aussteller ist eine Zwischen-CA, fehlt die Kette. Danach prüfst du, welchen Truststore Java wirklich benutzt:
java -Djavax.net.debug=ssl:trustmanager -jar app.jar 2>&1 | grep -i "truststore"
Die Ausgabe nennt die Datei, zum Beispiel trustStore is: /usr/lib/jvm/java-21/lib/security/cacerts. Mit -Djavax.net.debug=ssl:handshake siehst du zusätzlich, welche Zertifikate der Server geschickt hat.
Lösung 1: Die Kette am Server reparieren
Fehlt das Zwischenzertifikat, gehört es auf den Server. Das ist die einzige Lösung, die für alle Clients gleichzeitig hilft. Bei nginx gehört das Zwischenzertifikat in dieselbe Datei hinter das Serverzertifikat (fullchain.pem), bei Apache ebenso in SSLCertificateFile. Der Zertifikat-Konverter ergänzt fehlende Zwischenzertifikate automatisch.
Lösung 2: Die CA in den Truststore aufnehmen
Bei internen CAs oder einem TLS-Proxy musst du Java die Root-CA bekannt machen. Nimm die Root- oder Zwischen-CA, nicht das Serverzertifikat selbst. Sonst bricht es beim nächsten Zertifikatswechsel wieder.
# Java 9 und neuer: direkt in den cacerts des laufenden JDK keytool -importcert -cacerts -alias firmen-root -file firmen-root.crt -storepass changeit -noprompt # eigener Truststore nur für eine Anwendung keytool -importcert -keystore truststore.p12 -storetype PKCS12 -alias firmen-root -file firmen-root.crt java -Djavax.net.ssl.trustStore=truststore.p12 -Djavax.net.ssl.trustStorePassword=geheim -jar app.jar
Achtung beim eigenen Truststore: Er ersetzt cacerts komplett. Soll die Anwendung auch öffentliche Server erreichen, kopier zuerst cacerts und füge die CA dort hinzu. Mehr dazu im Artikel Zertifikat in cacerts importieren.
Ohne keytool geht es im Browser: Im SSL-Check auf „In Java-Truststore übernehmen“ klicken. Die Kette landet im Java-Keystore-Helfer, dort wählst du die CA aus und speicherst den Truststore als JKS oder PKCS#12.
Lösung 3: Den Zertifikatsspeicher des Systems nutzen
Hat die IT die Firmen-CA schon im Betriebssystem verteilt, kann Java diesen Speicher direkt nutzen:
- Windows:
-Djavax.net.ssl.trustStoreType=Windows-ROOT - macOS:
-Djavax.net.ssl.trustStore=NONE -Djavax.net.ssl.trustStoreType=KeychainStoreliest den Schlüsselbund des Benutzers. - Debian, Ubuntu, RHEL: Hier ist cacerts ohnehin aus dem Systemspeicher erzeugt. CA dort hinzufügen und
update-ca-certificatesbzw.update-ca-trustausführen.
Was du nicht tun solltest
Im Netz kursieren Code-Schnipsel mit einem TrustManager, der alles akzeptiert, oder Optionen wie -Dmaven.wagon.http.ssl.insecure=true. Damit ist die Verbindung zwar „verschlüsselt“, aber jeder im Netz kann sich als der Server ausgeben. Das ist nur für einen kurzen Test vertretbar, nie für den Betrieb.
FAQ
Warum funktioniert die Seite im Browser, aber nicht in Java?
Browser laden fehlende Zwischenzertifikate über die Adresse im Zertifikat nach (AIA) und nutzen den Zertifikatsspeicher des Systems. Java macht beides standardmäßig nicht. Meist fehlt also ein Zwischenzertifikat am Server oder die Firmen-CA liegt nur im System.
Kann Java fehlende Zwischenzertifikate selbst nachladen?
Ja, mit -Dcom.sun.security.enableAIAcaIssuers=true. Das ist eine Notlösung: Sie braucht Internetzugang zur CA und verlangsamt den ersten Verbindungsaufbau. Besser die Kette am Server reparieren.
Muss ich nach einem Java-Update die CA erneut importieren?
Bei manuell importierten Zertifikaten im cacerts des JDK oft ja, weil Updates die Datei ersetzen. Unter Linux mit dem Systemspeicher oder mit einem eigenen Truststore außerhalb des JDK passiert das nicht.
Was ist der Unterschied zu „unable to get local issuer certificate“?
Es ist derselbe Fehler aus Sicht von OpenSSL, curl oder Python. Die Ursachen und Lösungen sind die gleichen.