我正在通过 OpenSSL 和 Windows 套接字在 Windows 上以 C++ 运行本地 TCP 服务器。我正在使用 SSL 套接字在 Java 中运行客户端。对于大多数用户来说,这个设置是有效的,但是,一些用户在尝试 SSL 握手时会遇到以下 Java 异常:
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
at sun.security.ssl.SSLSocketImpl.handleEOF(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketImpl.decode(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) ~[?:1.8.0_321]
...
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.SSLSocketInputRecord.read(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketInputRecord.readHeader(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLSocketInputRecord.decode(Unknown Source) ~[?:1.8.0_321]
at sun.security.ssl.SSLTransport.decode(Unknown Source) ~[?:1.8.0_321]
... 10 more
我的 Java 客户端套接字代码如下所示:
public static Socket getClientSocket() throws Exception
{
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 54321);
SSLSocketFactory socketFactory = getSSLSocketFactory();
Socket clientSocket = socketFactory.createSocket();
clientSocket.connect(socketAddress, 1_000);
((SSLSocket) clientSocket).startHandshake(); // <-- Exception here for some users
return clientSocket;
}
private static SSLSocketFactory getSSLSocketFactory() throws Exception
{
// Create a trust manager that does not validate certificate chains
X509TrustManager trustManager = new X509TrustManager()
{
@Override
public X509Certificate[] getAcceptedIssuers()
{
// TODO Returning null is not allowed but it still works
return null;
}
@Override
public void checkClientTrusted(final X509Certificate[] certificates, final String authType)
{
}
@Override
public void checkServerTrusted(final X509Certificate[] certificates, final String authType)
{
for (X509Certificate certificate : certificates)
{
String certificateString = certificate.toString();
if (!certificateString.contains("blablabla"))
{
throw new SSLException("Certificate not trusted");
}
}
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
return sslContext.getSocketFactory();
}
尝试的解决方案:
- 连接前调用
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2")
不起作用(参考) - 我不想告诉用户将任何 SSL 证书导入他们的信任库(除非我可以轻松地自动执行此操作)
不一致的行为从何而来,如何为每个遇到异常的人彻底解决?我可以访问服务器源代码和证书。也欢迎任何关键问题或建议。如有必要,我可以提供更多信息。