我可以使用curl访问各种虚拟主机(假设SNI使其工作).我也可以使用一个基本上只是在URL上的openConnection()的命令行Java程序来访问它们.
在我的Tomcat应用程序中,基本相同的客户端代码访问与客户端相同的Apache服务器,但总是以默认证书(defaulthost.defaultdomain)结束,而不是在URL中指定的虚拟主机的证书尝试访问. (这会产生SunCertPathBuilderException – 基本上它无法验证证书的证书路径,当然这是真的,因为它是非官方证书.但是,无论如何都不应该使用默认证书.)
就像我的应用程序/ Tomcat中的SNI已在客户端停用一样.我不知道为什么我的应用程序和命令行之间应该有不同的行为;相同的JDK,相同的主机等
我找到了属性jsse.enableSNIExtension,但我确认两种情况都设置为true.问题:
>任何想法,甚至是狂野的想法,为什么这两个程序的行为不同?
>任何想法我将如何调试这个?
这是Arch Linux on 86_64,JDK 8u77,Tomcat 8.0.32.
它所说的一切似乎都是正确的,但它不是默认的HostnameVerifier的罪魁祸首,而是故障排除者.当HttpsClient执行afterConnect时首先尝试建立setHost(仅当socket是SSLSocketImpl时):
SSLSocketFactory factory = sslSocketFactory;
try {
if (!(serverSocket instanceof SSLSocket)) {
s = (SSLSocket)factory.createSocket(serverSocket,
host, port, true);
} else {
s = (SSLSocket)serverSocket;
if (s instanceof SSLSocketImpl) {
((SSLSocketImpl)s).setHost(host);
}
}
} catch (IOException ex) {
// If we fail to connect through the tunnel, try it
// locally, as a last resort. If this doesn't work,
// throw the original exception.
try {
s = (SSLSocket)factory.createSocket(host, port);
} catch (IOException ignored) {
throw ex;
}
}
如果您使用自定义SSLSocketFactory而不覆盖createSocket()(没有参数的方法),则使用完全参数化的createSocket,并且所有都按预期工作(使用客户端sni扩展).但是当第二种方式被使用时(尝试setHost和SSLSocketImpl),执行的代码是:
// ONLY used by HttpsClient to setup the URI specified hostname
//
// Please NOTE that this method MUST be called before calling to
// SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter
// may override SNIHostName in the customized server name indication.
synchronized public void setHost(String host) {
this.host = host;
this.serverNames =
Utilities.addToSNIServerNameList(this.serverNames, this.host);
}
评论说全部.您需要在客户端握手之前调用setSSLParameters.如果使用默认的HostnameVerifier,HttpsClient将调用setSSLParameters.但是没有相反的方式执行setSSLParameters. Oracle的修复应该非常简单:
SSLParameters paramaters = s.getSSLParameters();
if (isDefaultHostnameVerifier) {
// If the HNV is the default from HttpsURLConnection, we
// will do the spoof checks in SSLSocket.
paramaters.setEndpointIdentificationAlgorithm("HTTPS");
needToCheckSpoofing = false;
}
s.setSSLParameters(paramaters);
Java 9在SNI中正如预期的那样工作.但他们(甲骨文)似乎不想解决这个问题:
> https://bugs.openjdk.java.net/browse/JDK-8072464
> https://bugs.openjdk.java.net/browse/JDK-8144566
>还有更多
转载注明原文:使用Java8的SNI客户端之谜 - 乐贴网