java.net.SocketTimeoutException:读取失败请求后读取超时

时间:2014-04-03 17:32:55

标签: java ssl

在我的程序中,我需要对两个不同的服务器执行安全(双向SSL)请求。目前,当我尝试连接server1时,我遇到异常javax.net.ssl.SSLHandshakeException。最奇怪的是,在server2请求失败后,我无法收到来自server1的回复。我得到例外java.net.SocketTimeoutException。如果未server1的请求未执行server2,则会成功返回数据。

堆栈追踪:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
    at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
    at sun.security.ssl.Handshaker.processLoop(Unknown Source)
    at sun.security.ssl.Handshaker.process_record(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 14 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 20 more

Exception in thread "main": java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at sun.security.ssl.InputRecord.readFully(Unknown Source)
    at sun.security.ssl.InputRecord.read(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
    at sun.security.ssl.AppInputStream.read(Unknown Source)
    at java.io.BufferedInputStream.fill(Unknown Source)
    at java.io.BufferedInputStream.read1(Unknown Source)
    at java.io.BufferedInputStream.read(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
    ...

server1的请求代码:

URL url = ...
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-type", "application/soap+xml; charset=UTF-8");
connection.setConnectTimeout(300000);
connection.setReadTimeout(300000);
connection.setDoInput(true);
connection.setDoOutput(true);
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream is = new FileInputStream(new File(...));
try {
    keyStore.load(is, "password".toCharArray());
    keyManagerFactory.init(keyStore, "password".toCharArray());
} finally {
    is.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
is = new FileInputStream(new File(...));
try {
    keyStore.load(is, "password".toCharArray());
    trustManagerFactory.init(keyStore);
} finally {
    is.close();
}
sslContext.init(keyManagerFactory.getKeyManagers(),
    trustManagerFactory.getTrustManagers(),new SecureRandom());
httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String s, SSLSession sslSession) {
        return true;
    }
});
try {
    OutputStream os = connection.getOutputStream();
    try {
        String s = ...;
        os.write(s.getBytes());
    } finally {
        os.close();
    }
    System.out.println("Response code: " + connection.getResponseCode());
} finally {
    connection.disconnect();
}

server2的请求代码:

SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream is = new FileInputStream(new File(...));
try {
    keyStore.load(is, "password".toCharArray());
    keyManagerFactory.init(localKeyStore, "password".toCharArray());
} finally {
    is.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyStore = KeyStore.getInstance("JKS");
is = new FileInputStream(new File(...));
try {
    keyStore.load(is, "password".toCharArray());
    trustManagerFactory.init(keyStore);
} finally {
    is.close();
}
sslContext.init(keyManagerFactory.getKeyManagers(),
    trustManagerFactory.getTrustManagers(), new SecureRandom());
URL url = ...
URLConnection connection = url.openConnection();
((HttpsURLConnection) connection).setSSLSocketFactory(sslContext.getSocketFactory());
connection.setReadTimeout(40000);
connection.setConnectTimeout(40000);
connection.setDoInput(true);
connection.setDoOutput(false);
int respCode = ((HttpURLConnection) connection).getResponseCode();
if (respCode != 200)
    throw new RuntimeException(...);
BufferedInputStream bufferedIs = new BufferedInputStream(connection.getInputStream());
try {
    byte[] buf = new byte[512];
    int j;
    while ((j = bufferedIs.read(buf)) != -1) {
        ...
} finally {
    bufferedIs.close();
}

我很感激你的帮助。

1 个答案:

答案 0 :(得分:0)

我在代码片段中遗漏了一条非常重要的内容:System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true")。该属性在请求server1之后和server2请求之前设置,如果其值为false请求server2失败,但主题异常。在探索了OpenJDK的来源之后,我找到了具有静态和最终字段com.sun.net.ssl.internal.ssl.Handshaker的类boolean allowUnsafeRenegotiation,该字段使用以下方法调用进行初始化:Debug.getBooleanProperty("sun.security.ssl.allowUnsafeRenegotiation", false)

所以在我的情况下,首次请求属性sun.security.ssl.allowUnsafeRenegotiation初始化为默认值(false)后,任何后续尝试更改该属性都没有效果。