web-dev-qa-db-fra.com

Comment désactiver la vérification de certificat SSL avec Spring RestTemplate?

J'essaie d'écrire un test d'intégration au cours duquel notre test lance un serveur HTTPS intégré à l'aide de Simple . Je créé un certificat auto-signé à l'aide de keytool et suis en mesure d'accéder au serveur à l'aide d'un navigateur (plus précisément de Chrome, et je reçois un avertissement concernant le certificat auto-signé).

Cependant, lorsque j'essaie de me connecter avec Spring RestTemplate , j'obtiens un ResourceAccessException :

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8088":Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.Java:557)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.Java:502)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.Java:444)
    at net.initech.DummySslServer.shouldConnect(DummySslServer.Java:119)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.Java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:229)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.Java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.Java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.Java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:67)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.Java:134)
Caused by: javax.net.ssl.SSLHandshakeException: Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at Sun.security.ssl.Alerts.getSSLException(Alerts.Java:192)
    at Sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.Java:1917)
    at Sun.security.ssl.Handshaker.fatalSE(Handshaker.Java:301)
    at Sun.security.ssl.Handshaker.fatalSE(Handshaker.Java:295)
    at Sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.Java:1369)
    at Sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.Java:156)
    at Sun.security.ssl.Handshaker.processLoop(Handshaker.Java:925)
    at Sun.security.ssl.Handshaker.process_record(Handshaker.Java:860)
    at Sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.Java:1043)
    at Sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.Java:1343)
    at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1371)
    at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1355)
    at Sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.Java:563)
    at Sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.Java:185)
    at Sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.Java:153)
    at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.Java:78)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.Java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.Java:52)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.Java:541)
    ... 33 more
Caused by: Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at Sun.security.validator.PKIXValidator.doBuild(PKIXValidator.Java:387)
    at Sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.Java:292)
    at Sun.security.validator.Validator.validate(Validator.Java:260)
    at Sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.Java:324)
    at Sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.Java:229)
    at Sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.Java:124)
    at Sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.Java:1351)
    ... 47 more
Caused by: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at Sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.Java:145)
    at Sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.Java:131)
    at Java.security.cert.CertPathBuilder.build(CertPathBuilder.Java:280)
    at Sun.security.validator.PKIXValidator.doBuild(PKIXValidator.Java:382)
    ... 53 more

De autres questions et blogposts J'ai vu le conseil de remplacer le HostnameVerifier par quelque chose comme:

private static final HostnameVerifier PROMISCUOUS_VERIFIER = ( s, sslSession ) -> true;

Et je l'ai mis à la fois globalement et sur le RestTemplate lui-même:

HttpsURLConnection.setDefaultHostnameVerifier( PROMISCUOUS_VERIFIER );

... et sur le RestTemplate lui-même:

final RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory( new SimpleClientHttpRequestFactory() {
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if(connection instanceof HttpsURLConnection ){
            ((HttpsURLConnection) connection).setHostnameVerifier(PROMISCUOUS_VERIFIER);
        }
        super.prepareConnection(connection, httpMethod);
    }
});

Pourtant, je reçois toujours l'erreur ci-dessus. Comment puis-je le contourner?

  1. L'installation du certificat localement en dehors de du test unitaire est pas une option comme alors, il devrait être installé manuellement sur chaque machine de développement et serveur de construction, ce qui provoquerait une avalanche de formalités administratives.
  2. Nous avons besoin de SSL puisque nous testons une bibliothèque reposant sur RestTemplate et que nous la configurons correctement.

J'utilise Java 8 (mais pourrait utiliser 7) et Spring 4.0.3.

41
Tobogganski

J'aimerais avoir encore un lien vers la source qui m'amène dans cette direction, mais c'est le code qui a fini par fonctionner pour moi. En examinant le JavaDoc pour X509TrustManager , cela ressemble à la façon dont fonctionne le TrustManagers en ne renvoyant rien après une validation réussie, sinon en lançant une exception. Ainsi, avec une implémentation nulle , elle est traitée comme une validation réussie. Ensuite, vous supprimez toutes les autres implémentations.

import javax.net.ssl.*;
import Java.security.*;
import Java.security.cert.X509Certificate;

public final class SSLUtil{

    private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
            new X509TrustManager() {
                public Java.security.cert.X509Certificate[] getAcceptedIssuers(){
                    return null;
                }
                public void checkClientTrusted( X509Certificate[] certs, String authType ){}
                public void checkServerTrusted( X509Certificate[] certs, String authType ){}
            }
        };

    public  static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
        // Install the all-trusting trust manager
        final SSLContext sc = SSLContext.getInstance("SSL");
        sc.init( null, UNQUESTIONING_TRUST_MANAGER, null );
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException {
        // Return it to the initial state (discovered by reflection, now hardcoded)
        SSLContext.getInstance("SSL").init( null, null, null );
    }

    private SSLUtil(){
        throw new UnsupportedOperationException( "Do not instantiate libraries.");
    }
}
46
Tobogganski

Par souci d'autres développeurs qui trouvent cette question et ont besoin d'une autre solution qui convient non seulement aux tests unitaires:

J'ai trouvé ceci sur un blog (pas ma solution! Crédit au propriétaire du blog).

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

Vous pouvez également enregistrer votre magasin de clés:

private void registerKeyStore(String keyStoreName) {
    try {
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream keyStoreInputStream = classLoader.getResourceAsStream(keyStoreName);
        if (keyStoreInputStream == null) {
            throw new FileNotFoundException("Could not find file named '" + keyStoreName + "' in the CLASSPATH");
        }

        //load the keystore
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(keyStoreInputStream, null);

        //add to known keystore 
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keystore);

        //default SSL connections are initialized with the keystore above
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustManagers, null);
        SSLContext.setDefault(sc);
    } catch (IOException | GeneralSecurityException e) {
        throw new RuntimeException(e);
    }
}
4
Arnaud

Voici une solution dans laquelle la vérification de la sécurité est désactivée (par exemple, converser avec localhost). En outre, certaines des solutions que je vois actuellement contiennent des méthodes obsolètes, etc.

/**
 * @param configFilePath
 * @param ipAddress
 * @param userId
 * @param password
 * @throws MalformedURLException
 */
public Upgrade(String aConfigFilePath, String ipAddress, String userId, String password) {
    configFilePath = aConfigFilePath;
    baseUri = "https://" + ipAddress + ":" + PORT + "/";

    restTemplate = new RestTemplate(createSecureTransport(userId, password, ipAddress, PORT));
    restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
    restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
 }

ClientHttpRequestFactory createSecureTransport(String username,
        String password, String Host, int port) {
    HostnameVerifier nullHostnameVerifier = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(
            new AuthScope(AuthScope.ANY_Host, AuthScope.ANY_PORT, AuthScope.ANY_REALM), credentials);

    HttpClient client = HttpClientBuilder.create()
            .setSSLHostnameVerifier(nullHostnameVerifier)
            .setSSLContext(createContext())
            .setDefaultCredentialsProvider(credentialsProvider).build();

    HttpComponentsClientHttpRequestFactory requestFactory = 
            new HttpComponentsClientHttpRequestFactory(client);

    return requestFactory;
}

private SSLContext createContext() {
    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, null);
        SSLContext.setDefault(sc);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        return sc;

    } catch (Exception e) {
    }
    return null;
}
3
Michael

Je sais que c'est trop vieux pour répondre, mais je n'ai pas pu trouver une solution comme celle-là.

Le code qui a fonctionné pour moi avec le client du maillot:

import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;

import javax.net.ssl.*;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import Java.security.KeyManagementException;
import Java.security.NoSuchAlgorithmException;
import Java.security.cert.CertificateException;

public class Testi {

static {
    disableSslVerification();
}
private static void disableSslVerification() {
    // Create all-trusting Host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    // Install the all-trusting Host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}

public Testi() {
    MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
    //... initialize headers

    Form form = new Form();
    Entity<Form> entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE);
    // initialize entity ...

    WebTarget target = getWebTarget();
    Object responseResult = target.path("api/test/path...").request()
            .headers(headers).post(entity, Object.class);

}

public static void main(String args[]) {
    new Testi();
}

private WebTarget getWebTarget() {
    ClientConfig clientConfig = new ClientConfig();
    clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 30000);
    clientConfig.property(ClientProperties.READ_TIMEOUT, 30000);

    SSLContext sc = getSSLContext();
    Client client = ClientBuilder.newBuilder().sslContext(sc).withConfig(clientConfig).build();
    WebTarget target = client.target("...url...");
    return target;
}

private SSLContext getSSLContext() {
    try {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(Java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(Java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }
        };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new Java.security.SecureRandom());
        return sc;
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
        e.printStackTrace();
    }
    return null;
}
}
0
Sohbati