Próbuję wysłać żądanie do serwera przy użyciu klasy HttpsUrlConnection. Serwer ma problemy z certyfikatami, więc ustawiam TrustManager, który ufa wszystkim, a także weryfikator nazwy hosta, który jest równie łagodny. Ten menedżer działa dobrze, gdy bezpośrednio wysyłam żądanie, ale wydaje się, że nie jest on w ogóle używany, gdy wysyłam żądanie za pośrednictwem serwera proxy.Jak wysłać żądanie HTTPS przez serwer proxy w Javie?
ustawić moje ustawienia serwera proxy tak:
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost", "proxyserver");
systemProperties.setProperty("http.proxyPort", "8080");
systemProperties.setProperty("https.proxyHost", "proxyserver");
systemProperties.setProperty("https.proxyPort", "8080");
TrustManager dla domyślnego SSLSocketFactory jest skonfigurowany tak:
SSLContext sslContext = SSLContext.getInstance("SSL");
// set up a TrustManager that trusts everything
sslContext.init(null, new TrustManager[]
{
new X509TrustManager()
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType)
{
// everything is trusted
}
public void checkServerTrusted(X509Certificate[] certs, String authType)
{
// everything is trusted
}
}
}, new SecureRandom());
// this doesn't seem to apply to connections through a proxy
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
// setup a hostname verifier that verifies everything
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String arg0, SSLSession arg1)
{
return true;
}
});
Jeśli uruchomić poniższy kod, ja skończyć z SSLHandshakException ("Zdalne host zamknięte połączenie podczas uzgadniania"):
URL url = new URL("https://someurl");
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", "0");
connection.connect();
Zakładam, że brakuje mi jakiegoś ustawienia związanego z używaniem proxy podczas pracy z SSL. Jeśli nie używam proxy, moja metoda checkServerTrusted zostanie wywołana; to jest to, co muszę zrobić, kiedy również przechodzę przez proxy.
Zazwyczaj nie mam do czynienia z Javą i nie mam dużego doświadczenia z materiałami HTTP/sieciowymi. Uważam, że dostarczyłem wszystkich szczegółów niezbędnych do zrozumienia tego, co próbuję zrobić. Jeśli tak nie jest, daj mi znać.
Aktualizacja:
Po przeczytaniu artykułu zaproponowaną przez ZZ Coder, zrobiłem następujące zmiany w kodzie gra:
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setSSLSocketFactory(new SSLTunnelSocketFactory(proxyHost, proxyPort));
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", "0");
connection.connect();
Wynik (SSLHandshakeException) jest taka sama. Kiedy ustawię SLLSocketFactory tutaj na SSLTunnelSocketFactory (klasa wyjaśniona w artykule), rzeczy zrobione z TrustManager i SSLContext zostaną nadpisane. Czy nadal tego nie potrzebuję?
Kolejna aktualizacja:
zmodyfikowałem SSLTunnelSocketFactory klasę używać SSLSocketFactory który używa mojego TrustManager że ufa wszystko. Nie wydaje się, żeby to miało jakikolwiek wpływ. Jest to metoda createSocket z SSLTunnelSocketFactory:
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException, UnknownHostException
{
Socket tunnel = new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, host, port);
SSLSocket result = (SSLSocket)dfactory.createSocket(
tunnel, host, port, autoClose);
result.addHandshakeCompletedListener(
new HandshakeCompletedListener()
{
public void handshakeCompleted(HandshakeCompletedEvent event)
{
System.out.println("Handshake finished!");
System.out.println(
"\t CipherSuite:" + event.getCipherSuite());
System.out.println(
"\t SessionId " + event.getSession());
System.out.println(
"\t PeerHost " + event.getSession().getPeerHost());
}
});
result.startHandshake();
return result;
}
Kiedy mój kod wywołuje connection.connect, metoda ta jest wywoływana, a wezwanie do doTunnelHandshake jest udany. Następna linia kodu używa mojego SSLSocketFactory do utworzenia SSLSocket; wartość toString rezultacie po tej rozmowy jest:
"1d49247 [SSL_NULL_WITH_NULL_NULL: gniazdo [addr =/proxyHost, port = proxyPort, localport = 24372]]".
Jest to dla mnie bez znaczenia, ale może to być przyczyną, dla której po tym nastąpi załamanie.
Kiedy wywoływana jest metoda result.startHandshake(), ta sama metoda createSocket jest wywoływana ponownie, zgodnie ze stosem wywołań, HttpsClient.afterConnect, z tymi samymi argumentami, z wyjątkiem Socket s ma wartość null, a gdy przychodzi do wyniku .startHandshake() ponownie, wynik jest taki sam SSLHandshakeException.
Czy nadal brakuje mi ważnej części tej coraz bardziej skomplikowanej układanki?
To jest ślad stosu:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:808) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123) at gsauthentication.SSLTunnelSocketFactory.createSocket(SSLTunnelSocketFactory.java:106) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:391) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133) at gsauthentication.GSAuthentication.main(GSAuthentication.java:52) Caused by: java.io.EOFException: SSL peer shut down incorrectly at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:333) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:789) ... 8 more
Czy kiedykolwiek sprawdzić, czy serwer proxy tak ścisły związek, czyli pełnomocnika ma wbudowaną walidację certyfikatu? – sfussenegger
Komunikat o błędzie "Zamknięte połączenie zdalnego hosta podczas uzgadniania". Czy to proxy, czy serwer, na który próbuję wysłać żądanie? Nie mam pojęcia o walidacji certyfikatu. –
Z punktu widzenia klienta uważam, że proxy też jest odległe. Dlatego powinienem (przynajmniej) wykluczyć proxy jako punkt awarii. Mam pewne doświadczenie z magazynami zaufania i jak bolesne może być czasami SSL. Ale nigdy nie widziałem takiego wyjątku. Nigdy nie korzystałem z serwera proxy z protokołem SSL, ale najpierw upewniłem się, że serwer proxy nie wyrządza żadnej szkody w twoim połączeniu. – sfussenegger