web-dev-qa-db-ja.com

SSLハンドシェイク警告:Java 1.7.0へのアップグレード後のunrecognized_nameエラー

今日は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 に接続しようとしました。私のせいですか?

ありがとう。

219
pvomhoff

Java 7では、デフォルトで有効になっているSNIサポートが導入されました。私は、特定の設定が誤っているサーバーがSSLハンドシェイクで "Unrecognized Name"という警告を送信することを発見しました。これはほとんどのクライアントで無視されます... Javaを除いて。 @ Bob Kerns が述べたように、Oracleのエンジニアはこのバグ/機能の「修正」を拒否しています。

回避策として、彼らはjsse.enableSNIExtensionプロパティを設定することを提案します。プログラムを再コンパイルしなくても機能するようにするには、次のようにアプリを実行します。

Java -Djsse.enableSNIExtension=false yourClass

このプロパティはJavaコードでも設定できますが、SSLアクションの前にを設定する必要があります。 SSLライブラリがロードされたら、プロパティを変更できますが、 はSNIステータスに影響しません 。実行時にSNIを無効にするには(前述の制限付きで)、次のようにします。

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

このフラグを設定することの欠点は、SNIがアプリケーションの至る所で無効になっていることです。 SNIを利用し、まだ設定ミスのサーバをサポートするためには:

  1. 接続したいホスト名でSSLSocketを作成します。これをsslsockと名付けましょう。
  2. sslsock.startHandshake()を実行してみてください。これは完了するまでブロックするか、エラー時に例外を投げます。 startHandshake()でエラーが発生したときはいつでも、例外メッセージを受け取ります。それがhandshake alert: unrecognized_nameと等しい場合、あなたは間違って設定されたサーバを見つけました。
  3. unrecognized_name警告(Javaでは致命的)を受け取った場合は、SSLSocketのオープンを再試行してください。ただし、今回はホスト名は指定しません。これは実質的にSNIを無効にします(結局のところ、SNI拡張子はClientHelloメッセージにホスト名を追加することです)。

Webscarab SSLプロキシの場合、 このコミット はフォールバック設定を実装します。

295
Lekensteyn

私は私が同じ問題があると信じるものを持っていました。ホストにServerNameまたはServerAliasを含めるようにApache設定を調整する必要があることがわかりました。

このコードは失敗しました:

public class a {
   public static void main(String [] a) throws Exception {
      Java.net.URLConnection c = new Java.net.URL("https://mydomain.com/").openConnection();
      c.setDoOutput(true);
      c.getOutputStream();
   }
}

そしてこのコードはうまくいきました:

public class a {
   public static void main(String [] a) throws Exception {
      Java.net.URLConnection c = new Java.net.URL("https://google.com/").openConnection();
      c.setDoOutput(true);
      c.getOutputStream();
   }
}

Wiresharkは、TSL/SSLハロー中に警告アラート(レベル:警告、説明:不明な名前)、サーバーハローがサーバーからクライアントに送信されていることを明らかにしました。ただの警告ですが、Java 7.1はすぐに「致命的な説明:予期しないメッセージ」で応答しました。これは、Java SSLライブラリが認識できない名前の警告を表示したくないことを意味します。

トランスポート層セキュリティ(TLS)に関するWikiから:

112認識されない名前警告TLSのみ。クライアントのサーバー名インジケーターが、サーバーでサポートされていないホスト名を指定しました

これで私は私のApache設定ファイルを見るようになりました、そして私がクライアント/ Java側から送られた名前のためにServerNameかServerAliasを追加するならば、それはエラーなしで正しく働いたことがわかりました。

<VirtualHost mydomain.com:443>
  ServerName mydomain.com
  ServerAlias www.mydomain.com
86

システムプロパティjsse.enableSNIExtension = falseでSNIレコードの送信を無効にすることができます。

コードを変更できるのであれば、SSLCocketFactory#createSocket()を使用すると便利です(Hostパラメータなし、または接続ソケットあり)。この場合、server_nameの指示は送信されません。

35
eckes

私達はまた新しいApacheサーバー構築でこのエラーに遭遇しました。

今回の修正は、Javaが接続しようとしているホスト名に対応するhttpd.confServerAliasを定義することでした。私たちのServerNameは内部のホスト名に設定されました。当社のSSL証明書は外部ホスト名を使用していましたが、それは警告を回避するのに十分ではありませんでした。

デバッグを容易にするために、このsslコマンドを使用できます。

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

そのホスト名に問題があるなら、それは出力の一番上の近くにこのメッセージをプリントするでしょう:

SSL3 alert read: warning:unrecognized name

また、そのコマンドを使用して内部ホスト名に接続しても、そのエラーが発生しなかったことにも注意してください。たとえそれがSSL証明書と一致しなくてもです。

17
Scott McIntyre

Apacheのデフォルトの仮想ホストメカニズムに頼る代わりに、任意のServerNameとワイルドカードServerAliasを使用する最後の包括仮想ホストを定義することができます。

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

そのようにしてSNIを使うことができ、ApacheはSSL警告を送り返しません。

もちろん、これはワイルドカード構文を使用してすべてのドメインを簡単に記述できる場合にのみ機能します。

15
Erik

それは役に立つはずです。 Apache HttpClient 4.4でSNIエラーを再試行する - 最も簡単な方法( HTTPCLIENT-1522 を参照):

public class SniHttpClientConnectionOperator extends DefaultHttpClientConnectionOperator {

    public SniHttpClientConnectionOperator(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
        super(socketFactoryRegistry, null, null);
    }

    @Override
    public void connect(
            final ManagedHttpClientConnection conn,
            final HttpHost Host,
            final InetSocketAddress localAddress,
            final int connectTimeout,
            final SocketConfig socketConfig,
            final HttpContext context) throws IOException {
        try {
            super.connect(conn, Host, localAddress, connectTimeout, socketConfig, context);
        } catch (SSLProtocolException e) {
            Boolean enableSniValue = (Boolean) context.getAttribute(SniSSLSocketFactory.ENABLE_SNI);
            boolean enableSni = enableSniValue == null || enableSniValue;
            if (enableSni && e.getMessage() != null && e.getMessage().equals("handshake alert:  unrecognized_name")) {
                TimesLoggers.httpworker.warn("Server received saw wrong SNI Host, retrying without SNI");
                context.setAttribute(SniSSLSocketFactory.ENABLE_SNI, false);
                super.connect(conn, Host, localAddress, connectTimeout, socketConfig, context);
            } else {
                throw e;
            }
        }
    }
}

そして

public class SniSSLSocketFactory extends SSLConnectionSocketFactory {

    public static final String ENABLE_SNI = "__enable_sni__";

    /*
     * Implement any constructor you need for your particular application -
     * SSLConnectionSocketFactory has many variants
     */
    public SniSSLSocketFactory(final SSLContext sslContext, final HostnameVerifier verifier) {
        super(sslContext, verifier);
    }

    @Override
    public Socket createLayeredSocket(
            final Socket socket,
            final String target,
            final int port,
            final HttpContext context) throws IOException {
        Boolean enableSniValue = (Boolean) context.getAttribute(ENABLE_SNI);
        boolean enableSni = enableSniValue == null || enableSniValue;
        return super.createLayeredSocket(socket, enableSni ? target : "", port, context);
    }
}

そして

cm = new PoolingHttpClientConnectionManager(new SniHttpClientConnectionOperator(socketFactoryRegistry), null, -1, TimeUnit.MILLISECONDS);
12
Shcheklein

つかいます:

  • System.setProperty( "jsse.enableSNIExtension"、 "false");
  • Tomcatを再起動してください(重要)
11

残念ながら、jarsigner.exeツールにシステムプロパティを指定することはできません。

@eckesの不具合 7127374 を参考にして不具合 7177232 を送信し、説明しているなぜそれが誤って閉じられたのです。

私の問題は、jarsignerツールへの影響に関するものですが、他の問題を解決して問題に適切に対処することにつながる可能性があります。

UPDATE:実際には、Jarsignerツールにシステムプロパティを提供することができます。ヘルプメッセージには含まれていません。 jarsigner -J-Djsse.enableSNIExtension=falseを使う

5
Bob Kerns

spring bootとjvm 1.7および1.8でこの問題に遭遇しました。 AWSでは、ServerNameとServerAliasを一致させるように変更することはできませんでした(それらは異なります)。そのため、次のようにしました。

build.gradleに以下を追加しました。

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

それにより、「認識されない名前」の問題を回避することができました。

5
Ray Hunter

私は同じ問題にぶつかりました、そしてそれは逆DNSが正しくセットアップされていないことが判明しました、それはIPの間違ったホスト名を指していました。リバースDNSを修正してhttpdを再起動すると、警告は消えます。 (逆DNSを修正しない場合は、ServerNameを追加してもうまくいきます)

3
user2888387

私のVirtualHostServerNameはデフォルトでコメントアウトされています。コメント解除してからうまくいきました。

2
Aram Paronikyan

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;
}
2
Alfredo

私はまた、Java 1.6_29から1.7へのアップグレード中にこの問題に遭遇しました。

驚いたことに、私の顧客はこれを解決するJavaコントロールパネルの設定を発見しました。

[詳細設定]タブで、[SSL 2.0互換のClientHello形式を使用する]を選択できます。

これで問題は解決したようです。

Internet ExplorerブラウザでJavaアプレットを使用しています。

お役に立てれば。

1
Allan D

ここに解決策を追加するだけです。これはLAMPユーザーにとって役に立つかもしれません

Options +FollowSymLinks -SymLinksIfOwnerMatch

仮想ホスト構成の上記の行が原因でした。

エラー時の仮想ホスト設定

<VirtualHost *:80>
    DocumentRoot /var/www/html/load/web
    ServerName dev.load.com
    <Directory "/var/www/html/load/web">
        Options +FollowSymLinks -SymLinksIfOwnerMatch
        AllowOverride All
        Require all granted
        Order Allow,Deny
        Allow from All
    </Directory>
     RewriteEngine on
     RewriteCond %{SERVER_PORT} !^443$
     RewriteRule ^/(.*) https://%{HTTP_Host}/$1 [NC,R=301,L]
</VirtualHost>

動作設定

<VirtualHost *:80>
    DocumentRoot /var/www/html/load/web

   ServerName dev.load.com
   <Directory "/var/www/html/load/web">

        AllowOverride All

        Options All

        Order Allow,Deny

        Allow from All

    </Directory>

    # To allow authorization header
    RewriteEngine On
    RewriteCond %{HTTP:Authorization} ^(.*)
    RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]

   # RewriteCond %{SERVER_PORT} !^443$
   # RewriteRule ^/(.*) https://%{HTTP_Host}/$1 [NC,R=301,L]


</VirtualHost>
0
Griffin

Subversionを実行しているUbuntu LinuxサーバーでEclipse経由でアクセスしたときにも同じ問題がありました。

Apacheが(再)起動したときの問題は警告と関係があることがわかりました。

[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

これは、別のNameVirtualHostディレクティブがports.conf内のディレクティブと一緒に入力された、sites-enabled/000-default内の新規エントリーによるものです。

ports.confのディレクティブを削除した後、問題は消えました(当然のことながらApacheを再起動した後に)

0
Gerhard