web-dev-qa-db-ja.com

javax.net.ssl.SSLException:証明書がサブジェクトの別名に一致しません

最近、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がdeveloper.foo.barが私の仮想ホストの1つであることを知っています(この仮想ホストはアルファベット順に、SSLがオンになっている最初のものですが) 。

Staging.foo.comの証明書の詳細を確認しましたが、[Subject Alternative Name]フィールドの下にリストされている名前はstaging.foo.comだけです。

では、どこからdeveloper.foo.comを取得するのでしょうか。

そして、どうすればこの問題を修正できますか?

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

これは、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>

SSLセクションは、LetsEncrypt CLIツールであるcertbotによって追加されました。

これらの各サブドメインへのアクセスは、最新のブラウザー(Chromeなど)でも問題ないことを付け加えておきます。

6
yassam

HttpClient 4.4を使用する場合は、ホスト検証(NoopH​​ostnameVerifier)を指定して、さまざまなホストからの証明書を受け入れることができるようにする必要があります。

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

使用しているApache HttpClientのバージョンはわかりませんが、バージョン4.4.1および4.5.1には、SNIが正しく機能しないバグがありました。これは4.5.3で修正されました

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

1
matt freake

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;
    }
}
1
Godfather

Httpリクエストを作成するためにorg.Apache.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;
}
0
vig-go