2009-10-02 16 views
26

HttpsUrlConnectionクラスを使用してサーバーに要求を送信しようとしています。サーバーには証明書の問題があるので、すべてを信頼するTrustManagerと同様に寛容なホスト名ベリファイアを設定します。このマネージャーは私の要求を直接行うとうまく動作しますが、プロキシ経由で要求を送信するときにはまったく使用されていないようです。Javaのプロキシ経由でHTTPS要求を送信するにはどうすればよいですか?

は、私はこのような私のプロキシ設定を設定します。

デフォルトのSSLSocketFactoryのためのTrustManagerは、このように設定されて
Properties systemProperties = System.getProperties(); 
systemProperties.setProperty("http.proxyHost", "proxyserver"); 
systemProperties.setProperty("http.proxyPort", "8080"); 
systemProperties.setProperty("https.proxyHost", "proxyserver"); 
systemProperties.setProperty("https.proxyPort", "8080"); 

:私は、次のコードを実行した場合

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; 
    } 
}); 

が、私は終わりますSSLHandshakException( "ハンドシェイク中のリモートホストクローズ接続"):

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(); 

私は、SSLを扱う際にプロキシを使用することと何らかの関係がないことを想定しています。プロキシを使用しない場合、私のcheckServerTrustedメソッドが呼び出されます。これは私がプロキシを通って行くときに起こる必要があるものです。

私は通常Javaを扱っていませんし、HTTP/Webのものにはあまり経験がありません。私は自分がしようとしていることを理解するのに必要なすべての詳細を提供していると信じています。そうでない場合は、教えてください。

更新:

ZZコーダによって提案記事を読んだ後、私は、接続コードを次のように変更製:

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(); 

の結果(SSLHandshakeException)は同じです。ここでSLLSocketFactoryをSSLTunnelSocketFactory(この記事で説明したクラス)に設定すると、TrustManagerとSSLContextで行った処理がオーバーライドされます。それでも私はそれを必要としないのですか?

別の更新:

私はすべてを信頼し、私のTrustManagerを使用するのSSLSocketFactoryを使用するようにSSLTunnelSocketFactoryクラスを変更しました。これは何の違いも生じていないようです。これはSSLTunnelSocketFactoryのcreateSocketメソッドです:

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; 
} 

私のコードはconnection.connectを呼び出すと、このメソッドが呼び出され、doTunnelHandshakeへの呼び出しが成功しています。次のコード行では、SSLSocketFactoryを使用してSSLSocketを作成します。この呼び出し後の結果のtoString値である:

"1d49247 [SSL_NULL_WITH_NULL_NULL:ソケット[ADDR =/proxyHost、ポート= proxyPortの、次に、localport = 24372]"

これは私には意味がありませんが、これが後に崩壊する理由かもしれません。

result.startHandshake()が呼び出されると、呼び出しスタックHttpsClient.afterConnectから同じ引数を使用して、同じcreateSocketメソッドが再度呼び出されます。ただし、Socketがnullである場合を除き、result .startHandshake()を再度実行すると、結果は同じSSLHandshakeExceptionになります。

私はまだますます複雑化しているこのパズルに重要な部分がありませんか?

これは、スタックトレースです:あなたはセキュリティ上の理由から、プロキシであなたのHTTP接続を終了することはできませんので

 
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 
+0

あなたは今までにプロキシが接続を閉じましたかどうかを確認しました、プロキシ、すなわち証明書の検証が組み込まれていますか? – sfussenegger

+0

「リモートホストがハンドシェイク中に接続をクローズしました」というエラーメッセージが表示されます。それはプロキシですか、または私がリクエストを送信しようとしているサーバーですか?私は証明書の検証について知らない。 –

+0

クライアントの観点から見ると、私はプロキシもリモートであると考えています。したがって、私は(少なくとも)失敗のポイントとしてプロキシを除外することを確認したいと思います。私は信託店で何か経験があり、時にSSLが苦しいこともあります。しかし、私はそのような例外を見たことはありません。 SSLでプロキシを使用したことはありませんでしたが、最初はプロキシが接続に何の悪影響を及ぼしていないことを確認しましたが、 – sfussenegger

答えて

28

HTTPSプロキシは意味がありません。信頼ポリシーを使用すると、プロキシサーバーにHTTPSポートがある場合に機能します。 HTTPSを使用してHTTPプロキシポートに接続すると、エラーが発生します。

プロキシCONNECTコマンドを使用して、SSLトンネリング(多くの人がそのプロキシと呼んでいる)を使用してプロキシ経由で接続できます。ただし、Javaではプロキシトンネリングの新しいバージョンはサポートされていません。その場合、自分でトンネリングを処理する必要があります。あなたが、ここ

http://www.javaworld.com/javaworld/javatips/jw-javatip111.html

EDITをサンプルコードを見つけることができます:あなたはJSSEのすべてのセキュリティ対策を倒したい場合は、まだ自分自身のTrustManagerを必要としています。このような何か、

public SSLTunnelSocketFactory(String proxyhost, String proxyport){ 
     tunnelHost = proxyhost; 
     tunnelPort = Integer.parseInt(proxyport); 
     dfactory = (SSLSocketFactory)sslContext.getSocketFactory(); 
} 

... 

connection.setSSLSocketFactory(new SSLTunnelSocketFactory(proxyHost, proxyPort)); 
connection.setDefaultHostnameVerifier(new HostnameVerifier() 
{ 
    public boolean verify(String arg0, SSLSession arg1) 
    { 
     return true; 
    } 
} ); 

EDIT 2:私はちょうどSSLTunnelSocketFactoryを使用して、私は数年前に書いた私のプログラムを試してみましたが、それはどちらか動作しません。どうやら、Sunは

http://bugs.sun.com/view_bug.do?bug_id=6614957

良いニュースは、あなただけのデフォルトのファクトリを使用できるように、SSLトンネリングのバグが修正されていることである、いつかこのバグレポートを参照してくださいするJava 5の新しいバグを導入しました。私はちょうどプロキシを試して、すべてが期待どおりに動作します。あなたが見ることができるように

checkServerTrusted ============= 
hostnameVerifier ============= 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
...... 

は、SSLContextのとのHostnameVerifierの両方が呼ばなっている、これは私がプログラムを実行すると、私が得るものである、

public class SSLContextTest { 

    public static void main(String[] args) { 

     System.setProperty("https.proxyHost", "proxy.xxx.com"); 
     System.setProperty("https.proxyPort", "8888"); 

     try { 

      SSLContext sslContext = SSLContext.getInstance("SSL"); 

      // set up a TrustManager that trusts everything 
      sslContext.init(null, new TrustManager[] { new X509TrustManager() { 
       public X509Certificate[] getAcceptedIssuers() { 
        System.out.println("getAcceptedIssuers ============="); 
        return null; 
       } 

       public void checkClientTrusted(X509Certificate[] certs, 
         String authType) { 
        System.out.println("checkClientTrusted ============="); 
       } 

       public void checkServerTrusted(X509Certificate[] certs, 
         String authType) { 
        System.out.println("checkServerTrusted ============="); 
       } 
      } }, new SecureRandom()); 

      HttpsURLConnection.setDefaultSSLSocketFactory(
        sslContext.getSocketFactory()); 

      HttpsURLConnection 
        .setDefaultHostnameVerifier(new HostnameVerifier() { 
         public boolean verify(String arg0, SSLSession arg1) { 
          System.out.println("hostnameVerifier ============="); 
          return true; 
         } 
        }); 

      URL url = new URL("https://www.verisign.net"); 
      URLConnection conn = url.openConnection(); 
      BufferedReader reader = 
       new BufferedReader(new InputStreamReader(conn.getInputStream())); 
      String line; 
      while ((line = reader.readLine()) != null) { 
       System.out.println(line); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

を自分のコードを参照してください。 HostnameVerifierは、ホスト名がcertと一致しない場合にのみ関与します。私はこれを引き起こすために "www.verisign.net"を使いました。

+0

この記事で説明したSSLTunnelSocketFactoryクラスを使用すると、前に入れたデフォルトのTrustManagerの設定が上書きされます。それでも私はそれが必要ですか? –

+0

信頼はブラウザと最終サーバーの間です。それはトンネルとは関係がありません。あなたはまだそれが必要です。 –

+0

SSLTunnelSocketFactoryクラスでそれを使用できる方法はありません。私はどちらか一方しか使うことができないようです。 –

0

代わりに独自のロールしようとしているのはApache Commonsのは、HttpClientをライブラリ試してみてください:自分のサンプルコードから http://hc.apache.org/httpclient-3.x/index.html

を:

HttpClient httpclient = new HttpClient(); 
    httpclient.getHostConfiguration().setProxy("myproxyhost", 8080); 

    /* Optional if authentication is required. 
    httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost", 
    new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password")); 
    */ 

    PostMethod post = new PostMethod("https://someurl"); 
    NameValuePair[] data = { 
    new NameValuePair("user", "joe"), 
    new NameValuePair("password", "bloggs") 
    }; 
    post.setRequestBody(data); 
    // execute method and handle any error responses. 
    // ... 
    InputStream in = post.getResponseBodyAsStream(); 
    // handle response. 


    /* Example for a GET reqeust 
    GetMethod httpget = new GetMethod("https://someurl"); 
    try { 
    httpclient.executeMethod(httpget); 
    System.out.println(httpget.getStatusLine()); 
    } finally { 
    httpget.releaseConnection(); 
    } 
    */ 
+0

私の友人は先週私にこれを勧めました。私はそれをダウンロードしましたが、まだ試してみる機会がありませんでした。私は明日の朝初めてそれを試してみるでしょう。 –

関連する問題