如何发送一个经过Java中的代理服务器的HTTPS请求?(How do a send an HTTP

2019-07-20 15:41发布

我想发送给使用HttpsURLConnection的类服务器的请求。 该服务器证书的问题,所以我成立了一个的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" );

默认的SSLSocketFactory的的TrustManager设置是这样的:

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 /网络的东西太多经验。 我相信我已经提供了所有必要的细节,以了解我试图做的。 如果不是这种情况,让我知道。

更新:

通过读取编码器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做的东西被覆盖。 难道我还需要什么?

另一个更新:

我修改了SSLTunnelSocketFactory类使用该使用我的TrustManager一个信任一切的SSLSocketFactory。 它不会出现,这已经作出任何区别。 这是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()被调用时,相同的createSocket方法是从再次调用,根据调用堆栈,HttpsClient.afterConnect,使用相同的参数,除了插座S为空,并且当它涉及到周围result.startHandshake()再次,结果是一样的出现SSLHandshakeException。

我仍然缺少一个重要的一块这个日益复杂的难题?

这是堆栈跟踪:

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

Answer 1:

HTTPS代理是没有意义的,因为你不能终止在代理出于安全原因,您的HTTP连接。 有了您的信任策略,如果代理服务器有一个HTTPS端口它可能工作。 你的错误是由连接到HTTP代理端口使用HTTPS引起的。

您可以通过使用SSL隧道代理连接使用代理服务器连接命令(许多人称之为代理)。 然而,Java不支持代理隧道的新版本。 在这种情况下,你需要自己处理的隧道。 你可以在这里找到示例代码,

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

编辑:如果你想打败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;
    }
 }  );

编辑2:我只是想我的节目,我几年前写的使用SSLTunnelSocketFactory,它并不能工作。 显然,Sun推出了新的错误有时在Java 5中看到这个错误报告,

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

好消息是,SSL隧道的错误是固定的,所以你可以只使用默认的出厂。 我只是想通过代理,一切都按预期工作。 见我的代码,

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

这是我所得到的,当我运行该程序,

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

正如你所看到的,的SSLContext和的HostnameVerifier越来越调用。 的HostnameVerifier仅涉及当主机名不匹配的证书。 我用“www.verisign.net”来触发此。



Answer 2:

尝试,而不是试图推出自己的Apache的百科全书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();
  }
  */


文章来源: How do a send an HTTPS request through a proxy in Java?