javax.net.ssl.SSLException:证书与任何使用者备用名称都不匹配

nuypyhwy  于 2023-02-13  发布在  Java
关注(0)|答案(8)|浏览(203)

我最近添加了LetsEncrypt证书到我的服务器,我的Java小程序在使用TLS连接时遇到了问题。
我的小程序使用Apache HttpClient。
我的Web服务器是Apache 2,4,我有一些虚拟主机设置为我的主域(foo.com-不是我的真实域名)的子域。
当我在临时子域上运行我的小程序时(例如,它在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$1.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)

我不知道发生了什么事。
首先,我不知道Java如何知道www.example.com是我的虚拟主机之一(尽管这个虚拟主机是第一个打开SSL的虚拟主机,按字母顺序排列)。developer.foo.bar is one of my virtual hosts (although this virtual host is the first one, alphabetically, that has SSL turned on).
我查看了www.example.com的证书详细信息,"Subject Alternative Name"字段下列出的唯一名称是staging.foo.com。staging.foo.com, and the only name listed under the "Subject Alternative Name" field is staging.foo.com.
那么它是从哪里得到www.example.com的呢?developer.foo.com from?
我该如何解决这个问题?
我正在使用Firefox的OS X El Capitan 10.11.6与以下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

这是www.example.com的Apache配置文件staging.foo.com:

<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>

SSL部分是由LetsEncrypt CLI工具certbot添加的。
我应该补充的是,在现代浏览器(如Chrome)中访问这些子域都是可以的。

envsm3lx

envsm3lx1#

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

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

mcvgt66p2#

根据Yurri的评论,它通过在初始化SSLConnectionSocketFactory时添加NoopHostnameVerifier.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;
    }
}
ct2axkht

ct2axkht3#

我不知道您使用的是哪个版本的ApacheHttpClient,但是4.4.1和4.5.1版本有一个错误,SNI不能正常工作。
https://issues.apache.org/jira/browse/HTTPCLIENT-1726

piwo6bdm

piwo6bdm4#

如果您尝试访问任何对象中的URL,请尝试在代码中设置以下参数(取决于您尝试访问URL的方式,例如,这里我们使用WebClient对象来设置以下参数)
创建WebClient对象并设置以下内容:-
WebClient webClient = null;
System.setProperty("jsse.enableSNIExtension", "false");
根据您的WebClient版本设置如下。
webClient.getOptions().setUseInsecureSSL(true);

8ehkhllq

8ehkhllq5#

当您的主机/域名与您的证书CN名不匹配时,您会收到此错误。因此,在这种情况下,我们必须关闭NO_OP的主机名验证(它在大于4.3的httpclient依赖项中可用)
样本代码:

SSLContext sslContextBuilder = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("file:" + "/path/myclient.jks"), "123456".toCharArray(),"123456".toCharArray())
                .loadTrustMaterial(ResourceUtils.getFile("file:" + "/path/myclient.jks"), "123456".toCharArray()).build();

    CloseableHttpClient httpClient
                = HttpClients.custom()
                .setSSLHostnameVerifier(new NoopHostnameVerifier()).setSSLContext(sslContextBuilder)
                .build();

        final ClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);

        restTemplate.setRequestFactory(requestFactory);
k7fdbhmy

k7fdbhmy6#

如果你正在开发一个react-native应用程序,而这个问题发生在你试图构建你的android应用程序的时候(至少我是这样做的!),这可能会有所帮助。
这是一个黑客,而不是一个解决方案,但如果你只是需要它的工作,现在,这是怎么回事。对我来说,问题是:

Execution failed for task ':expo-modules-core:downloadBoost'.
> A failure occurred while executing de.undercouch.gradle.tasks.download.internal.DefaultWorkerExecutorHelper$DefaultWorkAction
   > javax.net.ssl.SSLPeerUnverifiedException: Certificate for <jfrog-prod-usw2-shared-oregon-main.s3.amazonaws.com> doesn't match any of the subject alternative names: [*.s3.amazonaws.com, s3.amazonaws.com]

因此,我转到模块expo-modules-corebuild.gradle文件(注意,不是您的根或应用程序build.gradle文件。
现在,我找到了downloadBoost任务(因为我的日志显示问题与downloadBoost有关。对我来说,它看起来像这样:

def downloadBoost = tasks.create('downloadBoost', Download) {
  dependsOn(createNativeDepsDirectories)
  def srcUrl = REACT_NATIVE_TARGET_VERSION >= 69
    ? "https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz"
    : "https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz"
  src(srcUrl)
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
}

我刚刚添加了一个hack,这样它就可以接受任何证书。acceptAnyCertificate true。因此,最后一个块将类似于:

def downloadBoost = tasks.create('downloadBoost', Download) {
  acceptAnyCertificate true
  dependsOn(createNativeDepsDirectories)
  def srcUrl = REACT_NATIVE_TARGET_VERSION >= 69
    ? "https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz"
    : "https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz"
  src(srcUrl)
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
}

好吧,就是这样!我成功了。希望能帮上忙。

dfty9e19

dfty9e197#

当我使用org.apache.http.* 中的方法进行http请求时,我也遇到了同样的错误。从你的堆栈跟踪来看,我假设你也在使用相同的方法。
当我使用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;
}
lpwwtiir

lpwwtiir8#

/*
 * 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 {

相关问题