Deaktivieren der SSL-Zertifikatüberprüfung in Spring RestTemplate

76

Ich habe zwei Spring-basierte Web-Apps A und B auf zwei verschiedenen Computern.

Ich möchte einen https-Anruf von Web-App A zu Web-App B tätigen, verwende jedoch ein selbstsigniertes Zertifikat in Computer B. Daher schlägt meine HTTPS-Anforderung fehl.

Wie kann ich die Validierung von https-Zertifikaten deaktivieren, wenn ich RestTemplate in Spring verwende? Ich möchte die Validierung deaktivieren, da sich sowohl die Webanwendung A als auch B innerhalb des internen Netzwerks befinden, die Datenübertragung jedoch über HTTPS erfolgen muss

Prabhu R.
quelle

Antworten:

29

Was Sie hinzufügen müssen, ist eine benutzerdefinierte HostnameVerifierKlasse, die die Zertifikatüberprüfung umgeht und true zurückgibt

HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

Dies muss entsprechend in Ihren Code eingefügt werden.

Raghuram
quelle
4
Ich verwende die RestTemplate-Klasse von Spring. Wissen Sie, wie ich das in RestTemplate tun kann?
Prabhu R
7
Eigentlich ist dies außerhalb von RestTemplate. Einige Beispielcodes finden Sie unter forum.springsource.org/showthread.php?t=60854 . Schauen Sie auch auf stackoverflow.com/questions/1725863/…
Raghuram
16
Wo ist der geeignete Ort dafür? Kannst du es erklären?
Kamaci
10
Dieser Link hat mir besser geholfen: jroller.com/jurberg/entry/using_a_hostnameverifier_with_spring
Konsumierer
7
Das hat geholfen, danke. Übrigens können Sie dies zu Lambda-Ausdrücken wie diesem vereinfachenHttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
sagte Fagan
66
@Bean
public RestTemplate restTemplate() 
                throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();

    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

    CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(csf)
                    .build();

    HttpComponentsClientHttpRequestFactory requestFactory =
                    new HttpComponentsClientHttpRequestFactory();

    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
 }
DanieleDM
quelle
45

Im Wesentlichen müssen Sie eine benutzerdefinierte TrustStrategy verwenden, die allen Zertifikaten vertraut , und NoopHostnameVerifier () verwenden , um die Überprüfung des Hostnamens zu deaktivieren. Hier ist der Code mit allen relevanten Importen:

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            return true;
        }
    };
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
}
Rubel
quelle
5
TrustStrategy acceptTrustStrategy = (X509Certificate [] x509Certificates, String s) -> true;
Humoyun Ahmad
1
Danke, das hat mir das Leben gerettet. Alle anderen Methoden (Hinzufügen von exportiertem .crt zu Java-Cacerts mithilfe von keytool, blog.codeleak.pl/2016/02/… und etwa 5 bis 6 anderen Stackoverflow-Posts, einschließlich Antworten auf diese), schlugen fehl und dauerten 7 Stunden. Diese Antwort war meine letzte Hoffnung, danke.
Gyuhyeon Lee
1
Das NoopHostnameVerifierist sehr wichtig - andere Antworten vernachlässigen dies. Ein Anwendungsfall ist die Verwendung Ihres öffentlichen SSL-Zertifikats per Loopback an localhost.
Jocull
NoopHostnameVerifier ist in httpclient-android: 4.3.5.1 nicht verfügbar. Wenn ich httpclient: 4.5.6 verwende, wird folgende Fehlermeldung angezeigt: Kein statisches Feld INSTANCE vom Typ Lorg / apache / http / conn / ssl / AllowAllHostnameVerifier. Könnte mir bitte jemand helfen?
Celtik
1
Bitte erwähnen Sie den Namen und die Version der Apache HTTP-Clientbibliothek, die diese Lösung unterstützt. In der ursprünglichen Frage wird nach der Spring REST-Vorlage gefragt, die AFAICT nicht automatisch in diese Bibliothek einbringt, danke.
Chrisinmtown
8

Fügen Sie meine Antwort mit Cookie hinzu:

public static void main(String[] args) {
     MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
     params.add("username", testUser);
     params.add("password", testPass);
     NullHostnameVerifier verifier = new NullHostnameVerifier(); 
     MySimpleClientHttpRequestFactory requestFactory = new MySimpleClientHttpRequestFactory(verifier , rememberMeCookie);
     ResponseEntity<String> response = restTemplate.postForEntity(appUrl + "/login", params, String.class);

     HttpHeaders headers = response.getHeaders();
     String cookieResponse = headers.getFirst("Set-Cookie");
     String[] cookieParts = cookieResponse.split(";");
     rememberMeCookie = cookieParts[0];
     cookie.setCookie(rememberMeCookie);

     requestFactory = new  MySimpleClientHttpRequestFactory(verifier,cookie.getCookie());
          restTemplate.setRequestFactory(requestFactory);
}


public class MySimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

        private final HostnameVerifier verifier;
        private final String cookie;

        public MySimpleClientHttpRequestFactory(HostnameVerifier verifier ,String cookie) {
            this.verifier = verifier;
            this.cookie = cookie;
        }

        @Override
        protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
            if (connection instanceof HttpsURLConnection) {
                ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
                ((HttpsURLConnection) connection).setSSLSocketFactory(trustSelfSignedSSL().getSocketFactory());
                ((HttpsURLConnection) connection).setAllowUserInteraction(true);
                String rememberMeCookie = cookie == null ? "" : cookie; 
                ((HttpsURLConnection) connection).setRequestProperty("Cookie", rememberMeCookie);
            }
            super.prepareConnection(connection, httpMethod);
        }

        public SSLContext trustSelfSignedSSL() {
            try {
                SSLContext ctx = SSLContext.getInstance("TLS");
                X509TrustManager tm = new X509TrustManager() {

                    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                    }

                    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                    }

                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                };
                ctx.init(null, new TrustManager[] { tm }, null);
                SSLContext.setDefault(ctx);
                return ctx;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return null;
        }

    }


    public class NullHostnameVerifier implements HostnameVerifier {
           public boolean verify(String hostname, SSLSession session) {
              return true;
           }
        }
Ran Adler
quelle
NullHostnameVerifier verifier = new NullHostnameVerifier (); Sie können sehen, erinnern Sie sich an mich Cookie ist ein String
Ran Adler
5

Vollständiger Code zum Deaktivieren der Überprüfung des SSL-Hostnamens,

RestTemplate restTemplate = new RestTemplate();
//to disable ssl hostname verifier
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() {
   @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if (connection instanceof HttpsURLConnection) {
            ((HttpsURLConnection) connection).setHostnameVerifier(new NoopHostnameVerifier());
        }
        super.prepareConnection(connection, httpMethod);
    }
});
user1986251
quelle
1
Die Abhängigkeit von (Apache) NoopHostnameVerifiersollte nicht notwendig sein. Stellen Sie einfach eine (einfache) benutzerdefinierte Implementierung von bereit, HostnameVerifierwie in anderen Antworten gezeigt. Dies kann sogar ein Lambda sein wie : (hostname, session) -> true.
Brent Bradburn
4

Sie können dies mit der HTTPClient-API verwenden.

public RestTemplate getRestTemplateBypassingHostNameVerifcation() {
    CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);

}
Amit Parashar
quelle
2
Dies schlägt mit "sun.security.validator.ValidatorException: PKIX-Pfaderstellung fehlgeschlagen ..." fehl, aber es wurde auch gebeten, die Zertifikatsprüfung zu überspringen
socona
1
@socona Danke für den Hinweis. Dies deaktiviert einfach die Überprüfung des Hostnamens, wie im Methodennamen angegeben.
Amit Parashar
3

Ich habe einen einfachen Weg gefunden

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    RestTemplate restTemplate = new RestTemplate(requestFactory);
Yash Jagdale
quelle
2
Welche soll importiert werden? so schwer zu verstehen.
Sunsoft
Ich werde auch Importe hinzufügen !!
Yash Jagdale
1

Um die Standardstrategie zu überschreiben, können Sie eine einfache Methode in der Klasse erstellen, in der Sie Ihre restTemplate verkabelt haben:

 protected void acceptEveryCertificate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {

    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            return true;
        }
    };

    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(
            HttpClientBuilder
                    .create()
                    .setSSLContext(SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build())
                    .build()));
}

Hinweis: Sicherlich müssen Sie Ausnahmen behandeln, da diese Methode sie nur weiter wirft!

Sagaris
quelle
Dies funktionierte bei mir mit SB 2.2.4 in Verbindung mit einem RestTemplateBuilderinnerhalb eines @Configuration. Plus: - Anstelle der verschachtelten Klasse habe ich dieTrustAllStrategy
elonderin
1

Sicherheit: Deaktivieren Sie die Prüfung des Hostnamens des https / TLS-Zertifikats. Der folgende Code wurde in der Spring Boot Rest-Vorlage verwendet

*HttpsURLConnection.setDefaultHostnameVerifier(
        //SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
        // * @deprecated (4.4) Use {@link org.apache.http.conn.ssl.NoopHostnameVerifier}
        new NoopHostnameVerifier()
);*
Sats
quelle
0

Bei diesem Problem handelt es sich um eine SSL-Verbindung. Wenn Sie versuchen, eine Verbindung zu einer Ressource herzustellen, muss das https-Protokoll eine gesicherte Verbindung herstellen. Das bedeutet, dass nur Ihr Browser und Ihr Website-Server wissen, welche Daten in Anforderungskörpern gesendet werden. Diese Sicherheit wird durch SSL-Zertifikate erreicht, die auf der Website gespeichert sind und von Ihrem Browser (oder einem anderen Client, in unserem Fall Spring RestTemplate mit Apache Http Client) mit der ersten Verbindung zum Host heruntergeladen werden. Es gibt RSA256-Verschlüsselung und viele andere coole Dinge. Aber am Ende eines Tages: Falls das Zertifikat nicht registriert oder ungültig ist, wird ein Zertifikatfehler angezeigt (die HTTPS-Verbindung ist nicht sicher). Um einen Zertifikatfehler zu beheben, muss der Website-Anbieter ihn für eine bestimmte Website kaufen oder irgendwie reparieren, z. B. https://www.register.com/ssl-certificates

Richtig, wie das Problem gelöst werden kann

  • Registrieren Sie das SSL-Zertifikat

Kein richtiger Weg, wie ein Problem gelöst werden kann

Schmutzige (unsichere) Art und Weise, wie das Problem gelöst werden kann

  • Lassen Sie RestTemplate die SSL-Überprüfung ignorieren

    @Bean
    public RestTemplateBuilder restTemplateBuilder(@Autowired SSLContext sslContext) {
        return new RestTemplateBuilder() {
            @Override
            public ClientHttpRequestFactory buildRequestFactory() {
                return new HttpComponentsClientHttpRequestFactory(
                        HttpClients.custom().setSSLSocketFactory(
                                new SSLConnectionSocketFactory(sslContext
                                        , NoopHostnameVerifier.INSTANCE)).build());
            }
        };
    }
    
    @Bean
        public SSLContext insecureSslContext() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
           return SSLContexts.custom()
                    .loadTrustMaterial(null, (x509Certificates, s) -> true)
                    .build();
        }
    
Oleg Maksymuk
quelle
Dieser Code bietet möglicherweise eine Lösung für das Problem. Es wird jedoch dringend empfohlen, einen zusätzlichen Kontext anzugeben, in dem erläutert wird, warum und / oder wie dieser Code die Frage beantwortet. Nur-Code-Antworten werden auf lange Sicht normalerweise unbrauchbar, da zukünftige Betrachter mit ähnlichen Problemen die Gründe für die Lösung nicht verstehen können.
Palasso
0

Wenn Sie eine Ruhevorlage verwenden, können Sie diesen Code verwenden

    fun getClientHttpRequestFactory(): ClientHttpRequestFactory {
        val timeout = envTimeout.toInt()
        val config = RequestConfig.custom()
            .setConnectTimeout(timeout)
            .setConnectionRequestTimeout(timeout)
            .setSocketTimeout(timeout)
            .build()

        val acceptingTrustStrategy = TrustStrategy { chain: Array<X509Certificate?>?, authType: String? -> true }

        val sslContext: SSLContext = SSLContexts.custom()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .build()

        val csf = SSLConnectionSocketFactory(sslContext)

        val client = HttpClientBuilder
            .create()
            .setDefaultRequestConfig(config)
            .setSSLSocketFactory(csf)
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .build()
        return HttpComponentsClientHttpRequestFactory(client)
    }

    @Bean
    fun getRestTemplate(): RestTemplate {
        return RestTemplate(getClientHttpRequestFactory())
    }
R. Ahmadi
quelle
0

Java-Codebeispiel für HttpClient> 4.3

package com.example.teocodownloader;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class Example {
    public static void main(String[] args) {
        CloseableHttpClient httpClient
                = HttpClients.custom()
                .setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory
                = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
    }
}

Vergessen Sie übrigens nicht, der POM-Datei die folgenden Abhängigkeiten hinzuzufügen:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

Sie können auch ein Java-Codebeispiel für HttpClient <4.3 finden .

Maks
quelle