Предупреждение о рукопожатии SSL: ошибка нераспознанного имени после обновления до Java 1.7.0

205

Я сегодня обновился с Java 1.6 до Java 1.7. С тех пор возникает ошибка, когда я пытаюсь установить соединение с моим веб-сервером через SSL:

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. где моя вина?

Спасибо.

Теги:
ssl

15 ответов

275

В Java 7 появилась поддержка SNI, которая включена по умолчанию. Я обнаружил, что некоторые неправильно сконфигурированные серверы отправляют предупреждение "Нераспознанное имя" в квитировании SSL, которое игнорируется большинством клиентов... кроме Java. Как упоминалось @Bob Kerns, инженеры Oracle отказываются "исправлять" эту ошибку/функцию.

В качестве обходного пути они предлагают установить свойство jsse.enableSNIExtension. Чтобы ваши программы работали без повторной компиляции, запустите приложение как:

java -Djsse.enableSNIExtension=false yourClass

Свойство также может быть установлено в коде Java, но оно должно быть установлено перед любыми действиями SSL. После загрузки библиотеки SSL вы можете изменить свойство, но не будет иметь никакого эффекта в статусе SNI. Чтобы отключить SNI во время выполнения (с указанными ограничениями), используйте:

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

Недостаток установки этого флага заключается в том, что SNI отключен повсюду в приложении. Чтобы использовать SNI и поддерживать неверно сконфигурированные серверы:

  • Создайте SSLSocket с именем хоста, с которым хотите подключиться. Назовите это sslsock.
  • Попробуйте запустить sslsock.startHandshake(). Это будет заблокировано до тех пор, пока оно не будет выполнено, или не вызовет исключение при ошибке. Всякий раз, когда произошла ошибка в startHandshake(), получите сообщение об исключении. Если он равен handshake alert: unrecognized_name, то вы нашли неверно сконфигурированный сервер.
  • Когда вы получили предупреждение unrecognized_name (фатальное в Java), повторите попытку открытия SSLSocket, но на этот раз без имени хоста. Это фактически отключает SNI (в конце концов, расширение SNI связано с добавлением имени узла в сообщение ClientHello).

Для прокси-сервера Webscarab SSL этот коммит реализует настройку спада.

  • 5
    это работает, спасибо! Клиент Subversion IntelliJ IDEA имел ту же ошибку при подключении через HTTPS. Просто нужно обновить файл idea.exe.vmoptions строкой: -Djsse.enableSNIExtension = false
  • 2
    Для веб-запуска Java используйте это: javaws -J-Djsse.enableSNIExtension = false yourClass
Показать ещё 11 комментариев
81

У меня было то, что я считаю той же проблемой. Я обнаружил, что мне нужно настроить конфигурацию Apache для включения ServerName или ServerAlias ​​для хоста.

Этот код не удалось:

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 приветствие Предупреждение (Уровень: Предупреждение, Описание: Нераспознанное имя), Сервер Hello Был отправлен с сервера клиенту. Это было только предупреждение, однако, Java 7.1 затем сразу же ответил "Fatal, Description: Unexpected Message", который, как я полагаю, означает, что библиотеки Java SSL не любят видеть предупреждение о непризнанном имени.

Из Wiki по безопасности транспортного уровня (TLS):

112 Предупреждение о непризнанном имени только TLS; указатель имени сервера клиента указал имя хоста, которое не поддерживается сервером

Это заставило меня посмотреть мои конфигурационные файлы Apache, и я обнаружил, что если бы я добавил имя_сервера или ServerAlias ​​для имени, отправленного со стороны client/java, он работал правильно без каких-либо ошибок.

<VirtualHost mydomain.com:443>
  ServerName mydomain.com
  ServerAlias www.mydomain.com
  • 3
    Это похоже на ошибку в реализации TLS Java.
  • 1
    Я фактически открываю ошибку для этого. Мне присвоили этот номер (пока не видно): bugs.sun.com/bugdatabase/view_bug.do?bug_id=7127374
Показать ещё 4 комментария
34

Вы можете отключить отправку записей SNI с помощью свойства System jsse.enableSNIExtension = false.

Если вы можете изменить код, он помогает использовать SSLCocketFactory#createSocket() (без параметра хоста или с подключенным сокетом). В этом случае он не будет отправлять идентификатор server_name.

  • 11
    Что делается следующим образом: System.setProperty ("jsse.enableSNIExtension", "false");
  • 0
    Я обнаружил, что это не всегда работает. Я не знаю, почему это работает в некоторых случаях, а не в других.
Показать ещё 3 комментария
14

Вместо того, чтобы полагаться на механизм виртуального хоста по умолчанию в apache, вы можете определить один последний виртуальный хост, который использует произвольное имя сервера и подстановочный сервер ServerAlias, например

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

Таким образом вы можете использовать SNI, и apache не отправит предупреждение SSL.

Конечно, это работает только в том случае, если вы можете легко описать все свои домены с помощью подстановочного синтаксиса.

  • 0
    Насколько я понимаю, apache отправляет это предупреждение только в том случае, если клиент использует имя, не настроенное как ServerName или ServerAlias. Поэтому, если у вас есть *.mydomain.com сертификат, либо укажите все используемые поддомены, либо используйте синтаксис *.mydomain.com для ServerAlias. Обратите внимание, что вы можете добавить несколько псевдонимов, используя ServerAlias , например, ServerAlias *.mydomain.com mydomain.com *.myotherdomain.com .
12

Мы также столкнулись с этой ошибкой на новой сборке сервера Apache.

Исправление в нашем случае состояло в том, чтобы определить ServerAlias в httpd.conf, который соответствует имени хоста, к которому пытается подключиться Java. Наш ServerName был настроен на внутреннее имя хоста. Наш сертификат SSL использовал имя внешнего хоста, но этого было недостаточно, чтобы избежать предупреждения.

Чтобы отладить, вы можете использовать эту команду ssl:

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

Если с этим именем хоста возникла проблема, он будет печатать это сообщение в верхней части вывода:

SSL3 alert read: warning:unrecognized name

Я также должен отметить, что мы не получили эту ошибку при использовании этой команды для подключения к внутреннему имени хоста, хотя это не соответствовало сертификату SSL.

  • 6
    Спасибо за включение примера о том, как воспроизвести проблему с использованием openssl . Это очень полезно.
  • 2
    Есть ли способ для клиента openssl увидеть разницу имен, которая SSL3 alert read: warning:unrecognized name сообщение « SSL3 alert read: warning:unrecognized name ? Я вижу напечатанное предупреждение, но результат не помогает мне увидеть эту разницу в именах
9

Использование:

  • System.setProperty( "jsse.enableSNIExtension", "false" );
  • Перезапустите Tomcat (важно)
8

Это должно быть полезно. Чтобы повторить ошибку SNI в Apache HttpClient 4.4 - самый простой способ, с которым мы столкнулись (см. 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);
  • 0
    Если вы делаете это таким образом. Как вы справляетесь с verifyHostname(sslsock, target) в SSLConnectionSocketFactory.createLayeredSocket ? Поскольку target меняется на пустую строку, verifyHostname не будет успешным. Я что-то упускаю здесь очевидное?
  • 2
    Это было именно то, что я искал, большое спасибо! Я не нашел другого способа поддержки SNI и серверов, которые одновременно отвечают этим предупреждением "unrecognized_name".
Показать ещё 1 комментарий
5

К сожалению, вы не можете предоставить системные свойства инструменту jarsigner.exe.

Я отправил дефект 7177232, ссылаясь на дефект @eckes 7127374 и объяснил, почему он был закрыт по ошибке.

Мой дефект в особенности касается воздействия на инструмент jarsigner, но, возможно, это приведет к повторному открытию другого дефекта и правильному решению проблемы.

ОБНОВЛЕНИЕ: На самом деле, оказывается, что вы можете предоставлять системные свойства инструменту Jarsigner, это просто не в справочном сообщении. Используйте jarsigner -J-Djsse.enableSNIExtension=false

  • 1
    Спасибо Бобу за продолжение. К сожалению, Oracle до сих пор не понимает всего этого. Предупреждение SNI не смертельно, его можно лечить смертельно. Но большинство типичных клиентов SSL (т. Е. Браузеров) решили игнорировать его, поскольку это не является реальной проблемой безопасности (поскольку вам все равно нужно проверить сертификат сервера и обнаружить неправильные конечные точки). Конечно, печально, что ЦС не может установить правильно настроенный сервер https.
  • 2
    На самом деле, оказывается, что вы МОЖЕТЕ предоставить системные свойства инструменту Jarsigner, но его нет в справочном сообщении. Это описано в документации. Глупый я за веру в онлайн текст. Конечно, они использовали это как оправдание для того, чтобы не исправить это.
Показать ещё 1 комментарий
4

Включите эту проблему с помощью spring загрузки и jvm 1.7 и 1.8. В AWS у нас не было возможности изменить имя ServerName и ServerAlias ​​для соответствия (они разные), поэтому мы сделали следующее:

В build.gradle мы добавили следующее:

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

Это позволило нам обойти проблему с помощью "Нераспознанного имени".

3

Я столкнулся с той же проблемой, и оказалось, что обратный dns не был настроен правильно, он указал на неправильное имя хоста для IP. После того, как я исправлю обратные dns и перезапустите httpd, предупреждение исчезнет. (если я не исправляю обратные dns, добавление ServerName тоже помогло мне)

1

Мой VirtualHost ServerName был отмечен по умолчанию. Он работал после расторжения.

  • 0
    Тоже самое. Имя сервера отсутствует.
1

Я также столкнулся с этой проблемой при обновлении с версии от Java 1.6_29 до 1.7.

Тревожно, мой клиент обнаружил настройку на панели управления Java, которая разрешает это.

На вкладке "Дополнительно" вы можете проверить "Использовать совместимый с SSL 2.0 формат ClientHello".

Это, похоже, разрешает проблему.

Мы используем Java-апплеты в браузере Internet Explorer.

Надеюсь, что это поможет.

0

Просто добавьте решение здесь. Это может помочь пользователям 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

У меня была та же проблема с сервером Ubuntu Linux, выполняющим подрывную деятельность при доступе через Eclipse.

Он показал, что проблема связана с предупреждением при запуске 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, естественно)

-3

Существует более простой способ, где вы можете просто использовать свой собственный HostnameVerifier для неявного доверия определенным соединениям. Проблема связана с Java 1.7, где добавлены расширения SNI, а ваша ошибка связана с неправильной конфигурацией сервера.

Вы можете использовать "-Djsse.enableSNIExtension = false", чтобы отключить SNI для всей JVM или прочитать мой блог, где я объясню, как реализовать пользовательский верификатор поверх URL-соединения.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню