Estoy tratando de enviar una solicitud a un servidor utilizando la clase HttpsUrlConnection. El servidor tiene problemas de certificado, por lo que configuro un TrustManager que confía en todo, así como un verificador de nombre de host que es igualmente indulgente. Este administrador funciona bien cuando realizo mi solicitud directamente, pero no parece usarse en absoluto cuando envío la solicitud a través de un proxy.¿Cómo se envía una solicitud HTTPS a través de un proxy en Java?
puse mi configuración del proxy de esta manera:
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost", "proxyserver");
systemProperties.setProperty("http.proxyPort", "8080");
systemProperties.setProperty("https.proxyHost", "proxyserver");
systemProperties.setProperty("https.proxyPort", "8080");
El TrustManager para la SSLSocketFactory por defecto está configurado de esta manera:
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;
}
});
Si funciono el siguiente código, que terminan con una SSLHandshakException ("Conexión remota del host remoto durante el protocolo de enlace"):
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();
Supongo que me falta algún tipo de ajuste que tenga que ver con el uso de un proxy cuando se trata de SSL. Si no uso un proxy, se llama a mi método checkServerTrusted; esto es lo que necesito que pase cuando estoy pasando por el proxy también.
Normalmente no trato con Java y no tengo mucha experiencia con cosas HTTP/web. Creo que he proporcionado todos los detalles necesarios para entender lo que estoy tratando de hacer. Si este no es el caso, házmelo saber.
Actualización:
Después de leer el artículo propuesto por ZZ Coder, he hecho los siguientes cambios en el código de conexión:
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();
El resultado (SSLHandshakeException) es el mismo. Cuando configuro SLLSocketFactory aquí en SSLTunnelSocketFactory (la clase explicada en el artículo), las cosas que hice con TrustManager y SSLContext quedan anuladas. ¿Todavía no necesito eso?
Otro Actualización:
he modificado la clase SSLTunnelSocketFactory utilizar el SSLSocketFactory que utiliza mi TrustManager que confía todo. No parece que esto haya hecho ninguna diferencia. Este es el método de 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;
}
Cuando mi código llama connection.connect, se llama este método, y el llamado a doTunnelHandshake tiene éxito. La siguiente línea de código usa mi SSLSocketFactory para crear un SSLSocket; el valor toString de resultado después de esta llamada es:
"1d49247 [SSL_NULL_WITH_NULL_NULL: Socket [addr =/proxyHost, port = proxyPort, localport = 24372]]".
Esto no tiene sentido para mí, pero podría ser la razón por la cual las cosas se descomponen después de esto.
Cuando se llama a result.startHandshake(), se llama al mismo método createSocket de nuevo, de acuerdo con la pila de llamadas, HttpsClient.afterConnect, con los mismos argumentos, excepto que Socket s es nulo, y cuando se produce el resultado .startHandshake() de nuevo, el resultado es la misma SSLHandshakeException.
¿Todavía me falta una pieza importante en este rompecabezas cada vez más complicado?
Este es el seguimiento de la pila:
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
¿Alguna vez comprobar si el proxy se cerró la conexión, es decir, el proxy tiene validación de certificado incorporada? – sfussenegger
El mensaje de error dice "Conexión remota del host remoto durante el protocolo de enlace". ¿Es ese el proxy o el servidor al que intento enviar la solicitud? No tengo idea de la validación del certificado. –
Desde el punto de vista del cliente, consideraría que el proxy también es remoto. Por lo tanto, me aseguraré de (al menos) excluir el proxy como un punto de falla. Tengo cierta experiencia con tiendas de confianza y cuán doloroso puede ser SSL a veces. Pero nunca he visto una excepción. Nunca utilicé un proxy con SSL, pero al principio me aseguré de que el proxy no perjudicara tu conexión. – sfussenegger