programing

SSL 핸드쉐이크 경고: Java 1.7.0으로 업그레이드한 후 인식되지 않는_name 오류

bestcode 2022. 7. 13. 23:19
반응형

SSL 핸드쉐이크 경고: Java 1.7.0으로 업그레이드한 후 인식되지 않는_name 오류

저는 오늘 자바 1.6에서 자바 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 접속에 성공했습니다.내 잘못은 어디에 있지?

고마워요.

Java 7에서는 SNI 지원이 도입되었습니다.이것은 디폴트로 유효하게 되어 있습니다.SSL 핸드쉐이크로 잘못 구성된 특정 서버가 "인식할 수 없는 이름" 경고를 보내 대부분의 클라이언트에서 무시한다는 것을 알게 되었습니다.자바 제외@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_nameJava에서는 ), a warning(Java의 ), retry opening(재시도 retry opening(Java의 경우)SSLSocket그러나 이번에는 호스트 이름이 없습니다.이것에 의해, SNI 는 사실상 디세블이 됩니다(결국, SNI 확장은 ClientHello 메시지에 호스트명을 추가하는 것입니다).

Webscarab SSL 프록시의 경우 이 커밋은 폴백 설정을 구현합니다.

나도 같은 문제를 가지고 있었다.호스트의 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 Hello 중에 경고(레벨: 경고, 설명:인식할 수 없는 이름) 서버 Hello가 서버에서 클라이언트로 전송되었습니다.그러나 Java 7.1은 경고에 불과했지만 즉시 "Fatal, Description:예기치 않은 메시지"는 Java SSL 라이브러리가 인식할 수 없는 이름의 경고를 보고 싶어하지 않는다는 것을 의미합니다.

Wiki on Transport Layer Security(TLS)에서 다음을 수행합니다.

112 인식할 수 없는 이름 경고 TLS만. 클라이언트의 서버 이름 표시기가 서버에서 지원되지 않는 호스트 이름을 지정했습니다.

이를 통해 Apache 설정 파일을 살펴보니 클라이언트/자바 측에서 전송된 이름에 ServerName 또는 ServerAlias를 추가해도 오류 없이 정상적으로 동작하는 것을 알 수 있었습니다.

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

System 속성 jse.enable을 사용하여 SNI 레코드 전송을 비활성화할 수 있습니다.SNIExtension=false.

할 수 는, 「 」를 하는 것이 도움이 됩니다.SSLCocketFactory#createSocket()(호스트 파라미터가 없거나 소켓이 연결되어 있습니다.server_name __ 、 server _ name 표표____ 。

새로운 Apache 서버 빌드에서도 이 오류가 발생했습니다.

은 '어느 쪽인가'를 이었습니다.ServerAlias httpd.confJava가 접속하려고 하는 호스트명에 대응하고 있습니다. ★★★★★★★★★★★★★★★★★★ServerName이치노SSL 증명서는 외부 호스트명을 사용하고 있었지만, 경고를 회피하기에는 불충분했습니다.

디버깅을 용이하게 하려면 다음 ssl 명령을 사용합니다.

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

그 호스트명에 문제가 있는 경우는, 출력의 상부에 다음의 메세지가 표시됩니다.

SSL3 alert read: warning:unrecognized name

또, 그 커맨드를 사용해 내부 호스트명에 접속했을 때에, SSL 증명서와 일치하지 않는 에러는 발생하지 않았던 것에 주의해 주세요.

Apache의 기본 가상 호스트 메커니즘에 의존하는 대신 임의의 ServerName 및 와일드카드 ServerAlias를 사용하는 마지막 캐치올 가상 호스트를 정의할 수 있습니다.

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

이렇게 하면 SNI를 사용할 수 있으며 Apache는 SSL 경고를 반환하지 않습니다.

물론 이것은 와일드카드 구문을 사용하여 모든 도메인을 쉽게 설명할 수 있는 경우에만 작동합니다.

도움이 될 거예요.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);

용도:

  • System.setProperty("jse.enable")SNIExtension", "false";
  • Tomcat을 재시작합니다(중요).

스프링 부트 및 jvm 1.7 및 1.8에서 이 문제가 발생.AWS에서는 ServerName과 ServerAlias를 일치하도록 변경할 수 있는 옵션이 없었기 때문에 다음 작업을 수행했습니다.

build.gradle에는 다음 항목이 추가되었습니다.

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

그 때문에, 「인식할 수 없는 이름」의 문제를 회피할 수 있었습니다.

안타깝게도 jarsigner.exe 도구에는 시스템 속성을 제공할 수 없습니다.

@eckes의 결함 7127374를 참고하여 결함 7177232를 제출하여 실수로 닫힌 이유를 설명하였습니다.

저의 결함은 jarsigner 툴에 대한 영향에 관한 것이지만, 다른 결함을 다시 열어 적절히 대처하는 것으로 이어질 수 있습니다.

업데이트: 실제로 Jarsigner 도구에 시스템 속성을 제공할 수 있지만 도움말 메시지에는 표시되지 않습니다.사용하다jarsigner -J-Djsse.enableSNIExtension=false

같은 문제에 부딪혔는데, 리버스 DNS가 올바르게 설정되어 있지 않은 것이 판명되어 IP의 호스트명이 잘못되어 있습니다.리버스 DNS를 수정하고httpd를 재기동하면 경고가 사라집니다.(리버스 DNS를 수정하지 않으면 ServerName을 추가해도 문제가 되지 않습니다.)

★★★VirtualHost의 »ServerName는 디폴트로 코멘트 아웃 되어 있습니다.언코멘트 후에 효과가 있었다.

Resttemplate를 사용하여 클라이언트를 구축하는 경우 엔드포인트를 https://IP/path_to_service와 requestFactory로만 설정할 수 있습니다.
Apache를 .「 」 、 「 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;
}

Java 1.6_29에서 1.7로 업그레이드 할 때도 이 문제가 발생하였습니다.

놀랍게도, 제 고객은 Java 제어판에서 이 문제를 해결할 수 있는 설정을 발견했습니다.

고급 탭에서 'SSL 2.0 호환 ClientHello 형식 사용'을 선택할 수 있습니다.

이것으로 문제가 해결된 것 같습니다.

Internet Explorer 브라우저에서 Java 애플릿을 사용하고 있습니다.

이게 도움이 됐으면 좋겠다.

Appache httpclient 4.5.11을 사용합니다..*.hostname.com되었지만 속성에 하지 않습니다.System.setProperty("jsse.enableSNIExtension", "false");구글 위치 클라이언트에서 오류가 발생했기 때문입니다.

간단한 솔루션을 찾았습니다(소켓만 수정).

import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;

import javax.inject.Named;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.util.List;

@Factory
public class BeanFactory {

    @Bean
    @Named("without_verify")
    public HttpClient provideHttpClient() {
        SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(SSLContexts.createDefault(), NoopHostnameVerifier.INSTANCE) {
            @Override
            protected void prepareSocket(SSLSocket socket) throws IOException {
                SSLParameters parameters = socket.getSSLParameters();
                parameters.setServerNames(List.of());
                socket.setSSLParameters(parameters);
                super.prepareSocket(socket);
            }
        };

        return HttpClients.custom()
                .setSSLSocketFactory(connectionSocketFactory)
                .build();
    }


}

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

이것은, 에서의 새로운 엔트리에 의한 것입니다.ports.conf 곳에, , , , , , ,NameVirtualHost 입력되었습니다.sites-enabled/000-default.

의 한 후ports.conf(Apache를 재기동한 후 자연스럽게 문제가 해소되었습니다.

여기에 솔루션을 추가합니다.이는 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>

독자적인 HostnameVerifier 를 사용해 특정 접속을 암묵적으로 신뢰하는 간단한 방법이 있습니다.이 문제는 Java 1.7에서 SNI 확장이 추가된 경우에 발생하며, 오류는 서버 설정 오류로 인해 발생합니다.

"-Djse.enable"을 사용할 수 있습니다.SNIExtension=false"를 클릭하여 JVM 전체에서 SNI를 비활성화하거나 URL 연결 위에 커스텀 검증기를 구현하는 방법에 대해 설명하는 블로그를 읽습니다.

언급URL : https://stackoverflow.com/questions/7615645/ssl-handshake-alert-unrecognized-name-error-since-upgrade-to-java-1-7-0

반응형