javax.net.ssl.SSLException:证书与任何主题替代名称都不匹配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39762760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:26:33  来源:igfitidea点击:

javax.net.ssl.SSLException: Certificate doesn't match any of the subject alternative names

javasslappletlets-encrypt

提问by yassam

I recently added LetsEncrypt certificates to my server and my java applet is having problems connecting using TLS.

我最近将 LetsEncrypt 证书添加到我的服务器,而我的 java 小程序在使用 TLS 连接时遇到了问题。

My applet uses Apache HttpClient.

我的小程序使用 Apache HttpClient。

My web server is Apache 2,4, and I have a few virtual hosts set up as subdomains of my main domain (foo.com - not my real domain name).

我的 Web 服务器是 Apache 2,4,我有几个虚拟主机设置为我的主域(foo.com - 不是我的真实域名)的子域。

When I run my applet on the staging subdomain (e.g. it runs off https://staging.foo.com), I get the following error:

当我在登台子域上运行我的小程序时(例如它运行https://staging.foo.com),我收到以下错误:

javax.net.ssl.SSLException: Certificate for <staging.foo.com> doesn't match any of the subject alternative names: [developer.foo.com]
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:165)
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:61)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:141)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:114)
at org.apache.http.conn.ssl.SSLSocketFactory.verifyHostname(SSLSocketFactory.java:580)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:554)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:412)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:179)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:328)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:612)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:884)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
...(cut)
at javax.swing.SwingWorker.call(SwingWorker.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at javax.swing.SwingWorker.run(SwingWorker.java:334)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

I have no idea what's going on.

我不知道是怎么回事。

First of all, I have no idea how Java knows that developer.foo.bar is one of my virtual hosts (although this virtual host is the first one, alphabetically, that has SSL turned on).

首先,我不知道 Java 怎么知道 developer.foo.bar 是我的虚拟主机之一(尽管这个虚拟主机是第一个按字母顺序打开 SSL 的)。

I've looked at the certificate detail for staging.foo.com, and the only name listed under the "Subject Alternative Name" field is staging.foo.com.

我查看了 staging.foo.com 的证书详细信息,“主题备用名称”字段下列出的唯一名称是 staging.foo.com。

So where is it getting developer.foo.com from?

那么它从哪里获得 developer.foo.com 呢?

And how do I fix this problem?

我该如何解决这个问题?

I'm using Firefox on OS X El Capitan 10.11.6 with the following Java plugin version info:

我在 OS X El Capitan 10.11.6 上使用 Firefox,其中包含以下 Java 插件版本信息:

Java Plug-in 11.102.2.14 x86_64
Using JRE version 1.8.0_102-b14 Java HotSpot(TM) 64-Bit Server VM

This is the Apache conf file for staging.foo.com:

这是 staging.foo.com 的 Apache conf 文件:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName staging.foo.com
    ServerAlias www.staging.foo.com

    # Turn on HTTP Strict Transport Security (HSTS). This tells the
    # client that it should only communicate with this site using
    # HTTPS. See
    # https://raymii.org/s/tutorials/HTTP_Strict_Transport_Security_for_Apache_NGINX_and_Lighttpd.html
    Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"

    # The following is used to tunnel websocket requests to daphne, so
    # that Django Channels can do its thing
    ProxyPass "/ws/" "ws://localhost:8001/ws/"
    ProxyPassReverse "/ws/" "ws://localhost:8001/ws/"

    # The following is used during deployment. Every page request is
    # served from one static html file.
    RewriteEngine       on
    RewriteCond         /home/www-mm/staging.foo.com/apache/in_maintenance -f
    RewriteRule .*      /home/www-mm/staging.foo.com/static/maintenance/maintenance.html

    # Use Apache to serve protected (non-static) files. This is so that
    # Apache can deal with ranges
    XSendFile on
    XSendFilePath /home/www-mm/staging.foo.com/user_assets

    # Limit uploads - 200MB
    LimitRequestBody 209715200

    Alias /static/ /home/www-mm/staging.foo.com/serve_static/
    Alias /robots.txt /home/www-mm/staging.foo.com/apache/serve-at-root/robots.txt

    <Directory /home/www-mm/staging.foo.com/serve_static>
        AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json
        Order deny,allow
        Require all granted
    </Directory>

    # Videos uploaded via staff to home page should never cache,
    # because they can change at any time (and we don't know if the
    # URLs will change or not). Etags are used and only headers are
    # sent if the files in question aren't modified (we get a 304
    # back)
    <Directory /home/www-mm/staging.foo.com/serve_static/video>
        ExpiresActive On
        # Expire immediately
        ExpiresDefault A0
    </Directory>

    # The following ensures that the maintenance page is never cached.
    <Directory /home/www-mm/staging.foo.com/static/maintenance>
        ExpiresActive On
        # Expire immediately
        ExpiresDefault A0
        Require all granted
    </Directory>

    # Hide uncompressed code from prying eyes. Python needs access to this code for the css compressor
    <Directory /home/www-mm/staging.foo.com/serve_static/js/muso>
        <Files ~ "\.js$">
            Deny from all
        </Files>
        # Order deny,allow
        # Deny from all
    </Directory>

    # Hide uncompressed code from prying eyes. Python needs access to this code for the css compressor
    <DirectoryMatch "/home/www-mm/staging.foo.com/serve_static/js/dist/.*/muso">
        Order deny,allow
        Deny from all
    </DirectoryMatch>

    <Directory /home/www-mm/staging.foo.com/apache>
        <Files django.wsgi>
            Order deny,allow
            Require all granted
        </Files>
    </Directory>

    WSGIScriptAlias / /home/www-mm/staging.foo.com/apache/django.wsgi
    WSGIDaemonProcess staging.foo.com user=www-mm group=www-mm
    WSGIProcessGroup staging.foo.com

    ErrorLog /var/log/apache2/staging.foo.com-error.log
    CustomLog /var/log/apache2/staging.foo.com-access.log combined

    SSLCertificateFile /etc/letsencrypt/live/staging.foo.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/staging.foo.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

The SSL sections were added by certbot, the LetsEncrypt CLI tool.

SSL 部分是由 LetsEncrypt CLI 工具 certbot 添加的。

I should add that accessing each of these subdomains in a modern browser (such as Chrome) is fine.

我应该补充一点,在现代浏览器(例如 Chrome)中访问这些子域中的每一个都很好。

回答by vig-go

I was getting the same error when I was using methods from org.apache.http.* for making my http requests. From your stack trace I assume that even you are using the same.

当我使用来自 org.apache.http.* 的方法来发出我的 http 请求时,我遇到了同样的错误。从您的堆栈跟踪中,我假设即使您也在使用相同的。

This error disappeared when I used java.net.HttpURLConnection and I was able to connect successfully.

当我使用 java.net.HttpURLConnection 并且能够成功连接时,此错误消失了。

import java.net.HttpURLConnection;

public static HttpURLConnection connectToWeb(String uri) {
    HttpURLConnection connection = null;
    try {
        URL url = new URL(uri);
        connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.connect();
    } catch (MalformedURLException ex) {
        ex.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return connection;
}

回答by Yurii

If you use HttpClient 4.4 then you need to specify host verifier (NoopHostnameVerifier) to allow accepting certificates from different hosts:

如果您使用 HttpClient 4.4,则需要指定主机验证器 (NoopH​​ostnameVerifier) 以允许接受来自不同主机的证书:

SSLConnectionSocketFactory scsf = SSLConnectionSocketFactory(
     SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(), 
        NoopHostnameVerifier.INSTANCE)
httpclient = HttpClients.custom().setSSLSocketFactory(scsf).build()

回答by matt freake

I don't know which version of the Apache HttpClient you were using but versions 4.4.1 and 4.5.1 had a bug where the SNI didn't work correctly. This was fixed in 4.5.3

我不知道您使用的是哪个版本的 Apache HttpClient,但 4.4.1 和 4.5.1 版本存在 SNI 无法正常工作的错误。这已在 4.5.3 中修复

https://issues.apache.org/jira/browse/HTTPCLIENT-1726

https://issues.apache.org/jira/browse/HTTPCLIENT-1726

回答by Godfather

Following Yurri's comment, it solved my problem by adding NoopHostnameVerifier.INSTANCE while initialising SSLConnectionSocketFactory :

按照 Yurri 的评论,它通过在初始化 SSLConnectionSocketFactory 时添加 NoopH​​ostnameVerifier.INSTANCE 解决了我的问题:

import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;

/**
 * Provide basic Utils for getting HttpHeader and making REST api calls.
 * 
 */
@Component
public class HttpUtil {

    private static final Logger LOG = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * The default implementation to get basic headers.
     * @return HttpHeaders.
     */
    public HttpHeaders getHttpHeaders(String userAgent, String host) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
        headers.set(HttpHeaders.USER_AGENT, userAgent);
        LOG.info("host=" + host);
        if (null != host) {
            headers.set(HttpHeaders.HOST, host);
        }

        return headers;
    }

    /**
     * Default implementation to get RestTemplate
     * @return
     */
     public RestTemplate getRestTemplate(String proxyHost, int proxyPort)
        throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {

    TrustStrategy acceptingTrustStrategy = new TrustSelfSignedStrategy();

    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
            .build();

    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();

    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    if (null != proxyHost && proxyPort > 0) {
        LOG.info("PROXY CONFIGURED | proxyHost=" + proxyHost + " | proxyPort=" + proxyPort);
        HttpHost proxy = new HttpHost(proxyHost, proxyPort, Proxy.Type.HTTP.name());
        httpClient = HttpClients.custom().setSSLSocketFactory(csf)
                .setRoutePlanner(new DefaultProxyRoutePlanner(proxy)).build();
    }
    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
}

    /**
     * Make a rest api call
     * @return ResponseEntity
     */
    public ResponseEntity<String> getApiResponse(HttpMethod httpMethod, final String URL, final String userAgent,
            String proxyHost, int proxyPort, String host) throws HttpClientErrorException {
        ResponseEntity<String> response = null;
        HttpEntity<String> httpEntity = new HttpEntity<>(getHttpHeaders(userAgent, host));
        try {
            if (null != httpMethod && null != URL) {
                RestTemplate request = null;
                try {
                    request = getRestTemplate(proxyHost, proxyPort);
                    response = request.exchange(URL, httpMethod, httpEntity, String.class);
                } catch (KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
                    LOG.error("Error creating Rest Template", e);
                }
            }
        } catch (HttpClientErrorException ex) {
            LOG.error("Method = " + httpMethod.toString() + "Request URL = " + URL);
            LOG.error("Headers =" + getHttpHeaders(userAgent, host));
            LOG.error("Response Status = " + ex.getStatusText());
            LOG.error("Response Body = " + ex.getResponseBodyAsString());
            throw ex;
        }
        return response;
    }
}

回答by Mahesh Poojary

/*
 * Inner class for Proxy SSL Socket Connection.
 */
static class MyConnectionSocketFactory extends SSLConnectionSocketFactory {
    private String proxyHost = null;
    private Integer proxyPort = null;

    public MyConnectionSocketFactory(final SSLContext sslContext, String proxyHost, Integer proxyPort) {
        super(sslContext, new NoopHostnameVerifier());
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
    }

    @Override
    public Socket createSocket(final HttpContext context) throws IOException {
        logger.debug("Create Socket:" + " ProxyHost: " + proxyHost + ", ProxyPort: " + proxyPort);
        InetSocketAddress socksaddr = new InetSocketAddress(proxyHost,proxyPort);
        Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
        return new Socket(proxy);
    }
}


   else if (proxyType.equalsIgnoreCase("socks")) {
        logger.debug("Proxy Type: " + proxyType);
        TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true;
            }
        };

        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                .loadTrustMaterial(null, acceptingTrustStrategy)
                .build();
        SSLConnectionSocketFactory csf = new MyConnectionSocketFactory(sslContext, proxyHost, proxyPort);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        restTemplate = new RestTemplate(requestFactory);
        return;
    } else {