开发者

Ignore self-signed ssl cert using Jersey Client [duplicate]

This question already has answers here: Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error? (33 answers) Closed 2 years ago.

I'm using the Jersey Client library to run tests against a rest service running on jboss. I have https set up fine on the server (running on localhost), using a self signed cert.

However whenever I run my tests with the https url I get the following error:

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

I know this is because my self signed cert is not in the java keystore. Is there any way I can make the Client not check the validity of the ssl cert and just use it regardless?

This code will only ever be run against test servers so I don't want to go to the hassle of adding new trusted certs each time we set up a new test server.

Here's the code which is making the call:

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.getPropert开发者_运维知识库y("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);

Any help would be greatly appreciated.


After some searching and trawling through some old stackoverflow questions I've found a solution in a previously asked SO question:

  • Question: Java client certificates over HTTPS/SSL
  • Answer Java client certificates over HTTPS/SSL

Here's the code that I ended up using.

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


For Jersey 2.* (Tested on 2.7) and 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();
}


I had the same problem adn did not want this to be set globally, so I used the same TrustManager and SSLContext code as above, I just changed the Client to be created with special properties

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


Since I am new to stackoverflow and have lesser reputation to comment on others' answers, I am putting the solution suggested by Chris Salij with some modification which worked for me.

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


This code will only ever be run against test servers so I don't want to go to the hassle of adding new trusted certs each time we set up a new test server.

This is the kind of code that will eventually find its way in production (if not from you, someone else who's reading this question will copy and paste the insecure trust managers that have been suggested into their applications). It's just so easy to forget to remove this sort of code when you have a deadline, since it doesn't show up as a problem.

If you're worried about having to add new certificates every time you have a test server, create your own little CA, issue all the certificates for the test servers using that CA and import this CA certificate into your client trust store. (Even if you don't deal with things like online certificate revocation in a local environment, this is certainly better than using a trust manager that lets anything through.)

There are tools to help you do this, for example TinyCA or XCA.


For anyone on Jersey 2.x without lambdas, use this:

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

Tested with jersey-client 2.11 on JRE 1.7.


For 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


I noticed that when using the Apache http client configuration with a pooling manager, the accepted answer doesn't work.

In this case it appears that the ClientConfig.sslContext and ClientConfig.hostnameVerifier setters are silently ignored. So if you are using connection pooling with the apache client http client config, you should be able to use the following code to get ssl verification to be ignored:

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


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


Okay, I'd like to just add my class only because there might be some dev out there in the future that wants to connect to a Netbackup server (or something similar) and do stuff from Java while ignoring the SSL cert. This worked for me and we use windows active directory for auth to the Netbackup server.

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

I know it can be considered off-topic, but I bet the dev that tries to connect to a Netbackup server will probably end up here. By the way many thanks to all the answers in this question! The spec I'm talking about is here and their code samples are (currently) missing a Java example.

***This is of course unsafe since we ignore the cert!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜