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?
RestTemplate
et que nous la configurons correctement.J'utilise Java 8 (mais pourrait utiliser 7) et Spring 4.0.3.
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 TrustManager
s 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.");
}
}
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);
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);
}
}
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;
}
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;
}
}