我今天从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。 我的错在哪里?

谢谢。


当前回答

而不是依赖apache中的默认虚拟主机机制,您可以定义最后一个使用任意ServerName和通配符ServerAlias的catchall虚拟主机,例如:

ServerName catchall.mydomain.com
ServerAlias *.mydomain.com

这样你就可以使用SNI, apache不会返回SSL警告。

当然,这只有在您可以使用通配符语法轻松描述所有域的情况下才有效。

其他回答

我在通过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;
}

我们在新的Apache服务器构建中也遇到了这个错误。

在我们的案例中,修复方法是在httpd.conf中定义一个ServerAlias,它对应于Java试图连接到的主机名。我们的ServerName被设置为内部主机名。我们的SSL证书使用外部主机名,但这不足以避免警告。

为了帮助调试,你可以使用ssl命令:

Openssl s_client -servername <hostname> -connect <hostname>:443 -state

如果该主机名有问题,那么它将在输出顶部附近打印以下消息:

SSL3警告读:警告:无法识别的名称

我还应该指出,在使用该命令连接到内部主机名时,即使它不匹配SSL证书,也没有得到该错误。

下面是针对apache httpclient 4.5.11的解决方案。我有问题与cert的主题通配符*.hostname.com。它返回了我同样的异常,但我不能使用属性System.setProperty(“jsse。enableSNIExtension”、“假”);因为它在谷歌位置客户端出错。

我找到了简单的解决方案(只修改socket):

import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;

import javax.inject.Named;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.util.List;

@Factory
public class BeanFactory {

    @Bean
    @Named("without_verify")
    public HttpClient provideHttpClient() {
        SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(SSLContexts.createDefault(), NoopHostnameVerifier.INSTANCE) {
            @Override
            protected void prepareSocket(SSLSocket socket) throws IOException {
                SSLParameters parameters = socket.getSSLParameters();
                parameters.setServerNames(List.of());
                socket.setSSLParameters(parameters);
                super.prepareSocket(socket);
            }
        };

        return HttpClients.custom()
                .setSSLSocketFactory(connectionSocketFactory)
                .build();
    }


}

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

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

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

这似乎解决了问题。

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

希望这能有所帮助。