Sto tentando di inviare una richiesta a un server utilizzando la classe HttpsUrlConnection. Il server ha problemi con i certificati, quindi ho configurato un TrustManager che si fida di tutto, così come un verificatore del nome host altrettanto indulgente. Questo gestore funziona perfettamente quando faccio la mia richiesta direttamente, ma non sembra essere usato affatto quando invio la richiesta tramite un proxy.Come inviare una richiesta HTTPS attraverso un proxy in Java?
ho impostato la mia impostazioni proxy in questo modo:
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost", "proxyserver");
systemProperties.setProperty("http.proxyPort", "8080");
systemProperties.setProperty("https.proxyHost", "proxyserver");
systemProperties.setProperty("https.proxyPort", "8080");
Il TrustManager per la SSLSocketFactory default è impostato in questo modo:
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;
}
});
Se corro il seguente codice, io alla fine con un SSLHandshakException ("Connessione remota dell'host chiuso durante l'handshake"):
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();
Presumo che mi manca un qualche tipo di impostazione che ha a che fare con l'utilizzo di un proxy quando si tratta di SSL. Se non utilizzo un proxy, viene chiamato il mio metodo checkServerTrusted; questo è quello che devo succedere quando sto passando attraverso il proxy.
Di solito non mi occupo di Java e non ho molta esperienza con materiale HTTP/web. Credo di aver fornito tutti i dettagli necessari per capire cosa sto cercando di fare. Se questo non è il caso, fammi sapere.
Aggiornamento:
Dopo aver letto l'articolo suggerito da ZZ Coder, ho fatto le seguenti modifiche al codice di connessione:
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();
Il risultato (SSLHandshakeException) è lo stesso. Quando imposto qui SLLSocketFactory su SSLTunnelSocketFactory (la classe spiegata nell'articolo), le cose che ho fatto con TrustManager e SSLContext sono sovrascritte. Non ne ho ancora bisogno?
Un altro aggiornamento:
ho modificato la classe SSLTunnelSocketFactory di utilizzare lo SSLSocketFactory che usa il mio TrustManager che si fida di tutto. Non sembra che questo abbia fatto alcuna differenza. Questo è il metodo di createSocket 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;
}
Quando il mio codice chiama connection.connect, questo metodo viene chiamato, e la chiamata a doTunnelHandshake è successo. La prossima riga di codice usa il mio SSLSocketFactory per creare un SSLSocket; il valore di toString risultato dopo questa chiamata è:
"1d49247 [SSL_NULL_WITH_NULL_NULL: Socket [addr =/proxyHost, port = proxyPort, localport = 24372]]".
Questo non ha senso per me, ma potrebbe essere la ragione per cui le cose si rompono dopo questo.
Quando viene chiamato result.startHandshake(), viene richiamato lo stesso metodo createSocket, in base allo stack di chiamate, HttpsClient.afterConnect, con gli stessi argomenti, tranne Socket s è null e quando viene visualizzato il risultato .startHandshake() di nuovo, il risultato è la stessa SSLHandshakeException.
Mi manca ancora un pezzo importante per questo puzzle sempre più complicato?
Questa è l'analisi dello stack:
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
Ti è mai verificare se il proxy ha fatto chiudere la connessione, vale a dire il proxy ha una convalida del certificato integrata? – sfussenegger
Il messaggio di errore dice "Connessione remota host chiuso durante l'handshake". È il proxy o il server a cui sto tentando di inviare la richiesta? Non ho idea della convalida del certificato. –
Dal punto di vista dei clienti, considererei il proxy remoto. Pertanto, mi assicurerei di (almeno) escludere il proxy come punto di errore. Ho una certa esperienza con i negozi di fiducia e su quanto possa essere talvolta doloroso SSL. Ma non ho mai visto un'eccezione del genere. Non ho mai usato un proxy con SSL, ma all'inizio dovrei assicurarmi che il proxy non stia facendo alcun danno alla tua connessione. – sfussenegger