Java 11下解决SSLHandshakeException: unrecognized_name的请求级方案问询
请求级禁用SNI解决Java 11的SSLHandshakeException问题
是的,Java 11环境下确实存在请求级的解决方案,可以单独针对https://www.minervamedica.it这类有SNI兼容性问题的站点禁用SNI,不需要全局修改系统属性,避免影响其他依赖SNI的请求。
可行的实现方案
核心思路是自定义SSLSocketFactory,在创建SSLSocket后手动移除SNI相关参数,或者在初始化SSL参数时不添加SNIHostName。下面是具体的代码实现:
自定义SSLSocketFactory包装类
import javax.net.ssl.*; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; public class SNIDisabledSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public SNIDisabledSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { SSLSocket sslSocket = (SSLSocket) delegate.createSocket(s, host, port, autoClose); // 移除SNI参数 disableSNI(sslSocket); return sslSocket; } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port); disableSNI(sslSocket); return sslSocket; } @Override public Socket createSocket(InetAddress host, int port) throws IOException { SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port); disableSNI(sslSocket); return sslSocket; } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort); disableSNI(sslSocket); return sslSocket; } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { SSLSocket sslSocket = (SSLSocket) delegate.createSocket(address, port, localAddress, localPort); disableSNI(sslSocket); return sslSocket; } private void disableSNI(SSLSocket sslSocket) { SSLParameters sslParams = sslSocket.getSSLParameters(); // 清空SNI主机名列表 sslParams.setServerNames(new ArrayList<>()); sslSocket.setSSLParameters(sslParams); } }
在HttpsURLConnection中使用该工厂
import javax.net.ssl.HttpsURLConnection; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; public class Main { public static void main(String[] args) throws Exception { URL url = new URL("https://www.minervamedica.it"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // 使用自定义的SSLSocketFactory,禁用SNI SSLSocketFactory originalFactory = SSLSocketFactory.getDefault(); SNIDisabledSSLSocketFactory sniDisabledFactory = new SNIDisabledSSLSocketFactory(originalFactory); conn.setSSLSocketFactory(sniDisabledFactory); // 同时设置一个宽松的HostnameVerifier(因为服务器证书可能存在主机名不匹配问题) conn.setHostnameVerifier((hostname, session) -> true); // 发起请求并读取响应 try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } } }
为什么之前的方案无效?
- 自定义X509ExtendedTrustManager配置SNIHostName:这个方向是错误的,问题根源是服务器无法识别发送的SNI扩展,应该移除SNI参数而不是主动设置。
- 自定义HostnameVerifier被忽略:因为
unrecognized_name异常发生在SSL握手的早期阶段,此时还没到主机名验证的步骤,所以HostnameVerifier根本不会被触发。
补充说明
- 该服务器确实存在配置问题,通过
openssl s_client -connect www.minervamedica.it:443 -servername www.minervamedica.it可以验证会返回unrecognized_name警告。 - Java 11对SNI的处理逻辑比Java 8更严格,Java 8中通过空主机创建Socket的方式在Java 11中已失效,这也是你遇到问题的原因。
- 你提交的JDK bug(JDK-8223677)确实反映了这个兼容性问题,但在官方修复前,上述请求级方案是可行的替代方案。
内容的提问来源于stack exchange,提问作者fly
相关产品推荐
相关产品推荐

