Comment envoyer une requête HTTPS via un proxy en Java?

J'essaie d'envoyer une requête à un serveur à l'aide de la HttpsUrlConnection classe. Le serveur a des problèmes de certificat, j'ai donc mis en place un TrustManager qui fait confiance à tout, ainsi qu'un nom d'hôte du vérificateur qui est aussi indulgents. Ce gestionnaire fonctionne très bien quand je fais ma demande directement, mais il ne semble pas être utilisés à tous, quand j'envoie la demande à travers un proxy.

- Je configurer 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" );

La TrustManager par défaut SSLSocketFactory est mis en place 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 j'exécute le code suivant, je me retrouve avec un SSLHandshakException ("l'hôte Distant a fermé la connexion lors de la poignée de main"):

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 que je suis absent de certains type de paramètre d'avoir à faire avec l'aide d'un proxy lorsque vous traitez avec SSL. Si je ne pas utiliser un proxy, mon checkServerTrusted méthode est appelée; c'est ce dont j'ai besoin pour arriver quand je suis passer par le proxy en tant que bien.

Je n'ai pas l'habitude de traiter avec Java et je n'ai pas beaucoup d'expérience avec HTTP/web trucs. Je crois que j'ai fourni tous les détails nécessaires pour comprendre ce que je suis en train de faire. Si ce n'est pas le cas, faites le moi savoir.

Mise à jour:

Après la lecture de l'article proposé par ZZ Codeur, 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 mis le SLLSocketFactory ici à la SSLTunnelSocketFactory (la classe expliqué dans l'article), les trucs que j'ai fait avec les TrustManager et la SSLContext est remplacée. Je n'ai pas encore besoin de ça?

Une Autre Mise À Jour:

J'ai modifié le SSLTunnelSocketFactory classe pour utiliser le SSLSocketFactory qui utilise mon TrustManager qui fait confiance à tout. Il ne semble pas que cela a fait une différence. C'est le createSocket méthode 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;
}

Lors de mes appels de code de connexion.connect, cette méthode est appelée, et l'appel à doTunnelHandshake est réussie. La prochaine ligne de code utilise mon SSLSocketFactory pour créer un SSLSocket; la toString valeur de résultat après cet appel est:

"1d49247[SSL_NULL_WITH_NULL_NULL: Socket[addr=/proxyHost,port=proxyPort,localport=24372]]".

Il n'en est rien pour moi, mais c'est peut être la raison pour laquelle les choses se décomposent après ce.

Lorsque le résultat.startHandshake() est appelée, le même createSocket méthode est appelée de nouveau à partir de, selon la pile d'appel, HttpsClient.afterConnect, avec les mêmes arguments, à l'exception de Socket s est null, et quand il vient à la suite.startHandshake() encore une fois, le résultat est le même SSLHandshakeException.

Suis-je manque encore un élément important à présent de plus en plus compliqué de puzzle?

C'est la trace de la pile:

javax.net.le protocole ssl.SSLHandshakeException: l'hôte Distant a fermé la connexion lors de la poignée de main 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:808) 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112) 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139) 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123) 
au gsauthentication.SSLTunnelSocketFactory.createSocket(SSLTunnelSocketFactory.java:106) 
au coucher du soleil.net.www.le protocole.https.HttpsClient.afterConnect(HttpsClient.java:391) 
au sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) 
au coucher du soleil.net.www.le protocole.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133) 
au gsauthentication.GSAuthentication.principale(GSAuthentication.java:52) 
Causés par: java.io.EOFException: SSL par les pairs fermé correctement 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.InputRecord.lire(InputRecord.java:333) 
au com.soleil.net.le protocole ssl.interne.le protocole ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:789) 
... 8 plus 
  • Avez-vous jamais vérifier si le proxy n'a fermer la connexion, c'est à dire le proxy a été intégrée à la validation de certificat?
  • Le message d'erreur dit "hôte Distant a fermé la connexion lors de la poignée de main". C'est que le proxy ou le serveur, je suis en train d'envoyer la demande? Je n'ai aucune idée à propos de la validation du certificat.
  • À partir d'un client de point de vue, je considère le proxy étant à distance de trop. Donc je m'assurerais de (au moins) d'exclure le proxy comme un point de défaillance. J'ai une certaine expérience avec la confiance des magasins et combien douloureux SSL peut être parfois. Mais je n'ai jamais vu une telle exception. Je n'ai jamais utilisé un proxy SSL, mais sur la première si j'avais assurez-vous que le proxy n'est pas de faire des dégâts à votre connexion.
  • Avez-vous déjà essayé de vous connecter à l'aide d'un navigateur? Si l'id ne fonctionne pas aussi bien, il doit être le proxy qui est en train de vous tromper.
  • La connexion via un navigateur fonctionne très bien (en plus des erreurs de certificat que j'ai à licencier), tout comme la connexion par programmation .NET. Le but est de faire automatisés de test de charge avec un cadre appelé Le Broyeur. Le Broyeur utilise Jython pour ses scripts de test, mais son HTTPRequest classe semble être assez limité, j'ai donc essayé de faire ce travail tout de Java premier.
  • Si je ne suis pas passer par le proxy, je reçois un UnknownHostException; le proxy est nécessaire, et non pas le problème, parce que la connexion via un navigateur ou avec C# est indolore..

InformationsquelleAutor Jeff Hillman | 2009-10-02