Java HTTPS POST Request

Guten Tag,

ich versuche gerade auf einen Spring Webservice welcher ein JSON POST body erwartet einen request zu senden. Leider gelingt es mir nicht. Ich erhalte jedes mal die gleiche Fehlermeldung:


java.net.SocketException: Unexpected end of file from server
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:792)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:789)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1535)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
	at Main.main(Main.java:74)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.uiDesigner.snapShooter.SnapShooter.main(SnapShooter.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Immer wenn ich etwas neues mit der Connection machen möchte wird die Exception geworfen. Wer kann mir weiterhelfen??? Wenn ich die Verbindung ohne https versuche klappt alles.


        ObjectMapper mapper = new ObjectMapper();

        Person person = new Person();
        person.setName("TEST");
        person.setVorname("Hans3");


        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            final X509Certificate cert1 = (X509Certificate) certificateFactory.generateCertificate(Main.class.getResourceAsStream("domain-CA.crt"));
            TrustManager[] trustManager = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                            if (!x509Certificates[0].equals(cert1)) {
                                throw new CertificateException();
                            }
                        }

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

            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManager, new SecureRandom());

            String jsonPerson = mapper.writeValueAsString(person);
            URL url = new URL("https://phonebook.domain.de/add");
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();

            connection.setConnectTimeout(1000);
            connection.setRequestMethod("POST");
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setUseCaches(false);
            connection.setRequestProperty("Content-Type",
                    "application/json");
            connection.setRequestProperty("Content-Length", String.valueOf(jsonPerson.length()));
            OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
            writer.write(jsonPerson);
            writer.close();
            connection.connect();
            if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));

                for (String line; (line = reader.readLine()) != null; ) {
                    System.out.println(line);
                }


                reader.close();
            }

            connection.disconnect();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }```

unexpected EOF ? Noch nie gesehen den Fehler.
“ohne SSL gehts” - dann würde ich stark vermuten dass entweder was mit dem Zertifikat nicht stimmt oder wie dieses verwendet wird (ggf ohne Passphrase versucht zu laden ?).
Auch wichtig : wenn dann bitte Code passende zur Exception posten. Da um die main() noch einiges fehlt wissen wir nicht welche Zeile es im gepostetem Abschnitt entspricht.
Auch sieht der Stack sehr merkwürdig aus für eine IDE. Schon echt wired dass die IDE lieber reflections nutzt statt den Code in einer eigenen VM zu starten. Dadurch kann es sein dass noch eine ältere Version der Klasse im Speicher liegt. Würde hier mal ne andere IDE oder komplett ohne direkt vom Terminal aus testen.
Auch interessant : wie siehts mit Logs auf Server-Seite aus ? Normalerweise gibts auf beiden Seiten eine Fehlermeldung. Vielleicht wird man ja aus dem Server-Log schlauer warum dieser die Verbindung vorzeitig schließt.
Vielleicht liegts auch nur an einer falschen Längen-Berechnung. Würde anstatt dem Writer mal einen PrintStream versuchen oder direkt raw auf dem OutputStream arbeiten. Könnte ein Charset-conflict sein.
Vielleicht fehlt auch nur ein CRLF am Ende. Vielleicht ist auch das Format falsch.

Alles in allem recht viele Möglichkeiten die sich ohne weiteren Code und Logs nur raten lässt.

btw : ich hab in einem anderen Thread noch ne andere Variante gepostet wie man Fremd-CAs nutzen kann anstatt gleich brutal einen neuen TrustManager zu nutzen, vielleicht hilft es ja : http://forum.byte-welt.net/java-forum-erste-hilfe-vom-java-welt-kompetenz-zentrum/netzwerkprogrammierung/15233-java-und-startssl.html

Hi,

danke für den Tipp so funktioniert zumindest der Get Request. Beim POST wird immer noch der Selbe Fehler geworfen.

In den Server Logs wird aber auch kein Log erstellt. Warum weiß ich nicht. Der Webservice den ich laufen lasse läuft mit Spring und wird über eine Jar gestartet. Der Port lautet 10033 ist aber von außen nicht erreichbar, so dass ich ein Apache Weiterleitung an den Port 10033 habe. Dies funktioniert bei all meinen Diensten. Die Verbindung ist verschlüsselt bis zum Apache und von dort geht es dann unverschlüsselt an den Dienst (Über das lokale netz)

Apache Weiterleitung Plesk:

ProxyPass         /  http://127.0.0.1:10033/
ProxyPassReverse  /  http://127.0.0.1:10033/
ProxyRequests     Off
<Proxy http://127.0.0.1:8081/*>
	Order deny,allow
	Allow from all
</Proxy>

Kann es sein, dass Java die Länge des HTTP-POST-BODYs an die verschlüsselte länge anpasst und der Apache auf meinem Server diese länge nach dem entschlüsseln nicht mehr zurücksetzt?

Der Tipp mit dem Beitrag war auf jedenfalls schon mal hilfreich. Danke

edit:

Person.java

import java.util.ArrayList;
import java.util.List;

@Entity
public class Person {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;


    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Nummer> nummern = new ArrayList<>();
    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Anschrift> anschriften = new ArrayList<Anschrift>();
    private String name;
    private String land;
    private String vorname;
    private String titel;
    private String geburtsdatum;

    public List<Nummer> getNummern() {
        return nummern;
    }

    public void setNummer(Nummer nummern) {
        nummern.setPerson(this);
        this.nummern.add(nummern);
    }

    private List<Anschrift> getAnschriften() {
        return anschriften;
    }

    private void setAnschrift(Anschrift anschriften) {
        anschriften.setPerson(this);
    }

    public String getVorname() {
        return vorname;
    }

    public void setVorname(String vorname) {
        this.vorname = vorname;
    }

    public String getTitel() {
        return titel;
    }

    public void setTitel(String titel) {
        this.titel = titel;
    }

    public String getGeburtsdatum() {
        return geburtsdatum;
    }

    public void setGeburtsdatum(String geburtsdatum) {
        this.geburtsdatum = geburtsdatum;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLand() {
        return land;
    }

    public void setLand(String land) {
        this.land = land;
    }

    @Override
    public String toString() {
        return "id= " + id + ",vorname= " + vorname + ",name= " + name + ", land= " + land + ",geburtsdatum= " + geburtsdatum;
    }
}

Main.java


import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

public class Main {
    public static void main(String[] args) throws Exception {

        ObjectMapper mapper = new ObjectMapper();

        Person person = new Person();
        person.setName("TEST");
        person.setVorname("Hans3");


        String jsonPerson = mapper.writeValueAsString(person);
        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        X509Certificate x509cert = (X509Certificate) factory.generateCertificate(Thread.currentThread().getContextClassLoader().getResourceAsStream("ca.crt"));

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // laut Java-Doc wird, wenn nicht anders definiert, "jks" geliefert, könnte man also auch hard-coden
        keyStore.load(null, null);
        keyStore.setCertificateEntry("StartSSL", x509cert); // Alias-Name kann frei gewählt werden

        TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("X509");
        tmFactory.init(keyStore);

        SSLContext context = SSLContext.getInstance("TLSv1.2"); // ggf. nur getInstance("TLS"); kommt auf den Server an
        context.init(null, tmFactory.getTrustManagers(), null); // erster Parameter KeyManager nur für Client-Auth wichtig / wenn SecureRandom == null wird (wie bei allen Crypto-Klassen) der "höchstwertigste" installierte Provider genutzt
        SSLSocketFactory socketFactory = context.getSocketFactory();

        URL url = new URL("http://phonebook.codeheap.de/add");

        HttpURLConnection con = (HttpURLConnection) url.openConnection();
//        con.setSSLSocketFactory(socketFactory);
        con.setConnectTimeout(1000);
        con.setRequestMethod("POST");
        con.setDoInput(true);
        con.setDoOutput(true);
        con.setUseCaches(false);
        con.setRequestProperty("Content-Type",
                "application/json");
        con.setRequestProperty("Content-Length", String.valueOf(jsonPerson.length()));
        OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
        writer.write(jsonPerson);
        writer.close();
        con.connect();
        if (con.getResponseCode() == HttpsURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(con.getInputStream()));

            for (String line; (line = reader.readLine()) != null; ) {
                System.out.println(line);
            }


            reader.close();
        }

    }
}


PS ist ein Studenten-Projekt wenn es nicht geht nicht so schlimm. Ist hauptsächlich aus eigenem Interesse.

Mit Spring kenn ich mich leider nicht aus, da kann ich nicht weiter helfen. Das es aber keine Logs gibt wundert ziemlich und lässt darauf vermuten das diese irgendwo verschluckt werden.
Versuchs doch mal direkt ohne den Umweg über den Indianer und vergleiche mit Wireshark (dort kann man auch irgendwie SSL-Keys laden um so auch in eine HTTPS-Verbindung reingucken zu können, weis nur grad nicht wie) zwischen HTTP und HTTPS ob es unterschiede gibt.
Weiter gibst du an das es bei GET funzt, bei POST aber zu nem Fehler kommt. Passt aber nicht wirklich zur Fehlermeldung. Denn die Exception deines Client sagt ja dass der Server unerwartet die Verbindung killt, und dass schon direkt beim Header. Zu diesem Zeitpunkt gibt es also noch keinen Payload. Spontan würde mir die Idee kommen einen kleinen MITM-Proxy zu basteln der halt zeigt wo der Stream abbricht. Da der Response-Code immer die erste Zeile ist und es dabei knallt heißt dass das entweder schon dort direkt das EOF kommt, dann kann also der Header nicht gebaut werden > Fehler in der Server-Software und sollte auch zu nem Log-Eintrag oder ggf. gleich zu nem kompletten Crash führen … oder er ist nicht vollständig (weil z.B. irgendwo NULL steht).

Alles sehr wage, vielleicht auch Fehler im Framework > andere Version versuchen.
Aber dass es keine Logs gibt würde ich auf jeden an den Dev melden und mir daher ggf einen anderen App-Server suchen der korrekt loggt. Ich les meine Server-Logs zwar auch nicht und sie müllen mir auf dem Root nur die Platte voll, aber für sowas wenn man mal gucken will warum was nich geht braucht man sie doch.
ggf auch mal Server-Code zeigen. Dein Bean kann ja nicht alles sein.

Du sagst, du hast Logs auf dem Server gesucht, aber keine gefunden. Hast du in deinem Dienst gesucht, oder im Apache? Wenn es ein SSL Problem ist, und die SSL Verbindung terminiert am Apache, wirst du bei dem suchen müssen.