這個答案來得晚,但我們只是碰到了問題(我不敢相信它,它似乎是一個非常大的錯誤)。
所有的說法似乎都是真的,但它並不是默認的HostnameVerifier罪魁禍首,而是故障排除者。當HttpsClient做afterConnect首先嚐試建立setHost(僅當插座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 EN 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中按預期工作。但是他們(甲骨文)似乎並不想解決這個問題:
你可以嘗試捕獲網絡跟蹤,看看在你的tomcat中,應用程序是否發送SNI指示?你的代碼是做什麼https請求? –
SNI不會奇蹟般地添加到SSL連接,但負責建立SSL連接的代碼必須使用它。並非所有的HTTP庫都這樣做。我的猜測是,您在tomcat中使用不同的庫或代碼來訪問URL,而不是在您的小測試程序中,以便在一種情況下使用SNI,而在另一種情況下不使用。 –
您可以在命令行中使用'-Djavax.net.debug = ssl'啓用Java的SSL消息調試_e.g._,並查看Java客戶端正在發送/接收的內容...... – Castaglia