我是SSL/TLS方面的新手,但最近我尝试从我的Java客户端向某些第三方服务器发送常规POST请求(以接收一些数据)。
我从使用cURL请求的简单方式开始,这很容易。
以下是请求:
curl -v -X POST --header "Content-Type: application/json"
--header "Accept: application/json"
-H "X-Client-Id: xxxx"
-H "X-Client-Secret: yyyy"
--cert ../my_certificate.crt:password
--key ../my_private_key.pem
-d "{
some data
}" "https://hostname/some_url"
它工作得很完美,我收到了正确的响应。
之后,我跟着十几个指南并结合了一些类似的解决方案,但没有成功。
首先,我正在使用Java jdk8,我的客户端是okhttp3,并且我有来自远程服务器的ca证书、签名证书和私钥。
我的当前版本的初始化客户端:
try (InputStream storeStream = this.getClass().getClassLoader().getResourceAsStream("client.p12")) {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(storeStream, "password".toCharArray());
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, "password".toCharArray());
KeyManager[] keyManagers = keyFactory.getKeyManagers();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, null, new SecureRandom());
SSLContext.setDefault(sslContext);
HostnameVerifier hostnameVerifier = createHostnameVerifier();
client = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(),trustManager)
.hostnameVerifier(hostnameVerifier)
.build();
} catch (Exception e) {
}
以下是使用以下命令生成的客户端.p12文件:
openssl pkcs12 -export -in certificate.crt -inkey private_key.pem -out client.p12 -password pass:password -CAfile caCertificate.pem
我的请求是:private String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, json);
Builder builder = new Request.Builder();
builder.addHeader("X-Client-Id", "xxxx");
builder.addHeader("X-Client-Secret", "yyyy");
}
Request request = builder.url(url).post(body).build();
Response response = client.newCall(request).execute();
return response.body().string();
}
我收到了以下异常:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:992)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:267)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(InputRecord.java:505)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
... 59 common frames omitted
有任何想法我在这里做错了什么吗? 或者有什么建议能够将cURL请求反映到Java客户端上吗? 非常感谢!
javax.net.debug=ssl
,可以准确地查看您发送的内容以及服务器在握手过程中关闭连接的时间点(这是不礼貌的,它应该首先发送一个警报)。您的Java代码同时使用p12文件作为truststore和keystore,而您的curl命令使用默认值;这可能是正确的,也可能不正确,但它不会导致此特定错误。有一件事可能会导致错误,那就是如果您的curl使用openssl(检查curl -V
),并且您的客户端证书的颁发者与服务器的请求不匹配(上面的跟踪将显示);openssl不会检查这一点,但Java通常会检查。 - undefined