java - 如何修复 SSLProtocolException : handshake alert: unrecognized_name without disabling SNI

标签 java ssl java-8 jsoup sni

我制作了一个爬虫应用程序,由于错误“握手警报:unrecognized_name”,某些网站无法连接。

我找到的大多数解决方案都是通过禁用 SNI 扩展 (jsse.enableSNIExtension=false)。但这会给需要启用 SNI 的域带来问题。

我怎样才能只对某些域禁用它?

我正在使用 Jsoup 进行抓取,并且因为我也在使用代理,所以我在启动时添加了这段代码。

  private static void disableSslVerification() {
    TrustManager[] trustAllCertificates = new TrustManager[] {
            new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null; // Not relevant.
                }
                @Override
                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    // Do nothing. Just allow them all.
                }
                @Override
                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    // Do nothing. Just allow them all.
                }
            }
    };

    HostnameVerifier trustAllHostnames = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // Just allow them all.
        }
    };

    try {
        System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");
       // System.setProperty("jsse.enableSNIExtension", "false");
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCertificates, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
    }
    catch (GeneralSecurityException e) {
        throw new ExceptionInInitializerError(e);
    }
}

如您所见,SNI 扩展已被注释。我将不胜感激。

我要访问的 url 是下一个。

https://www.ocinerioshopping.es/

最佳答案

我设法通过扩展 SSLSocketConnection 并在调用 createSocket 时发送 null 而不是主机名来解决问题。这样java就禁用了SNI。然后,我将新类的一个实例传递给 Jsoup,我知道 SNI 将在其中失败。

import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class CustomSSLSocketFactory extends SSLSocketFactory {
    private SSLSocketFactory defaultFactory;
    public CustomSSLSocketFactory() throws IOException {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }};

        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init((KeyManager[])null, trustAllCerts, new SecureRandom());
            defaultFactory = sslContext.getSocketFactory();
        } catch (KeyManagementException | NoSuchAlgorithmException var3) {
            throw new IOException("Can't create unsecure trust manager");
        }
    }
    @Override
    public String[] getDefaultCipherSuites() {
       return defaultFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return defaultFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
        //magic happens here, we send null as hostname
        return defaultFactory.createSocket(socket, null, i, b);
    }

    @Override
    public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
        return defaultFactory.createSocket(s,i);
    }

    @Override
    public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
        return defaultFactory.createSocket(s,i,inetAddress,i1);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
        return defaultFactory.createSocket(inetAddress, i);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
        return defaultFactory.createSocket(inetAddress,i, inetAddress1, i1);
    }
}

Jsoup 初始化。

Connection conn = Jsoup.connect(url);
conn.sslSocketFactory(new CustomSSLSocketFactory());

关于java - 如何修复 SSLProtocolException : handshake alert: unrecognized_name without disabling SNI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56786496/

相关文章:

ssl - 安装 kafka-connect-spooldir 不起作用

php - Wss Web 服务器无法使用自签名 key 正常工作

Java 8 : How to use javadoc aggregate

java - 使用 CompletableFuture 调整异常的正确方法

Java - 循环垃圾收集

java - 如何优化我使用 Java 仅渲染 JPEG 文件的 Web 服务器?这是极慢的

session - https 中 SSL session 的生命周期

java - CompletableFuture#whenComplete 如果使用 thenApply 则不调用

java - 为什么我使用简单的json库后无法比较字符串?

java - Sonar Web Service API,按开发者过滤数据