我今天从Java 1.6升级到Java 1.7。 从那时起,当我试图通过SSL建立连接到我的web服务器时,出现了一个错误:

javax.net.ssl.SSLProtocolException: handshake alert:  unrecognized_name
    at sun.security.ssl.ClientHandshaker.handshakeAlert(ClientHandshaker.java:1288)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1904)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1027)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1262)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1289)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1273)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at java.net.URL.openStream(URL.java:1035)

代码如下:

SAXBuilder builder = new SAXBuilder();
Document document = null;

try {
    url = new URL(https://some url);
    document = (Document) builder.build(url.openStream());
} catch (NoSuchAlgorithmException ex) {
    Logger.getLogger(DownloadLoadiciousComputer.class.getName()).log(Level.SEVERE, null, ex);  
}

这只是一个测试项目,这就是为什么我允许和使用不受信任的证书的代码:

TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager() {

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
        }

        public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
        }
    }
};

try {

    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {

    Logger.getLogger(DownloadManager.class.getName()).log(Level.SEVERE, null, e);
} 

我成功地连接到了https://google.com。 我的错在哪里?

谢谢。


当前回答

在spring引导和jvm 1.7和1.8时遇到了这个问题。在AWS上,我们没有选项来更改serververname和ServerAlias来匹配(它们是不同的),所以我们做了以下工作:

在构建。Gradle我们添加了以下内容:

System.setProperty("jsse.enableSNIExtension", "false")
bootRun.systemProperties = System.properties

这让我们绕过了“无法识别的名字”的问题。

其他回答

不幸的是,您不能向jarsigner.exe工具提供系统属性。

我已经提交了缺陷7177232,引用了@eckes的缺陷7127374,并解释了为什么它被错误地关闭。

我的缺陷具体是关于对jarsigner工具的影响,但它可能会导致他们重新打开其他缺陷并正确地解决问题。

更新:实际上,它证明你可以提供系统属性Jarsigner工具,它只是不在帮助消息。使用jarsigner -J-Djsse.enableSNIExtension=false

我在通过Eclipse访问Ubuntu Linux服务器时遇到了同样的问题。

它表明问题与Apache (re)启动时的警告有关:

[Mon Jun 30 22:27:10 2014] [warn] NameVirtualHost *:80 has no VirtualHosts

... waiting [Mon Jun 30 22:27:11 2014] [warn] NameVirtualHost *:80 has no VirtualHosts

这是由于ports.conf中的一个新条目,其中另一个NameVirtualHost指令与sites-enabled/000-default中的指令一起输入。

在移除ports.conf中的指令后,问题消失了(自然是在重新启动Apache之后)

如果您正在使用Resttemplate构建客户端,您只能像这样设置端点:https://IP/path_to_service并设置requestFactory。 使用这个解决方案,您不需要重新启动TOMCAT或Apache:

public static HttpComponentsClientHttpRequestFactory requestFactory(CloseableHttpClient httpClient) {
    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            return true;
        }
    };

    SSLContext sslContext = null;
    try {
        sslContext = org.apache.http.ssl.SSLContexts.custom()
                .loadTrustMaterial(null, acceptingTrustStrategy)
                .build();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }   

    HostnameVerifier hostnameVerifier = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    final SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,hostnameVerifier);

    final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", new PlainConnectionSocketFactory())
            .register("https", csf)
            .build();

    final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
    cm.setMaxTotal(100);
    httpClient = HttpClients.custom()
            .setSSLSocketFactory(csf)
            .setConnectionManager(cm)
            .build();

    HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory();

    requestFactory.setHttpClient(httpClient);

    return requestFactory;
}

Java 7引入了默认启用的SNI支持。我发现某些错误配置的服务器在SSL握手中发送一个“无法识别的名称”警告,大多数客户端都会忽略这个警告…除了Java。正如bob Kerns提到的,Oracle工程师拒绝“修复”这个bug/特性。

作为解决方案,他们建议设置jsse。enableSNIExtension财产。为了让你的程序在不重新编译的情况下工作,运行你的应用程序:

java -Djsse.enableSNIExtension=false yourClass

该属性也可以在Java代码中设置,但必须在任何SSL操作之前设置。加载SSL库后,您可以更改属性,但它不会对SNI状态产生任何影响。要在运行时禁用SNI(具有上述限制),请使用:

System.setProperty("jsse.enableSNIExtension", "false");

设置这个标志的缺点是SNI在应用程序中无处不在。为了使用SNI并且仍然支持错误配置的服务器:

Create a SSLSocket with the host name you want to connect to. Let's name this sslsock. Try to run sslsock.startHandshake(). This will block until it is done or throw an exception on error. Whenever an error occurred in startHandshake(), get the exception message. If it equals to handshake alert: unrecognized_name, then you have found a misconfigured server. When you have received the unrecognized_name warning (fatal in Java), retry opening a SSLSocket, but this time without a host name. This effectively disables SNI (after all, the SNI extension is about adding a host name to the ClientHello message).

对于Webscarab SSL代理,此提交实现了回退设置。

在从Java 1.6_29升级到1.7时,我也遇到过这个问题。

令人震惊的是,我的客户在Java控制面板中发现了一个可以解决这个问题的设置。

在高级选项卡中,你可以勾选“使用兼容SSL 2.0的ClientHello格式”。

这似乎解决了问题。

我们在Internet Explorer浏览器中使用Java小程序。

希望这能有所帮助。