Alerte de prise de contact SSL: erreur de nom non reconnue depuis la mise à niveau vers Java 1.7.0


Je suis passé de Java 1.6 à Java 1.7 aujourd'hui. Depuis lors, une erreur se produit lorsque j'essaie d'établir une connexion à mon serveur Web via 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)

Voici le code:

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);  
}

C'est seulement un projet de test c'est pourquoi j'autorise et utilise des certificats non approuvés avec le code:

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);
} 

J'ai essayé avec succès de me connecter à https://google.com . où est ma faute?

Merci.

Author: soufrk, 2011-10-01

16 answers

Java 7 a introduit le support SNI qui est activé par défaut. J'ai découvert que certains serveurs mal configurés envoient un avertissement "Nom non reconnu" dans la prise de contact SSL qui est ignoré par la plupart des clients... sauf pour Java. Comme @Bob Kerns l'a mentionné, les ingénieurs Oracle refusent de "corriger" ce bogue/fonctionnalité.

Comme solution de contournement, ils suggèrent de définir la propriété jsse.enableSNIExtension. Pour permettre à vos programmes de fonctionner sans re-compiler, exécutez votre application comme:

java -Djsse.enableSNIExtension=false yourClass

La propriété peut être également défini dans le code Java, mais il doit être défini avant toute action SSL. Une fois la bibliothèque SSL chargée, vous pouvez modifier la propriété, mais n'aura aucun effet sur le statut SNI. Pour désactiver SNI lors de l'exécution (avec les limitations susmentionnées), utilisez:

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

L'inconvénient de la définition de cet indicateur est que SNI est désactivé partout dans l'application. Afin d'utiliser SNI et de toujours prendre en charge les serveurs mal configurés:

  1. Créer un SSLSocket, avec le nom d'hôte que vous souhaitez vous connecter. Nommons ceci sslsock.
  2. Essayez d'exécuter sslsock.startHandshake(). Cela bloquera jusqu'à ce que ce soit fait ou lèvera une exception en cas d'erreur. Chaque fois qu'une erreur s'est produite dans startHandshake(), obtenez le message d'exception. S'il est égal à handshake alert: unrecognized_name, vous avez trouvé un serveur mal configuré.
  3. Lorsque vous avez reçu l'avertissement unrecognized_name (fatal en Java), réessayez d'ouvrir un SSLSocket, mais cette fois sans nom d'hôte. Cela désactive efficacement SNI (après tout, l'extension SNI concerne ajouter un nom d'hôte au message ClientHello).

Pour le proxy SSL Webscarab, ce commit implémente la configuration de secours.

 286
Author: Lekensteyn, 2017-05-23 12:10:35

J'ai eu ce que je crois que le même problème est. J'ai trouvé que j'avais besoin d'ajuster la configuration Apache pour inclure un nom de serveur ou ServerAlias pour l'hôte.

Ce code a échoué:

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();
   }
}

Et ce code a fonctionné:

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 a révélé que pendant le TSL / SSL Bonjour l'avertissement Alerte (Niveau: Avertissement, Description: Nom non reconnu), Serveur Bonjour A été envoyé à partir du serveur vers le client. Ce n'était qu'un avertissement, cependant, Java 7.1 a ensuite répondu immédiatement avec un "Fatal, Description: Unexpected Message", que je suppose signifie que les bibliothèques SSL Java n'aiment pas voir l'avertissement de nom non reconnu.

Du Wiki sur Transport Layer Security (TLS):

112 Avertissement de nom non reconnu TLS uniquement; l'indicateur de nom de serveur du client spécifiait un nom d'hôte non pris en charge par le serveur

Cela m'a amené à regarder mes fichiers de configuration Apache et j'ai constaté que si j'ajoutais un nom de serveur ou ServerAlias pour le nom envoyé du côté client / java, il a fonctionné correctement sans erreur.

<VirtualHost mydomain.com:443>
  ServerName mydomain.com
  ServerAlias www.mydomain.com
 84
Author: David McLaughlin, 2011-11-09 00:10:13

Vous pouvez désactiver l'envoi d'enregistrements SNI avec la propriété système jsse.enableSNIExtension=false.

Si vous pouvez changer le code, il est utile d'utiliser SSLCocketFactory#createSocket() (sans paramètre host ou avec un socket connecté). Dans ce cas, il n'enverra pas d'indication server_name.

 35
Author: eckes, 2014-06-03 01:30:18

Nous avons également rencontré cette erreur sur une nouvelle version du serveur Apache.

Le correctif dans notre cas était de définir un ServerAlias dans le httpd.conf qui correspondait au nom d'hôte auquel Java essayait de se connecter. Notre ServerName a été défini sur le nom d'hôte interne. Notre certificat SSL utilisait le nom d'hôte externe, mais cela n'était pas suffisant pour éviter l'avertissement.

Pour aider au débogage, vous pouvez utiliser cette commande ssl:

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

S'il y a un problème avec ce nom d'hôte, il affichera ceci message près du haut de la sortie:

SSL3 alert read: warning:unrecognized name

Je dois également noter que nous n'avons pas obtenu cette erreur lors de l'utilisation de cette commande pour nous connecter au nom d'hôte interne, même si elle ne correspondait pas au certificat SSL.

 16
Author: Scott McIntyre, 2015-06-29 14:26:09

Au lieu de compter sur le mécanisme d'hôte virtuel par défaut dans apache, vous pouvez définir un dernier catchall virtualhost qui utilise un nom de serveur arbitraire et un caractère générique ServerAlias, par exemple

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

De cette façon, vous pouvez utiliser SNI et apache ne renverra pas l'avertissement SSL.

Bien sûr, cela ne fonctionne que si vous pouvez décrire tous vos domaines facilement en utilisant une syntaxe générique.

 15
Author: Erik, 2012-12-17 10:21:32

Cela devrait être utile. Pour réessayer une erreur SNI dans Apache HttpClient 4.4-la façon la plus simple que nous ayons trouvée (voir 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;
            }
        }
    }
}

Et

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);
    }
}

Et

cm = new PoolingHttpClientConnectionManager(new SniHttpClientConnectionOperator(socketFactoryRegistry), null, -1, TimeUnit.MILLISECONDS);
 11
Author: Shcheklein, 2015-02-18 15:39:22

Utilisation:

  • Système.setProperty ("jsse.enableSNIExtension", "false");
  • Redémarrez votre serveur Tomcat (important)
 9
Author: Tomasz Janisiewicz, 2015-09-07 08:39:10

Vous ne pouvez pas fournir de propriétés système au jarsigner.outil exe, malheureusement.

J'ai soumis un défaut 7177232, référencement du défaut de @ eckes7127374 et expliquer pourquoi il a été fermé par erreur.

Mon défaut concerne spécifiquement l'impact sur l'outil jarsigner, mais cela les conduira peut-être à rouvrir l'autre défaut et à résoudre correctement le problème.

MISE À JOUR: En fait, il s'avère que vous POUVEZ fournir des propriétés système au Outil Jarsigner, ce n'est tout simplement pas dans le message d'aide. Utiliser jarsigner -J-Djsse.enableSNIExtension=false

 5
Author: Bob Kerns, 2014-03-10 18:49:06

Rencontré ce problème avec spring boot et jvm 1.7 et 1.8. Sur AWS, nous n'avions pas la possibilité de modifier ServerName et ServerAlias pour qu'ils correspondent (ils sont différents), nous avons donc fait ce qui suit:

Dans construire.gradle {[3] } nous avons ajouté ce qui suit:

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

Cela nous a permis de contourner le problème avec le "Nom non reconnu".

 5
Author: Ray Hunter, 2015-08-21 16:40:54

J'ai rencontré le même problème et il s'est avéré que le dns inverse n'était pas configuré correctement, il pointait vers un mauvais nom d'hôte pour l'IP. Après avoir corrigé le dns inverse et redémarré httpd, l'avertissement a disparu. (si je ne corrige pas le dns inverse, l'ajout de ServerName a également fait l'affaire pour moi)

 3
Author: user2888387, 2013-10-16 23:01:15

Mon VirtualHost's ServerName a été commentée par défaut. Cela a fonctionné après uncommenting.

 2
Author: Aram Paronikyan, 2017-01-31 21:01:40

Si vous construisez un client avec Resttemplate, vous ne pouvez définir le point de terminaison que comme ceci: https://IP/path_to_service et définissez la requestFactory.
Avec cette solution, vous n'avez pas besoin de REDÉMARRER votre TOMCAT ou 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
Author: Alfredo, 2018-02-22 14:58:40

J'ai également rencontré ce problème lors de la mise à niveau de Java 1.6_29 vers 1.7.

De manière alarmante, mon client a découvert un paramètre dans le panneau de configuration Java qui résout ce problème.

Dans l'onglet Avancé, vous pouvez cocher "Utiliser le format ClientHello compatible SSL 2.0".

Cela semble résoudre le problème.

Nous utilisons des applets Java dans un navigateur Internet Explorer.

J'espère que cela aide.

 1
Author: Allan D, 2015-05-19 13:29:07

J'ai eu le même problème avec un serveur Linux Ubuntu exécutant subversion lors de l'accès via Eclipse.

Il a montré que le problème était lié à un avertissement lors du (re)démarrage d'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

Cela est dû à une nouvelle entrée dans ports.conf, où une autre directive NameVirtualHost a été entrée parallèlement à la directive dans sites-enabled/000-default.

Après avoir supprimé la directive dans ports.conf, le problème avait disparu (après le redémarrage d'Apache, naturellement)

 0
Author: Gerhard, 2014-06-30 21:21:07

Juste pour ajouter une solution ici. Cela pourrait aider pour les utilisateurs de LAMP

Options +FollowSymLinks -SymLinksIfOwnerMatch

La ligne mentionnée ci-dessus dans la configuration de l'hôte virtuel était le coupable.

Configuration de l'hôte virtuel en cas d'erreur

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

Configuration de travail

<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
Author: Griffin, 2016-12-28 10:52:30

Il existe un moyen plus simple où vous pouvez simplement utiliser votre propre HostnameVerifier pour faire implicitement confiance à certaines connexions. Le problème vient avec Java 1.7 où des extensions SNI ont été ajoutées et votre erreur est due à une mauvaise configuration du serveur.

Vous pouvez utiliser "-Djsse.enableSNIExtension=false " pour désactiver SNI sur toute la JVM ou lire mon blog où j'explique comment implémenter un vérificateur personnalisé au-dessus d'une connexion URL.

 -2
Author: MagicDude4Eva, 2017-03-31 07:20:16