You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:32:53