Hallo da draußen,

gerade musste ich wieder einige Zeit lang schwitzen, weil bei meinem Cutworks-Projekt nach einer (vergessenen) SSL-Zertifikatserneuerung plötzlich eine essentielle Funktion ausgefallen und mit einer IOException/SSL Handshake-Exception den Dienst verweigerte.
Nach einigem Suchen, habe ich zumindest die Stelle gefunden, an der das ganze passiert.

Kurz als Hintergrund: In dem Shop wird eine Fremdkomponente über ein iframe integriert. Dieses iframe stellt eine Komponente bereit, mit der man Dinge auf einfache Weise konstruieren kann, sozusagen so eine Art Mini-CAD. Ist der Benutzer fertig wird durch einen Button-Druck außerhalb des iframe ein Server-Aufruf generiert, der Modell-Daten der externen Komponente abruft und entsprechend in interne Datenstrukturen umwandelt. Dabei erfolgt ein Aufruf einer externen URL direkt im Java-Server-Code über https.
Genau dieser Server-Call warf nach Umstellung des Zertifikats eine Fehlermeldung und zwar (gekürzt):

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Der Aufruf der URL erfolgt im großen und ganzen mit Bordmitteln von Java, über URL, HttpURLConnection und entsprechenden Streams, die dann abgefangen und umgewandelt werden. Hier mal ein entsprechendes Code-Snippet dazu:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
 
public class TestAbruf {
 
 public static void main(String[] args) {
    String data = null;
    try {
       URL url = new URL(args[0]);
       HttpURLConnection connection = (HttpURLConnection) url.openConnection();
       connection.setRequestMethod("GET");
       connection.connect();
       InputStream is = connection.getInputStream();
       BufferedReader reader = new BufferedReader(new InputStreamReader(is));
       data = IOUtils.toString(reader);
       System.out.println(data);
    }
    catch  (IOException e) {
       e.printStackTrace();
    }
 }
}

Ich habe den Code etwas abgewandelt, damit man das ganze relativ einfach direkt über ein Mini-Tool aufrufen kann – dieser gibt dann einfach den Inhalt aus, der über das Get geliefert wird. Soweit, so gut. Im Shop werden die Daten dann entsprechend weiter verarbeitet, der Inhalt ist eigentlich ein XML, dass dann geparst und in ein entsprechendes Modell umgewandelt wird.
Am Ende tut das aber nichts zur Sache. Bis heute morgen hat das ganze noch funktioniert, bis gegen Mittag das SSL-Zertifikat des Servers ungültig wurde und ich eine Aktualisierung des Zertifikats auf dem entsprechenden Server eingespielt habe.
Obwohl ich das entsprechend gemacht hatte, lief die Methode immer in die Exception hinein.

Ein Aufruf der entsprechenden URL im Browser funktioniert aber – also musste das irgendetwas java-seitiges sein, dass da kaputt ist. Nach einigem Überlegen und Web-Suche kam ich auf den Trichter, dass Java möglicherweise die Zertifikate entsprechend selbst verwaltet und das (neue) Zertifikat dann (noch) nicht kennt.
Die Suche ergab dann, dass es tatsächlich im JRE-Verzeichnis des Servers (bzw. des entsprechenden Rechners) im Unterverzeichnis lib\security den Keystore cacerts gibt, in dem Java seine “trusted” Zertifikate hinterlegt.
Das neue Zertifikat vom Server muss dort hinterlegt werden, erst dann akzeptiert Java eine URL-Verbindung zu dem entsprechenden Server.
Hier mal die Anleitung, wie ihr das entsprechend durchführt:

  1. ihr benötigt die Zertifikatsdatei (cer/pem), die ihr entweder direkt bei der Erstellung des Zertifikats oder über Euren Browser unter den Zertifikatsinformationen herunterladen könnt.
  2. öffnet eine Kommandozeile als Administrator (bzw. root-shell unter Linux oder macOS)
  3. wechselt in das Verzeichnis des JDK / JRE auf eurem Rechner / Server
    1. cd /D C:\Program Files\AdoptOpenJDK\jdk-8.0.232.09-hotspot\jre\lib\security

      In meinem Fall habe ich eine AdoptOpenJDK 8 installiert, welches in dem oben genannten Verzeichnis installiert ist. Wechselt entsprechend Eurer Umgebung in das richtige Verzeichnis. Wichtig hierbei ist, wenn ihr ein JDK habt, dass ihr in den Unterordner jre\lib\security wechselt und nicht direkt in den lib-Ordner des JDK.

  4. mit dem Keytool (im bin-Ordner der Java-Installation) könnt ihr dann das entsprechende Zertifikat importieren:
    keytool -keystore cacerts -import -alias mein_server -file C:\Verzeichnis\ssl\meinserver_ssl_certificate.cer

    Hinter alias könnt ihr einen beliebigen Namen einsetzen, über den ihr Euer Zertifikat finden könnt. C:\Verzeichnis\ssl\meinserver_ssl_certificate.cer ersetzt ihr entsprechend durch den Dateipfad der entsprechenden Zertifikatsdatei.

  5. Bei Abschicken des keytool-Kommandos wird ein Kennwort verlangt. Solltet ihr wie ich auch ein AdoptOpenJDK installiert haben, lautet das Kennwort standardmäßig: changeit

Sobald diese Schritte erfolgt sind, müsst ihr, wenn ihr das ganze z.B. wie ich aus dem Tomcat oder einem anderen java-basierten Application-Server aufruft, den Server neu starten, nur so werden die Änderungen aktiv und der Aufruf funktioniert wieder.

Ich bin mal gespannt, ob ich das nächstes Jahr wieder machen muss, oder ob das dann besser funktioniert.
In dem Sinne, bis bald.

Schreibe einen Kommentar

Artikel, die Dir auch gefallen könnten

Free Your IT endlich online

Hallo da draußen, endlich sind meine ersten beiden Tutorial-Videos inkl. eines kleinen Vorstellungs-Videos online. Da ich grundsätzlich weg möchte von den zensurbehafteten Plattformen habe ich

mehr...