【发布时间】:2010-12-03 10:59:05
【问题描述】:
我正在尝试使用 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/Web 的经验。我相信我已经提供了所有必要的细节来理解我正在尝试做的事情。如果不是这种情况,请告诉我。
更新:
看了ZZ Coder建议的文章后,我对连接代码做了如下修改:
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 类以使用 SSLSocketFactory,它使用信任所有内容的 TrustManager。这似乎没有任何区别。这是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: Socket[addr=/proxyHost,port=proxyPort,localport=24372]]”。
这对我来说毫无意义,但这可能是之后事情崩溃的原因。
调用 result.startHandshake() 时,根据调用堆栈 HttpsClient.afterConnect 再次调用相同的 createSocket 方法,具有相同的参数,除了 Socket s 为空,并且当它到达 result 时。再次startHandshake(),结果还是一样的SSLHandshakeException。
在这个日益复杂的难题中,我是否仍然缺少重要的一块?
这是堆栈跟踪:
javax.net.ssl.SSLHandshakeException:握手期间远程主机关闭连接 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:808) 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112) 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139) 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123) 在 gsauthentication.SSLTunnelSocketFactory.createSocket(SSLTunnelSocketFactory.java:106) 在 sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:391) 在 sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) 在 sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133) 在 gsauthentication.GSAuthentication.main(GSAuthentication.java:52) 原因:java.io.EOFException:SSL 对等体错误关闭 在 com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:333) 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:789) ... 8 更多【问题讨论】:
-
你有没有检查过代理是否关闭了连接,即代理是否有内置的证书验证?
-
错误消息显示“握手期间远程主机关闭连接”。那是代理,还是我试图将请求发送到的服务器?我不知道证书验证。
-
从客户的角度来看,我认为代理也是远程的。因此,我会确保(至少)将代理排除在故障点之外。我对信任存储有一些经验,并且有时 SSL 会带来多大的痛苦。但我从未见过这样的例外。我从未使用 SSL 代理,但首先我会确保代理不会对您的连接造成任何损害。
-
您是否尝试过使用浏览器进行连接?如果 id 在那里也不起作用,那一定是代理在欺骗你。
-
通过浏览器连接工作得很好(除了我必须消除的证书错误),以编程方式连接.NET也是如此。目标是使用名为 The Grinder 的框架进行自动负载测试。 Grinder 使用 Jython 作为其测试脚本,但它的 HTTPRequest 类似乎相当有限,所以我一直在尝试首先在 Java 中解决这个问题。
标签: java proxy sslhandshakeexception