J'utilise la bibliothèque du client Jersey pour exécuter des tests sur un service de repos s'exécutant sur jboss . Le protocole https est configuré sur le serveur (s'exécutant sur localhost), à l'aide d'un certificat auto-signé.
Cependant, chaque fois que je lance mes tests avec l’URL https, j’obtiens l’erreur suivante:
com.Sun.jersey.api.client.ClientHandlerException: 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 com.Sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.Java:131)
at com.Sun.jersey.api.client.Client.handle(Client.Java:629)
at com.Sun.jersey.oauth.client.OAuthClientFilter.handle(OAuthClientFilter.Java:137)
at com.Sun.jersey.api.client.WebResource.handle(WebResource.Java:601)
at com.Sun.jersey.api.client.WebResource.access$200(WebResource.Java:74)
at com.Sun.jersey.api.client.WebResource$Builder.get(WebResource.Java:459)
at test.helper.Helper.sendSignedRequest(Helper.Java:174)
... And so on
Je sais que cela est dû au fait que mon certificat auto-signé n'est pas dans le magasin de clés Java. Est-il possible de faire en sorte que la Client
ne vérifie pas la validité du certificat SSL et ne l'utilise que quand même?
Ce code ne sera jamais exécuté que sur des serveurs de test. Par conséquent, je ne veux pas avoir à ajouter de nouveaux certificats de confiance à chaque fois que nous configurons un nouveau serveur de test.
Voici le code qui effectue l'appel:
OAuthParameters params = new OAuthParameters();
// baseline OAuth parameters for access to resource
params.signatureMethod(props.getProperty("signature_method"));
params.consumerKey(props.getProperty("consumer_key"));
params.setToken(props.getProperty("token"));
params.setVersion("1.0");
params.nonce();
// OAuth secrets to access resource
OAuthSecrets secrets = new OAuthSecrets();
secrets.consumerSecret(props.getProperty("consumer_secret"));
secrets.setTokenSecret(props.getProperty("token_secret"));
// Jersey client to make REST calls to token services
Client client = Client.create();
// OAuth test server resource
WebResource resource = client.resource(props.getProperty("url"));
// if parameters and secrets remain static, filter cab be added to each web resource
OAuthClientFilter filter = new OAuthClientFilter(client.getProviders(), params, secrets);
// filter added at the web resource level
resource.addFilter(filter);
WebResource.Builder wbr = resource.getRequestBuilder().accept(props.getProperty("accept"));
return wbr.get(ClientResponse.class);
Toute aide serait grandement appréciée.
Après quelques recherches et réponses à des questions anciennes, j'ai trouvé une solution à une question SO précédemment posée:
Voici le code que j'ai fini par utiliser.
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType){}
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
;
}
Pour Jersey 2. * (testé sur 2.7) et Java 8:
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public static Client ignoreSSLClient() throws Exception {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new Java.security.SecureRandom());
return ClientBuilder.newBuilder()
.sslContext(sslcontext)
.hostnameVerifier((s1, s2) -> true)
.build();
}
J'ai eu le même problème et je ne voulais pas que cela soit réglé globalement. J'ai donc utilisé les mêmes codes TrustManager et SSLContext que ci-dessus, j'ai simplement modifié le client pour qu'il soit créé avec des propriétés spéciales.
ClientConfig config = new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(
new HostnameVerifier() {
@Override
public boolean verify( String s, SSLSession sslSession ) {
// whatever your matching policy states
}
}
));
Client client = Client.create(config);
Ce code ne sera jamais exécuté que sur des serveurs de test, donc je ne veux pas aller à la difficulté d'ajouter de nouveaux certificats de confiance à chaque fois que nous configurons un fichier nouveau serveur de test.
C'est le genre de code qui finira par se retrouver dans la production (sinon, quelqu'un qui lira cette question copiera et collera les gestionnaires de confiance non sécurisés suggérés dans leurs applications). C'est tellement facile d'oublier de supprimer ce type de code lorsque vous avez une date limite, car cela ne semble pas être un problème.
Si vous craignez de devoir ajouter de nouveaux certificats chaque fois que vous avez un serveur de test, créez votre propre petite autorité de certification, émettez tous les certificats pour les serveurs de test utilisant cette autorité de certification et importez ce certificat de l'autorité de certification dans votre magasin de données de confiance client. (Même si vous ne vous occupez pas de choses telles que la révocation de certificats en ligne dans un environnement local, cela vaut certainement mieux que d'utiliser un gestionnaire de confiance qui laisse passer quoi que ce soit.)
Il existe des outils pour vous aider à faire cela, par exemple TinyCA ou XCA .
Puisque je suis nouveau sur stackoverflow et que j'ai moins de réputation pour commenter les réponses des autres, je mets en place la solution suggérée par Chris Salij avec quelques modifications qui ont fonctionné pour moi.
SSLContext ctx = null;
TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType){}
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
try {
ctx = SSLContext.getInstance("SSL");
ctx.init(null, trustAllCerts, null);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
LOGGER.info("Error loading ssl context {}", e.getMessage());
}
SSLContext.setDefault(ctx);
Pour ceux qui utilisent Jersey 2.x sans lambdas, utilisez ceci:
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
public static Client getUnsecureClient() throws Exception
{
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{new X509TrustManager()
{
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException{}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException{}
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}}, new Java.security.SecureRandom());
HostnameVerifier allowAll = new HostnameVerifier()
{
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier(allowAll).build();
}
Testé avec jersey-client 2.11 on JRE 1.7 .
Il suffit d'ajouter le même code avec les importations. Contient également le code non implémenté nécessaire à la compilation. J'ai d'abord eu du mal à trouver ce qui était importé pour ce code. Ajoutant également le bon package pour le X509Certificate. Vous avez ce travail avec essais et erreurs:
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;
import javax.ws.rs.core.MultivaluedMap;
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
Java.security.cert.X509Certificate[] chck = null;
;
return chck;
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public void checkClientTrusted(
Java.security.cert.X509Certificate[] arg0, String arg1)
throws Java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
public void checkServerTrusted(
Java.security.cert.X509Certificate[] arg0, String arg1)
throws Java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
;
}
Pour Jersey 2. *:
Client client = ClientBuilder.newBuilder()
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();
-> https://jersey.Java.net/documentation/latest/migration.html
Pour Jersey 1.X
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(Java.security.cert.X509Certificate[] chain, String authType) throws Java.security.cert.CertificateException {}
public void checkServerTrusted(Java.security.cert.X509Certificate[] chain, String authType) throws Java.security.cert.CertificateException {}
public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
// or you can return null too
return new Java.security.cert.X509Certificate[0];
}
}};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String string, SSLSession sslSession) {
return true;
}
});
D'accord, j'aimerais juste ajouter ma classe uniquement parce qu'il pourrait y avoir à l'avenir un développeur qui souhaite se connecter à un serveur Netbackup (ou quelque chose de similaire) et faire des choses à partir de Java en ignorant le certificat SSL. Cela a fonctionné pour moi et nous utilisons Windows Active Directory pour autoriser le serveur Netbackup.
public static void main(String[] args) throws Exception {
SSLContext sslcontext = null;
try {
sslcontext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
try {
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}}, new Java.security.SecureRandom());
} catch (KeyManagementException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
//HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().credentials(username, password).build();
ClientConfig clientConfig = new ClientConfig();
//clientConfig.register(feature);
Client client = ClientBuilder.newBuilder().withConfig(clientConfig)
.sslContext(sslcontext)
.hostnameVerifier((s1, s2) -> true)
.build();
//String the_url = "https://the_server:1556/netbackup/security/cacert";
String the_token;
{
String the_url = "https://the_server:1556/netbackup/login";
WebTarget webTarget = client.target(the_url);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
String jsonString = new JSONObject()
.put("domainType", "NT")
.put("domainName", "XX")
.put("userName", "the username")
.put("password", "the password").toString();
System.out.println(jsonString);
Response response = invocationBuilder.post(Entity.json(jsonString));
String data = response.readEntity(String.class);
JSONObject jo = new JSONObject(data);
the_token = jo.getString("token");
System.out.println("token is:" + the_token);
}
{
String the_url = "https://the_server:1556/netbackup/admin/jobs/1122012"; //job id 1122012 is an example
WebTarget webTarget = client.target(the_url);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, the_token).header(HttpHeaders.ACCEPT, "application/vnd.netbackup+json;version=1.0");
Response response = invocationBuilder.get();
System.out.println("response status:" + response.getStatus());
String data = response.readEntity(String.class);
//JSONObject jo = new JSONObject(data);
System.out.println(data);
}
}
Je sais que cela peut être considéré comme hors sujet, mais je parie que le développeur qui tente de se connecter à un serveur Netbackup finira probablement ici. En passant, merci beaucoup à toutes les réponses à cette question! La spécification dont je parle est ici et leurs exemples de code manquent (actuellement) un exemple Java.
*** Ceci est bien sûr dangereux car nous ignorons le cert!
J'ai remarqué que lors de l'utilisation de la configuration du client Apache http avec un gestionnaire de pooling, la réponse acceptée ne fonctionnait pas.
Dans ce cas, il semble que les paramètres ClientConfig.sslContext
et ClientConfig.hostnameVerifier
sont ignorés en silence. Donc, si vous utilisez le regroupement de connexions avec la configuration du client http du client Apache, vous devriez pouvoir utiliser le code suivant pour ignorer la vérification SSL:
ClientConfig clientConfig = new ClientConfig();
// ... configure your clientConfig
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
}
}, null);
} catch (NoSuchAlgorithmException e) {
//logger.debug("Ignoring 'NoSuchAlgorithmException' while ignoring ssl certificate validation.");
} catch (KeyManagementException e) {
//logger.debug("Ignoring 'KeyManagementException' while ignoring ssl certificate validation.");
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(sslContext, new AbstractVerifier() {
@Override
public void verify(String Host, String[] cns, String[] subjectAlts) {
}
}))
.build();
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
return ClientBuilder.newClient(clientConfig);