Я пытаюсь отправить запрос на сервер, используя класс 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" );
TrustManager для SSLSocketFactory по умолчанию настроен следующим образом:
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-материалами. Я считаю, что я представил все детали, необходимые для понимания того, что я пытаюсь сделать. Если это не так, дайте мне знать.
Update:
После прочтения статьи, предложенной 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, который доверяет всем. Кажется, это не имеет никакого значения. Это метод createSocket для 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;
}
Когда мой код вызывает connection.connect, этот метод вызывается, и вызов doTunnelHandshake выполняется успешно. Следующая строка кода использует мой SSLSocketFactory для создания SSLSocket; значение toString результата после этого вызова:
"1d49247 [SSL_NULL_WITH_NULL_NULL: Socket [addr =/proxyHost, port = proxyPort, localport = 24372]]" .
Это бессмысленно для меня, но это может быть причиной того, что после этого сломаются.
Когда вызывается result.startHandshake(), тот же метод createSocket вызывается снова, согласно стеку вызовов, HttpsClient.afterConnect, с теми же аргументами, за исключением того, что Socket s имеет значение null, и когда это происходит, чтобы получить результат. 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