HTTP handshake_failure

darkwall

Aktives Mitglied
Hi,
ich habe eine Frage: In meinem Programm will ich eine Datei auf meinem Server auslesen. Es ist eine einfache Textdatei.
Den Server kann ich ohne und mit SSL anfragen, würde aber gerne nur per SSL das machen.

Im Programm selbst (Java 18) mache ich das so:
Java:
URL url = new URL("https://someserver.com");
URLConnection conn = url.openConnection();
Es geht um folgende Exception:
Received fatal alert: handshake_failure
Ansich auch klar und ich habe schon viele Artikel darüber gelesen, mit Zertifikaten und so.
Was mich wundert ist, dass das keine Rolle spielt, wenn ich das aus Netbeans 14 heraus starte. Dann klappt alles wie gewohnt. Erstelle ich aber ein z.B. Appimage daraus, erhalte ich diese Exception.
Der Server nutzt ein Zertifikat von Let's encrypt.

Wie kann ich das auch im Appimage ohne Exception machen lassen?

Vielen Dank!
dw
 

LimDul

Top Contributor
ich mach mal was Glaskugel lesen und hoffe es hilft dir.

Die Ursache dürfte sein, dass er das Zertifikat nicht validieren kann. Sprich, dass er nicht überprüfen, ob es ein gültiges Zertifikat ist. Dazu wird die Zertifikats-Chain nach oben gegangen bis man ein Zertifikat findet, den man vertraut (In der Regel das Root-Zertifikat).

Die Liste der Zertifikate, denen man vertraut ist "irgendwo" abgelegt. Das ist - soweit ich weiß - in Java im Java Keystore abgelegt. Und welche da drin sind, hängt ganz massiv vom verwendeten JDK (bzw. ggf. JRE sofern noch eine alte Java Version verwendet wird). Das heißt, was helfen könnte auf ein aktuelleres JDK beim zusammenbauen umzustellen. Bzw. generell mal zu prüfen, wo zieht Netbeans die Liste her bzw. welches JDK ist da drin und was im Appimage.
 

Oneixee5

Top Contributor
Es gibt 2 Möglichkeiten. Du kannst den SSLContext von HttpsURLConnection so steuern, dass einfach alles akzeptiert wird, was TLS-verschlüsselt ist. Das ist natürlich nicht so sicher.
Java:
final TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllManager() };
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new AllHosts());
Die andere Möglichkeit ist, du übergibst dem SSLContext einen TrustManager mit KeyStore, welcher das/die Zertifikate enthält, welchen du vertraust. Das ist natürlich etwas komplexer. Anleitungen findest du direkt bei Oracle oder im Netz. Das Ganze ist natürlich sicherheitsrelevant und du solltest nicht einfach Quellcode kopieren, wenn du nicht zu 100% verstehst was da passiert.
 

mihe7

Top Contributor
Könnte auch die TLS-Version sein; hatte ich vor einiger Zeit mal, dass beim Kunden das JDK aktualisiert wurde. Dumm nur, dass in den neueren Versionen standardmäßig TLSv1.0 und TLSv1.1 abgeschaltet sind.

Wenn es daran liegen sollte: die Datei heißt java.security, dort muss man die betreffende TLS-Version einfach unter dem Schlüssel jdk.tls.disabledAlgorithms entfernen. Die Datei selbst findet man unter conf/security im Java 11+-Home-Verzeichnis (falls es jemanden für Java 8 interessiert: dort liegt die Datei unter jre/lib/security)
 

darkwall

Aktives Mitglied
Könnte auch die TLS-Version sein; hatte ich vor einiger Zeit mal, dass beim Kunden das JDK aktualisiert wurde. Dumm nur, dass in den neueren Versionen standardmäßig TLSv1.0 und TLSv1.1 abgeschaltet sind.

Wenn es daran liegen sollte: die Datei heißt java.security, dort muss man die betreffende TLS-Version einfach unter dem Schlüssel jdk.tls.disabledAlgorithms entfernen. Die Datei selbst findet man unter conf/security im Java 11+-Home-Verzeichnis (falls es jemanden für Java 8 interessiert: dort liegt die Datei unter jre/lib/security)
Mein Server kann TLSv1.2, daran sollte es dann nicht liegen.
 

darkwall

Aktives Mitglied
Es gibt 2 Möglichkeiten. Du kannst den SSLContext von HttpsURLConnection so steuern, dass einfach alles akzeptiert wird, was TLS-verschlüsselt ist. Das ist natürlich nicht so sicher.
Java:
final TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllManager() };
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new AllHosts());
Die andere Möglichkeit ist, du übergibst dem SSLContext einen TrustManager mit KeyStore, welcher das/die Zertifikate enthält, welchen du vertraust. Das ist natürlich etwas komplexer. Anleitungen findest du direkt bei Oracle oder im Netz. Das Ganze ist natürlich sicherheitsrelevant und du solltest nicht einfach Quellcode kopieren, wenn du nicht zu 100% verstehst was da passiert.

Klappt immer noch nicht. Ich habe es jetzt so probiert:
Test-Methode:
Java:
public static void get(String uri) throws Exception {
        System.out.println("get...");
        final TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllManager()};
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        HostnameVerifier dummyHostVerifier = (String hostname, SSLSession session) -> true;
        sslContext.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(dummyHostVerifier);

        var properties = System.getProperties();
        properties.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

        HttpClient client = HttpClient.newBuilder()
                .sslContext(sslContext)
                .build();
        HttpRequest request = HttpRequest.newBuilder(URI.create(uri))
                .build();

        HttpResponse<Stream<String>> response = client.send(request, BodyHandlers.ofLines());

        for (String item : response.body().collect(toList())) {
            System.out.println(item);
        }
    }
Der TrustAllManager sieht so aus:
Code:
public class TrustAllManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] cert, String authType) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] cert, String authType) {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

Es ist wie gesagt ein Java-Module mit JDK18. Ich baue das AppImage so:
jpackage --type app-image --app-version 1 --input SSL_TestProject/dist/ --dest SSL_TestProject/TEST_RELEASE/Test -m mainmod/ssltest.TestClass --module-path SSL_TestProject/build/modules --jlink-options --strip-debug --verbose
Das klappt auch, starte ich die App aber, kommt nach wie vor die Exception:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at java.net.http/jdk.internal.net.http.HttpClientImpl.send(Unknown Source)
at java.net.http/jdk.internal.net.http.HttpClientFacade.send(Unknown Source)
at mainmod/ssltest.TestClass.get(Unknown Source)
at mainmod/ssltest.TestClass.main(Unknown Source)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Unknown Source)
at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)
at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.decode(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.readRecord(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)
at java.base/javax.net.ssl.SSLEngine.unwrap(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SSLFlowDelegate$Reader.unwrapBuffer(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SSLFlowDelegate$Reader.processData(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SSLFlowDelegate$Reader$ReaderDownstreamPusher.run(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(Unknown Source)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Starte ich das mit meinem installierten JDK klappt alles.
 

mihe7

Top Contributor
Vermutlich wird das Modul aus Sicht des Basismoduls nicht benötigt und man könnte theoretisch auch eine andere Crypto-Implementierung wählen.
 

Ähnliche Java Themen

Neue Themen


Oben