使用证书的 webservice 调用失败并出现套接字读取错误
webservice call with certificates fails with socket read error
我使用 Apache HttpClientBuilder 连接到服务器上的 REST
网络服务 运行。客户端必须发送证书进行验证,服务器证书将添加到客户端的信任库中。我用选项 -Djavax.net.ssl.keyStore
、 -Djavax.net.ssl.trustStore
指定。
但是我遇到以下错误,有什么线索吗?我不确定 SSL 握手是否成功,但日志中似乎有 ServerHelloDone
和 Found Certificate
。
代码
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet("https://testmachine.com/retrieve/path");
// add request header
request.addHeader(Constants.HTTP_HEADER_ACCEPT, Constants.MEDIA_TYPE_JSON);
// request to SERVER
HttpResponse response = client.execute(request);
日志
[write] MD5 and SHA1 hashes: len = 16
0000: 14 ........g...oJ..v...
Padded plaintext before ENCRYPTION: len = 32
0000: 14.......
0010: 5A.......
main, WRITE: TLSv1 Handshake, length = 32
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, SSL_RSA_WITH_RC4_128_MD5]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
Padded plaintext before ENCRYPTION: len = 18
0000: 02 .....@.y..A.
0010: 31 8F 1.
main, WRITE: TLSv1 Alert, length = 18
main, Exception sending alert: java.net.SocketException: Software caused connection abort: socket write error
main, called closeSocket()
main, called close()
main, called closeInternal(true)
Apr 18, 2016 1:52:17 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {s}->https://testmachine.com:443: Software caused connection abort: recv failed
Apr 18, 2016 1:52:17 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {s}->https://testmachine.com:443
Allow unsafe renegotiation: true
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
似乎 keystore
未从日志中读取。我怀疑这是因为我在 keystore
中的证书是“.p12”格式并且有自己的密码。所以我必须显式加载 keystore
和 truststore
并构建 SSLContext
以使其工作。
需要注意的一件重要事情是 privateKeyPassword
您必须提供 certificate
密码(而不是用于保护密钥输入的 keystore
密码)。
.loadKeyMaterial(keyStore, privateKeyPassword.toCharArray())
这是解释这一点的部分代码。
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream keyStream = new FileInputStream(keystoreFile);
try {
keyStore.load(keyStream, keystorePassword.toCharArray());
} catch (Exception ex) {
System.err.println("Failed to load keystore: " + ex.toString());
return;
} finally {
try {
keyStream.close();
} catch (Exception ignore) {
}
}
// load keystore and truststore
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(truststoreFile, truststorePassword.toCharArray(),
new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, privateKeyPassword.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
我使用 Apache HttpClientBuilder 连接到服务器上的 REST
网络服务 运行。客户端必须发送证书进行验证,服务器证书将添加到客户端的信任库中。我用选项 -Djavax.net.ssl.keyStore
、 -Djavax.net.ssl.trustStore
指定。
但是我遇到以下错误,有什么线索吗?我不确定 SSL 握手是否成功,但日志中似乎有 ServerHelloDone
和 Found Certificate
。
代码
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet("https://testmachine.com/retrieve/path");
// add request header
request.addHeader(Constants.HTTP_HEADER_ACCEPT, Constants.MEDIA_TYPE_JSON);
// request to SERVER
HttpResponse response = client.execute(request);
日志
[write] MD5 and SHA1 hashes: len = 16
0000: 14 ........g...oJ..v...
Padded plaintext before ENCRYPTION: len = 32
0000: 14.......
0010: 5A.......
main, WRITE: TLSv1 Handshake, length = 32
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, SSL_RSA_WITH_RC4_128_MD5]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
Padded plaintext before ENCRYPTION: len = 18
0000: 02 .....@.y..A.
0010: 31 8F 1.
main, WRITE: TLSv1 Alert, length = 18
main, Exception sending alert: java.net.SocketException: Software caused connection abort: socket write error
main, called closeSocket()
main, called close()
main, called closeInternal(true)
Apr 18, 2016 1:52:17 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {s}->https://testmachine.com:443: Software caused connection abort: recv failed
Apr 18, 2016 1:52:17 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {s}->https://testmachine.com:443
Allow unsafe renegotiation: true
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
似乎 keystore
未从日志中读取。我怀疑这是因为我在 keystore
中的证书是“.p12”格式并且有自己的密码。所以我必须显式加载 keystore
和 truststore
并构建 SSLContext
以使其工作。
需要注意的一件重要事情是 privateKeyPassword
您必须提供 certificate
密码(而不是用于保护密钥输入的 keystore
密码)。
.loadKeyMaterial(keyStore, privateKeyPassword.toCharArray())
这是解释这一点的部分代码。
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream keyStream = new FileInputStream(keystoreFile);
try {
keyStore.load(keyStream, keystorePassword.toCharArray());
} catch (Exception ex) {
System.err.println("Failed to load keystore: " + ex.toString());
return;
} finally {
try {
keyStream.close();
} catch (Exception ignore) {
}
}
// load keystore and truststore
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(truststoreFile, truststorePassword.toCharArray(),
new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, privateKeyPassword.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();