Вимкнення перевірки сертифіката SSL у Spring RestTemplate


81

У мене є два веб-додатки A і B на основі Spring, на двох різних машинах.

Я хочу зателефонувати за допомогою https із веб-програми A у веб-програму B, однак я використовую самопідписаний сертифікат у машині B. Отже, мій запит HTTPS не вдається.

Як я можу відключити перевірку сертифіката https під час використання RestTemplate навесні? Я хочу відключити перевірку, оскільки веб-програми A і B знаходяться у внутрішній мережі, але передача даних повинна відбуватися через HTTPS

Відповіді:


31

Вам потрібно додати спеціальний HostnameVerifierклас, який обходить перевірку сертифіката та повертає true

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

Це потрібно розмістити належним чином у вашому коді.


4
Я використовую клас RestTemplate Spring, чи знаєте ви, як я міг це зробити в RestTemplate?
Prabhu R

7
Насправді це поза межами RestTemplate. Деякі зразки коду доступні на forum.springsource.org/showthread.php?t=60854 . Також подивіться на stackoverflow.com/questions/1725863/…
Рагхурам

17
Де відповідне місце для цього, ви можете це пояснити?
kamaci

10
Це посилання допомогло мені краще: jroller.com/jurberg/entry/using_a_hostnameverifier_with_spring
Konsumierer

7
Це допомогло, дякую. До речі, ви можете спростити це до таких лямбда-виразів,HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
сказавфаган

68
@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;
 }

50

По суті, дві речі, які вам потрібно зробити, це використовувати власний TrustStrategy, який довіряє всім сертифікатам , а також використовувати NoopHostnameVerifier (), щоб вимкнути перевірку імені хосту. Ось код із усіма відповідними імпортами:

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

5
TrustStrategy acceptTrustStrategy = (X509Certificate [] x509Certificates, String s) -> true;
Хумоюн Ахмад

1
Дякую, це врятувало мені життя. Усі інші методи (додавання експортованого .crt до Java-кодексів за допомогою keytool, blog.codeleak.pl/2016/02/… та близько 5 ~ 6 інших публікацій stackoverflow , включаючи відповіді на цей) не вдалися, зайняли 7 годин мого часу. Ця відповідь була моєю останньою надією, дякую.
Gyuhyeon Lee

1
NoopHostnameVerifierДуже важливо - інша відповідь нехтувати цим. Одним з варіантів використання є використання вашого загальнодоступного SSL-сертифіката через зворотний зв'язок до localhost.
jocull

1
Будь ласка, згадайте ім'я та версію клієнтської бібліотеки Apache HTTP, яка підтримує це рішення. Оригінальне запитання задає шаблон Spring REST, який AFAICT не додає автоматично до цієї бібліотеки, дякую.
chrisinmtown


8

Додайте мою відповідь файлом cookie:

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

NullHostnameVerifier verifier = новий NullHostnameVerifier (); Ви бачите, пам’ятайте, що печиво - це рядок
Ран Адлер

5

Повний код для вимкнення перевірки імені хосту SSL,

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

1
Залежність від (Apache) NoopHostnameVerifierне повинна бути необхідною. Просто надайте (просту) користувацьку реалізацію, HostnameVerifierяк показано в інших відповідях. Це навіть може бути лямбда , як: (hostname, session) -> true.
Brent Bradburn

4

Ви можете використовувати це з HTTPClient API.

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

}

3
Це не вдається із "sun.security.validator.ValidatorException: Помилка побудови шляху PKIX ...", але його також попросили пропустити перевірку сертифіката
socona

1
@socona Дякуємо за вказівку. Це просто вимикає перевірку імені хосту, як зазначено в назві методу.
Аміт Парашар,

3

Я знайшов простий спосіб

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

2
Які імпортувати? так важко зрозуміти.
sunsoft

Додам і імпорт !!
Яш Джагдейл

1

Щоб скасувати стратегію за замовчуванням, ви можете створити простий метод у класі, де вам підключено ваш restTemplate:

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

Примітка: Звичайно, вам потрібно обробляти винятки, оскільки цей метод викидає їх лише далі!


це працювало для мене з SB 2.2.4 у зв'язку з RestTemplateBuilderвнутрішнім a @Configuration. Плюс: - замість вкладеного класу я використавTrustAllStrategy
elonderin

1

Безпека: вимкніть перевірку імені хосту сертифіката https / TLS, наступний код працював у шаблоні відпочинку для весняного завантаження

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

0

Ця проблема стосується з'єднання SSL. При спробі підключитися до якогось ресурсу протокол https вимагає створення захищеного з'єднання. Це означає, що лише ваш браузер і сервер веб-сайту знають, які дані надсилаються в тілах запитів. Ця безпека досягається сертифікатами ssl, які зберігаються на веб-сайті та завантажуються вашим браузером (або будь-яким іншим клієнтом, Spring RestTemplate із Apache Http Client позаду в нашому випадку) з першим підключенням до хосту. Навколо є шифрування RSA256 та багато інших цікавих речей. Але в кінці дня: Якщо сертифікат не зареєстрований або недійсний, ви побачите помилку сертифіката (з'єднання HTTPS не захищене). Щоб виправити помилку сертифіката, провайдеру веб-сайту потрібно придбати його для певного веб-сайту або якось виправити, наприклад https://www.register.com/ssl-certificates

Правильний спосіб вирішення проблеми

  • Зареєструйте сертифікат SSL

Неправильний спосіб вирішення проблеми

Брудний (небезпечний) спосіб вирішення проблеми

  • зробіть RestTemplate, щоб ігнорувати перевірку SSL

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

Хоча цей код може запропонувати вирішення проблеми, настійно рекомендується надати додатковий контекст щодо того, чому та / або як цей код відповідає на питання. Відповіді лише на код, як правило, стають марними в довгостроковій перспективі, оскільки майбутні глядачі, які стикаються з подібними проблемами, не можуть зрозуміти аргументацію рішення.
палаѕн

0

Якщо ви використовуєте шаблон відпочинку, ви можете використовувати цей фрагмент коду

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

0

Приклад коду Java для 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);
    }
}

До речі, не забудьте додати такі залежності до файлу pom:

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

Ви також можете знайти приклад коду Java для HttpClient <4.3 .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.