Kurz gesagt. PKIX path building failed: ... unable to find valid certification path to requested target bedeutet, dass die JVM vom Zertifikat des Servers keine Kette zu einem vertrauenswürdigen Root-Zertifikat in dem Trust Store bilden konnte, den sie tatsächlich geladen hat. Das ist meist $JAVA_HOME/lib/security/cacerts, nicht der Store des Betriebssystems. Die üblichen Ursachen sind eine private CA oder Proxy-CA, die nicht in dieser Datei steht, ein Server, der sein Zwischenzertifikat nicht sendet, eine JVM, die einen anderen Trust Store verwendet, als du denkst, ein altes JDK ohne ein neueres Root-Zertifikat und ein fehlender SNI-Name. Welche Ursache vorliegt, findest du mit openssl s_client -showcerts und -Djavax.net.debug=ssl:handshake:verbose heraus. Repariere dann den Trust Store oder den Server. Installiere niemals einen Trust Manager, der alles akzeptiert.
Was die Meldung bedeutet
Auf aktuellen JDKs sieht die vollständige Exception so aus. JDK 21 und 25 stellen den Namen des TLS-Alerts voran:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Die Bestandteile:
- PKIX ist das Modell für X.509-Zertifizierungspfade aus RFC 5280. Javas Implementierung steckt in
sun.security.provider.certpath. - Pfadaufbau (path building) ist der erste von zwei Schritten. Java versucht, irgendeine Folge von Zertifikaten vom Leaf-Zertifikat des Servers bis zu einem Vertrauensanker zu finden. Erst wenn das gelingt, prüft die Pfadvalidierung Gültigkeitszeitraum, Key Usage, Constraints und Widerruf. Fehler im zweiten Schritt haben eigene Meldungen, zum Beispiel PKIX path validation failed: validity check failed.
- „requested target“ ist das Zertifikat des Servers.
Frameworks verpacken diese Exception, zum Beispiel Spring in ResourceAccessException, Maven in „Could not transfer artifact“ oder Gradle in „Could not GET“. Der PKIX-Text steht trotzdem in der Kette der Ursachen, also suche im vollständigen Stacktrace danach.
Die fünf Ursachen
Alle fünf wurden mit eclipse-temurin:25.0.4.1_1-jdk-noble und einem kleinen Programm mit java.net.http.HttpClient nachgestellt. Die Test-PKI besteht aus einem Root-Zertifikat, einem Zwischenzertifikat („Issuing CA 1“) und einem Leaf-Zertifikat für api.example.test.
| # | Situation | Ergebnis |
|---|---|---|
| 1 | Private CA, unveränderte Datei cacerts des JDK | PKIX path building failed |
| 2 | Root-Zertifikat in eine Kopie von cacerts importiert, Server sendet Leaf- und Zwischenzertifikat | HTTP 200 |
| 3 | Derselbe Trust Store, Server sendet nur das Leaf-Zertifikat | PKIX path building failed |
| 4 | Wie 3, -Dcom.sun.security.enableAIAcaIssuers=true | PKIX path building failed |
| 4b | Wie 4, dazu -Dcom.sun.security.allowedAIALocations=http://aia.example.test:8080/ | HTTP 200 |
| 5 | Server wählt das Zertifikat per SNI, Client sendet SNI | HTTP 200 |
| 6 | Derselbe Server, -Djsse.enableSNIExtension=false | PKIX path building failed |
| 7 | Derselbe Server, URL mit IP-Adresse (kein SNI) | PKIX path building failed |
1. Das Zertifikat stammt von einer CA, der Java nicht vertraut
Das ist der häufigste Fall: interne Dienste, Kubernetes-Ingresses, Nexus oder Artifactory mit Firmenzertifikaten, Datenbanken und jede Website, die du über einen Proxy mit TLS-Inspektion aufrufst, der den Verkehr mit seinem eigenen Root-Zertifikat neu signiert. Installierst du dieses Root-Zertifikat unter Windows oder macOS, sind die Browser zufrieden, aber auf den meisten Plattformen liest Java den Store des Betriebssystems nicht. Die Datei cacerts des JDK (118 Einträge in diesem Temurin-25-Build) kennt „Example Corp Root CA“ nicht.
2. Der Server sendet sein Zwischenzertifikat nicht
Ein Server muss sein Leaf-Zertifikat und die Zwischenzertifikate bis zum Root-Zertifikat senden, ohne das Root-Zertifikat selbst. Viele Server sind nur mit dem Leaf-Zertifikat konfiguriert. Browser verdecken diesen Fehler oft, weil sie Zwischenzertifikate zwischenspeichern oder über die URL aus der Erweiterung Authority Information Access (AIA) des Zertifikats nachladen. Java tut standardmäßig beides nicht. Die Zeilen 3 und 4 der Tabelle zeigen genau diese Situation: Dem Root-Zertifikat wird vertraut, aber der Pfad lässt sich nicht aufbauen, weil Java „Issuing CA 1“ nie zu sehen bekommt.
Was ein Server sendet, siehst du mit zwei Zeilen:
$ openssl s_client -connect api.example.test:8443 -servername api.example.test -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9]+ s:|^ *i:'
0 s:CN = api.example.test
i:CN = Example Corp Issuing CA 1
1 s:CN = Example Corp Issuing CA 1
i:CN = Example Corp Root CA
$ openssl s_client -connect api.example.test:8444 -servername api.example.test -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9]+ s:|^ *i:'
0 s:CN = api.example.test
i:CN = Example Corp Issuing CA 1
Nennt die letzte Zeile i: etwas anderes als ein Root-Zertifikat, dem du vertraust, fehlt ein Zwischenzertifikat. Der Zertifikatsketten-Builder nimmt ein eingefügtes Bundle, bringt es in die richtige Reihenfolge, lädt fehlende Zwischenzertifikate über AIA nach und gibt dir genau die Kette aus, die der Server senden sollte.
3. Die JVM verwendet einen anderen Trust Store, als du denkst
Du hast das Root-Zertifikat importiert, und es scheitert trotzdem. Häufige Gründe:
- Ein anderes JDK. IDEs, Gradle-Toolchains, Maven-Wrapper, Anwendungsserver und Docker-Images bringen ihr eigenes JDK mit.
keytool -cacertsändert den Trust Store des JDK, unter demkeytoolläuft, und nur diesen. - Ein ersetzter Trust Store.
-Djavax.net.ssl.trustStore=only-corp.p12ersetztcacerts, statt die Datei zu ergänzen. Mit einem Store, der nur das Root-Zertifikat der Firma enthält, scheitert die Verbindung zu jeder öffentlichen Website:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
- Ein Tippfehler im Pfad des Trust Stores. Existiert die Datei aus
javax.net.ssl.trustStorenicht, meldet JDK 25 keinen Fehler. Es fällt stillschweigend aufcacertszurück, und dort ist dein Root-Zertifikat schlicht nicht enthalten. Nur die Debug-Ausgabe des Trust Managers verrät das:
Inaccessible trust store: /tmp/app-trustore.p12
trustStore is: /opt/java/openjdk/lib/security/cacerts
Fatal (CERTIFICATE_UNKNOWN): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target (
Ein Store, der existiert, aber keine vertrauenswürdigen Zertifikate enthält, führt zu einem anderen Fehler, nämlich the trustAnchors parameter must be non-empty.
- Ein Store auf Framework-Ebene. SSL-Bundles in Spring Boot, Tomcat-Connectors, Kafka und Datenbanktreiber haben eigene Trust-Store-Einstellungen, und für ihre Verbindungen haben diese Vorrang vor den Standardwerten der JVM.
Welche Datei geladen wurde, klärt die erste Zeile der JSSE-Debug-Ausgabe (siehe Diagnose).
4. Das JDK ist zu alt für das Root-Zertifikat des Servers
Jedes JDK-Update bringt eine Momentaufnahme der vertrauenswürdigen Root-Zertifikate mit. Endet die Kette einer öffentlichen Website bei einem neueren Root-Zertifikat, scheitert die Verbindung auf einem JDK, das älter ist als dieses Root-Zertifikat. Die Kompatibilitätsseite von Let's Encrypt nennt ein Beispiel: ISRG Root X2 gilt ab Java 8u401, 11.0.22, 17.0.10 und 21.0.2 als vertrauenswürdig. Entfernst du zwei Root-Zertifikate aus einer Kopie von cacerts, siehst du, was ein veralteter Store bewirkt:
--- letsencrypt.org (chains to ISRG roots)
javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
--- www.google.com (different CA)
HTTP 200
Scheitern nur Verbindungen zu manchen öffentlichen Websites, sieh dir das Root-Zertifikat an, bei dem deren Kette endet, und danach den Update-Stand deines JDK. Dasselbe passiert mit einem Trust Store, der vor Jahren aus cacerts kopiert und nie aktualisiert wurde.
5. Kein SNI, also sendet der Server ein anderes Zertifikat
Ein Server, der mehrere Namen bedient, wählt das Zertifikat anhand der Server Name Indication (SNI) im ClientHello. Ohne SNI sendet er ein Standardzertifikat, im Test ein selbstsigniertes Zertifikat für default.invalid. Dieses Zertifikat ist mit nichts verkettet, also lautet der Fehler wieder PKIX (Zeilen 6 und 7). Java sendet kein SNI, wenn:
- die URL eine IP-Adresse enthält (TLS verbietet IP-Literale in SNI),
-Djsse.enableSNIExtension=falsegesetzt ist, oft ein Überbleibsel eines alten Workarounds,- eine eigene
SSLSocketFactorySockets ohne Hostnamen erzeugt.
openssl s_client ohne -servername zeigt, was ein solcher Client erhält.
Diagnose Schritt für Schritt
1. Sieh dir die Kette an, die der Server sendet, möglichst von dem Rechner aus, auf dem der Fehler auftritt. Ein Proxy liefert diesem Rechner womöglich ein anderes Zertifikat:
openssl s_client -connect host.example:443 -servername host.example -showcerts </dev/null 2>/dev/null \
| grep -E '^ *[0-9]+ s:|^ *i:'
Läuft die Verbindung über einen Proxy mit TLS-Inspektion, ist der oberste Aussteller die CA des Proxys, keine öffentliche CA. Das allein erklärt den Fehler.
2. Frag Java, welchen Trust Store es geladen und was es empfangen hat. Die Debug-Ausgabe ist lang, in unserem Test etwa 5.000 Zeilen für einen einzigen fehlgeschlagenen Handshake, also filtere sie:
java -Djavax.net.debug=ssl:handshake:verbose -jar app.jar 2>&1 \
| grep -E 'trustStore is|Consuming server Certificate|"subject"|"issuer"|PKIX'
trustStore is: /opt/java/openjdk/lib/security/cacerts
...
javax.net.ssl ... Consuming server Certificate handshake message (
"issuer" : "CN=Example Corp Issuing CA 1",
"subject" : "CN=api.example.test",
...
"issuer" : "CN=Example Corp Root CA",
"subject" : "CN=Example Corp Issuing CA 1",
...
javax.net.ssl ... Fatal (CERTIFICATE_UNKNOWN): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target (
Die erste Zeile nennt die Datei des Trust Stores. Die Zertifikatsblöcke zeigen die Kette, die der Server gesendet hat, beginnend mit dem Leaf-Zertifikat. Der Aussteller des letzten Blocks ist das Zertifikat, das Java mit keinem Root-Zertifikat verbinden konnte. Ist dieser Aussteller ein Root- oder Zwischenzertifikat, das du für vertrauenswürdig gehalten hast, prüfe genau diese Datei:
$ keytool -list -cacerts -storepass changeit -alias corp-root
keytool error: java.lang.Exception: Alias <corp-root> does not exist
Für eine andere Datei verwende keytool -list -keystore /path/to/store.p12. -v gibt Subject und Fingerabdruck jedes Eintrags aus, sodass du das Zertifikat selbst abgleichen kannst statt nur den Alias.
3. Für Details zum Pfadaufbau füge -Djava.security.debug=certpath hinzu. Die Option protokolliert jeden möglichen Aussteller, den der Builder in Betracht gezogen hat, und bei AIA, ob ein Nachladen erlaubt war.
Lösungen, bei denen die Prüfung eingeschaltet bleibt
Repariere zuerst den Server, wenn ein Zwischenzertifikat fehlt. Konfiguriere die vollständige Kette (Leaf-Zertifikat, dann Zwischenzertifikate) in nginx, Apache, im Load Balancer oder im Ingress. Davon profitiert jeder Client, nicht nur Java.
Verwende einen anwendungseigenen Trust Store, der dein Root-Zertifikat ergänzt, wenn die CA privat ist oder ein Proxy den Verkehr neu signiert. Beginne mit einer Kopie von cacerts, damit öffentliche Websites weiter funktionieren:
cp "$JAVA_HOME/lib/security/cacerts" app-truststore.p12
keytool -importcert -noprompt -alias corp-root -file corp-root-ca.pem \
-keystore app-truststore.p12 -storepass changeit
java -Djavax.net.ssl.trustStore=app-truststore.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar
Für Tools, die du nicht selbst startest, etwa Maven, Gradle oder aus der IDE gestartete Tests, übergib dieselben Properties über JAVA_TOOL_OPTIONS oder die eigenen JVM-Optionen des Tools. keytool -importcert -cacerts ändert stattdessen den Standard-Store des JDK. Das erreicht jede Anwendung auf diesem JDK, verschwindet aber mit dem nächsten JDK-Upgrade.
Gib einem einzelnen Client einen eigenen Trust Store, wenn nur eine Integration das zusätzliche Root-Zertifikat braucht. Diese Variante lief gegen den privaten Dienst und eine öffentliche Website:
KeyStore ts = KeyStore.getInstance("PKCS12");
try (InputStream in = Files.newInputStream(Path.of("app-truststore.p12"))) {
ts.load(in, "changeit".toCharArray());
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
SSLContext ssl = SSLContext.getInstance("TLS");
ssl.init(null, tmf.getTrustManagers(), null);
HttpClient client = HttpClient.newBuilder().sslContext(ssl).build();
// https://api.example.test:8443/ -> HTTP 200
// https://letsencrypt.org/ -> HTTP 200
OkHttp, Apache HttpClient, Netty, gRPC und JDBC-Treiber akzeptieren alle einen auf dieselbe Weise gebauten SSLContext oder TrustManager.
In Containern importieren die Images von Eclipse Temurin beim Start zusätzliche CAs, wenn USE_SYSTEM_CA_CERTS gesetzt ist und Dateien *.crt unter /certificates/ eingehängt sind:
Adding certificate with alias Example Corp Root CA to the JVM truststore
Your keystore contains 240 entries
Das passiert nur über den Standard-Entrypoint des Images. Bei anderen Basis-Images führe keytool -importcert im Dockerfile aus.
Unter macOS mit JDK 23 oder neuer sorgt -Djavax.net.ssl.trustStoreType=KeychainStore-ROOT dafür, dass Java den Root-Schlüsselbund des Systems verwendet, sodass ein von deiner IT-Abteilung installiertes Root-Zertifikat als vertrauenswürdig gilt (JDK-8320362). Unter Windows erreicht -Djavax.net.ssl.trustStoreType=Windows-ROOT dasselbe mit dem Root-Store von Windows.
Aktualisiere das JDK, wenn ein öffentliches Root-Zertifikat fehlt. Patchst du cacerts auf einem alten JDK von Hand, fehlen ihm weiterhin alle übrigen Sicherheitskorrekturen.
Warum „allen Zertifikaten vertrauen“ keine Lösung ist
Die beliebteste Antwort im Netz ist ein X509TrustManager, dessen checkServerTrusted nichts tut, oft zusammen mit einem HostnameVerifier, der true zurückgibt. Damit verschwindet die Exception, weil TLS so zu einer Verschlüsselung wird, bei der niemand am anderen Ende geprüft ist. Jeder, der die Verbindung abfangen kann, kann ein beliebiges Zertifikat vorlegen, den Verkehr mitlesen und verändern: ein feindliches WLAN, ein kompromittierter Router, ein falsch konfigurierter Proxy, ein DNS-Hijacking. Außerdem lebt solcher Code meist länger als sein „vorübergehender“ Zweck und wird ins nächste Projekt kopiert. Jede der Lösungen oben dauert wenige Minuten und lässt die Prüfung eingeschaltet.
Dasselbe gilt dafür, das eigene Zertifikat des Servers als Vertrauensanker zu importieren. Das funktioniert bis zur nächsten Erneuerung, und die steht bei kürzeren Zertifikatslaufzeiten schon innerhalb weniger Wochen an. Vertraue der CA, die die Zertifikate ausstellt, nicht dem Zertifikat.
Checkliste
openssl s_client -showcertsvom betroffenen Host: Ist die Kette vollständig, und wer ist der oberste Aussteller?-Djavax.net.debug=ssl:handshake:verbose: Welche Datei hat die JVM lauttrustStore is:geladen?keytool -listfür genau diese Datei: Ist das ausstellende Root-Zertifikat enthalten?- Liegt ein Proxy mit TLS-Inspektion auf dem Weg? Dann brauchst du sein Root-Zertifikat.
- Wird SNI gesendet? Prüfe IP-Literale,
jsse.enableSNIExtensionund eigene Socket Factories. - Ist das JDK aktuell genug für das Root-Zertifikat der Website?
Quellen
- RFC 5280: Pfadvalidierung für Zertifikate (§6).
- Oracle, Release Notes zu JDK 21.0.10: Filterregeln für URIs in der Erweiterung Authority Info Access (
com.sun.security.allowedAIALocations). - Die mit JDK 25 ausgelieferte Datei
java.security($JAVA_HOME/conf/security/java.security), Abschnitt „X.509 AuthorityInfoAccess caIssuer URI Filtering“. - JDK-8320362: Vertrauensanker aus dem Keychain-Keystore laden (
KeychainStore-ROOT, JDK 23). - Let's Encrypt, Zertifikatskompatibilität.
- Container-Images von Eclipse Temurin: die Entrypoint-Option
USE_SYSTEM_CA_CERTS.
Alle Ergebnisse auf dieser Seite entstanden am 20. Oktober 2026 in eclipse-temurin:25.0.4.1_1-jdk-noble (OpenJDK 25.0.4.1, OpenSSL 3.0.13 für die Testserver), mit Testzertifikaten, die im Container erzeugt wurden.