truststore - Alerta de handshake SSL:erro unrecognized_name desde a atualização para o Java 1.7.0




https certificate (14)

Eu atualizei do Java 1.6 para o Java 1.7 hoje. Desde então, ocorreu um erro quando tentei estabelecer uma conexão com o meu servidor da Web sobre 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)

Aqui está o código:

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

Seu único projeto de teste é por isso que eu permito e uso certificados não confiáveis ​​com o código:

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

Tentei me conectar com sucesso a https://google.com.br . onde é minha culpa?

Obrigado.


Answers

Você não pode fornecer propriedades do sistema para a ferramenta jarsigner.exe, infelizmente.

Eu enviei o defeito 7177232 , referenciando o defeito de bugs.sun.com/bugdatabase/view_bug.do?bug_id=7127374 e explicando porque ele foi fechado por engano.

Meu defeito é especificamente sobre o impacto na ferramenta jarsigner, mas talvez isso os leve a reabrir o outro defeito e abordar o problema apropriadamente.

ATUALIZAÇÃO: Na verdade, você pode fornecer propriedades do sistema para a ferramenta Jarsigner, apenas não está na mensagem de ajuda. Use jarsigner -J-Djsse.enableSNIExtension=false


Existe uma maneira mais fácil de usar seu próprio HostnameVerifier para confiar implicitamente em determinadas conexões. O problema vem com o Java 1.7, onde as extensões SNI foram adicionadas e seu erro é devido a um erro de configuração do servidor.

Você pode usar "-Djsse.enableSNIExtension = false" para desabilitar o SNI em toda a JVM ou ler meu blog, onde explico como implementar um verificador personalizado em cima de uma conexão de URL.


Correu para este problema com boot de mola e jvm 1.7 e 1.8. Na AWS, não tínhamos a opção de alterar o ServerName e o ServerAlias ​​para corresponder (eles são diferentes), por isso fizemos o seguinte:

Em build.gradle nós adicionamos o seguinte:

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

Isso nos permitiu contornar o problema com o "Nome não reconhecido".


Eu também me deparei com esta questão ao atualizar do Java 1.6_29 para 1.7.

De forma alarmante, meu cliente descobriu uma configuração no painel de controle Java que resolve isso.

Na guia Avançado, você pode marcar 'Usar o formato ClientHello compatível com SSL 2.0'.

Isso parece resolver o problema.

Estamos usando applets Java em um navegador Internet Explorer.

Espero que isto ajude.


Se você estiver construindo um cliente com o Resttemplate, só poderá definir o terminal da seguinte forma: https: // IP / path_to_service e definir o requestFactory.
Com esta solução você não precisa reiniciar o seu 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;
}

Você pode desativar o envio de registros SNI com a propriedade do sistema jsse.enableSNIExtension = false.

Se você puder alterar o código, será útil usar SSLCocketFactory#createSocket() (sem parâmetro de host ou com um soquete conectado). Nesse caso, ele não enviará uma indicação de server_name.


Apenas para adicionar uma solução aqui. Isso pode ajudar os usuários do LAMP

Options +FollowSymLinks -SymLinksIfOwnerMatch

A linha acima mencionada na configuração do host virtual foi o culpado.

Configuração do host virtual quando erro

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

Configuração de trabalho

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

O ServerName meu VirtualHost foi comentado por padrão. Funcionou depois de descomentar.


Usar:

  • System.setProperty ("jsse.enableSNIExtension", "false");
  • Reinicie seu Tomcat (importante)

Deve ser útil. Para tentar novamente em um erro SNI no Apache HttpClient 4.4 - a maneira mais fácil que nós criamos (veja 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;
            }
        }
    }
}

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

e

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

Em vez de confiar no mecanismo de host virtual padrão no apache, você pode definir um último virtualhost catchall que usa um ServerName arbitrário e um curinga ServerAlias, por exemplo

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

Dessa forma, você pode usar o SNI e o apache não retornará o aviso SSL.

Claro, isso só funciona se você puder descrever todos os seus domínios facilmente usando uma sintaxe curinga.


O Java 7 introduziu o suporte a SNI que é ativado por padrão. Descobri que certos servidores configurados incorretamente enviam um aviso "Nome não reconhecido" no handshake SSL, que é ignorado pela maioria dos clientes ... exceto pelo Java. Como o @Bob Kerns mencionou, os engenheiros da Oracle se recusam a "consertar" esse bug / recurso.

Como solução alternativa, eles sugerem para definir a propriedade jsse.enableSNIExtension . Para permitir que seus programas funcionem sem recompilar, execute seu aplicativo como:

java -Djsse.enableSNIExtension=false yourClass

A propriedade também pode ser configurada no código Java, mas deve ser configurada antes de qualquer ação SSL . Depois que a biblioteca SSL for carregada, você poderá alterar a propriedade, mas isso não afetará o status do SNI . Para desabilitar o SNI em tempo de execução (com as limitações mencionadas acima), use:

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

A desvantagem de definir esse sinalizador é que o SNI está desabilitado em todos os lugares no aplicativo. Para fazer uso do SNI e ainda suportar servidores mal configurados:

  1. Crie um SSLSocket com o nome do host ao qual você deseja se conectar. Vamos nomear este sslsock .
  2. Tente executar sslsock.startHandshake() . Isso irá bloquear até que seja feito ou lançar uma exceção no erro. Sempre que um erro ocorreu em startHandshake() , obtenha a mensagem de exceção. Se for igual ao handshake alert: unrecognized_name , você encontrou um servidor mal configurado.
  3. Quando você receber o aviso unrecognized_name (fatal em Java), tente abrir um SSLSocket novamente, mas desta vez sem um nome de host. Isso efetivamente desabilita o SNI (afinal, a extensão SNI é sobre adicionar um nome de host à mensagem ClientHello).

Para o proxy SSL do Webscarab, este commit implementa a configuração de fall-back.


Eu tive o mesmo problema com um servidor Ubuntu Linux executando subversão quando acessado via Eclipse.

Ele mostrou que o problema tinha a ver com um aviso quando o Apache (re) foi iniciado:

[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

Isto foi devido a uma nova entrada no ports.conf , onde outra diretiva NameVirtualHost foi inserida ao lado da diretiva em sites-enabled/000-default .

Depois de remover a diretiva no ports.conf , o problema desapareceu (depois de reiniciar o Apache, naturalmente)


Eu vi todas as respostas. Mas todo mundo esqueceu de mencionar um ponto muito importante:

super () deve ser chamado ou usado na primeira linha do construtor.





java ssl