J'essaie d'envoyer une requête à un serveur utilisant la classe HttpsUrlConnection. Le serveur a des problèmes de certificat, donc j'ai mis en place un TrustManager qui fait confiance à tout, ainsi qu'un vérificateur de nom d'hôte qui est également indulgent. Ce gestionnaire fonctionne très bien lorsque je fais ma demande directement, mais il ne semble pas être utilisé du tout lorsque j'envoie la requête via un proxy.Comment envoyer une requête HTTPS via un proxy en Java?
Je mis mes paramètres de proxy comme ceci:
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost", "proxyserver");
systemProperties.setProperty("http.proxyPort", "8080");
systemProperties.setProperty("https.proxyHost", "proxyserver");
systemProperties.setProperty("https.proxyPort", "8080");
Le TrustManager pour le SSLSocketFactory par défaut est configuré comme ceci:
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 je lance le code suivant, je me retrouve avec un SSLHandshakException ("Connexion à distance de l'hôte distant pendant la négociation"):
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();
Je suppose qu'il me manque un certain type de paramètre ayant à voir avec l'utilisation d'un proxy lors du traitement SSL. Si je n'utilise pas de proxy, ma méthode checkServerTrusted est appelée; C'est ce que je dois faire quand je passe par le proxy aussi.
Je n'ai pas l'habitude de traiter avec Java et je n'ai pas beaucoup d'expérience avec les trucs HTTP/web. Je crois avoir fourni tous les détails nécessaires pour comprendre ce que j'essaie de faire. Si ce n'est pas le cas, faites le moi savoir.
Mise à jour:
Après avoir lu l'article proposé par ZZ Coder, j'ai fait les modifications suivantes au code de connexion:
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();
Le résultat (SSLHandshakeException) est le même. Lorsque j'ai défini SLLSocketFactory ici à SSLTunnelSocketFactory (la classe expliquée dans l'article), les choses que j'ai faites avec le TrustManager et le SSLContext sont remplacées. N'ai-je pas encore besoin de ça?
Une autre mise à jour:
J'ai modifié la classe SSLTunnelSocketFactory d'utiliser le SSLSocketFactory qui utilise mon TrustManager qui fait confiance tout. Il ne semble pas que cela ait fait une différence. Ceci est la méthode createSocket de 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;
}
Quand mon code appelle connection.connect, cette méthode est appelée, et l'appel à doTunnelHandshake est réussie. La ligne de code suivante utilise mon SSLSocketFactory pour créer un SSLSocket; la valeur toString du résultat après cet appel est:
"1d49247 [SSL_NULL_WITH_NULL_NULL: Socket [adr =/proxyHost, port = proxyPort, localport = 24372]]".
Cela n'a pas de sens pour moi, mais c'est peut-être la raison pour laquelle les choses se dégradent après cela. Lorsque result.startHandshake() est appelé, la même méthode createSocket est appelée à partir de, en fonction de la pile d'appels, HttpsClient.afterConnect, avec les mêmes arguments, sauf que Socket s est nul, et quand il s'agit de résultat .startHandshake() à nouveau, le résultat est le même SSLHandshakeException.Est-ce qu'il me manque encore une pièce importante pour ce puzzle de plus en plus compliqué?
C'est la trace de la pile:
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
Avez-vous vérifié jamais si le proxy a fermer la connexion, à savoir le proxy a une validation de certificat intégrée? – sfussenegger
Le message d'erreur indique «Connexion de l'hôte distant fermé pendant l'établissement de liaison». Est-ce le proxy ou le serveur auquel j'essaye d'envoyer la requête? Je n'ai aucune idée de la validation du certificat. –
Du point de vue des clients, je considère que le proxy est également distant. Par conséquent, je m'assurerais de (au moins) exclure le proxy comme point de défaillance. J'ai une certaine expérience avec les magasins de confiance et à quel point SSL peut être douloureux parfois. Mais je n'ai jamais vu une telle exception. Je n'ai jamais utilisé de proxy avec SSL, mais d'abord, je m'assurerais que le proxy ne nuise pas à votre connexion. – sfussenegger