Can I reuse Java trusted certificates for javax.ws.rs (implementation of Resteasy)?

Suppose we need to trust a self-signed SSL certificate. An example is https://self-signed.badssl.com/ .

Since the signer is not an โ€œappropriateโ€ authority, Java does not trust him and refuses to connect to this server. However after

$ cd $JAVA_HOME/jre/lib/security $ keytool -import -trustcacerts -alias ... -file ... -keystore cacerts 

and restarting the application, the following code works:

 new URL ("https://self-signed.badssl.com/").openConnection ().getResponseCode () 

and returns 200 (OK) without throwing an exception. That is, the basic Java method for opening an HTTPS connection is now available, since the certificate is now trusted.

However, this has no visible effect on the javax.ws.rs Client (as implemented in Resteasy, at least), and I still get the exception:

 javax.ws.rs.ProcessingException: Unable to invoke request at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:287) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:407) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.method(ClientInvocationBuilder.java:273) [...] 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:1949) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) at sun.security.ssl.Handshaker.process_record(Handshaker.java:914) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:535) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:403) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177) at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:304) at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:446) at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57) at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:283) ... 90 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:1488) ... 107 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:146) 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) ... 113 more 

Resteasy seems to disregard the โ€œstandardโ€ keystore. But I would prefer to have a central (machine) location for additional trusted keys and not worry about how the application uses them using URL.openConnection or javax.ws.rs.

Question Is it possible to make javax.ws.rs Client use the same keystore as the โ€œnormalโ€ Java HTTPS mechanism?

+6
source share
1 answer

Configure SSL context when creating a Client instance

There is a method in the ClientBuilder API that allows you to set the SSLContext :

public abstract ClientBuilder sslContext(SSLContext sslContext)

Specify the SSL context that will be used when creating secure transport connections to the server endpoints from the web targets created by the client instance that uses this SSL context. It is expected that in the context of SSL, the entire security infrastructure, including key and trust managers, will be initialized.

Setting up an SSL context instance resets all previously stored values โ€‹โ€‹of the keystore or trust store.

Parameters:

SSLContext is an implementation of a secure socket protocol that acts as a factory for secure socket factories or SSL mechanisms. Must not be null .

Return:

Updated client client instance.

Throws:

NullPointerException - if the SSLContext parameter is null .

Assuming you have added a certificate to the cacerts trust store, you can use the default SSLContext when creating the Client instance.

 Client client = ClientBuilder.newBuilder().sslContext(SSLContext.getDefault()).build(); 

That should be enough. However, for some reason, the above code snippet does not work with RESTEasy, but works with Jersey. This is probably a RESTEasy error.

The standard solution does not work with RESTEasy. What should I do?

The RESTEasy documentation states the following:

Network communication between the client and server is handled in RESTEasy by default using the HttpClient (4.x) from the Apache HttpComponents project. [...]

RESTEasy and HttpClient make smart decisions by default, so you can use the client infrastructure without referring to HttpClient , but for some applications you may need to talk about the details of HttpClient . [...]

To configure the HttpClient used by RESTEeasy, follow these steps:

 HttpClient httpClient = HttpClientBuilder.create() .setSslcontext(SSLContext.getDefault()) .build(); ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient); Client client = new ResteasyClientBuilder().httpEngine(engine).build(); 

Then you can execute the query:

 Response response = client.target("https://self-signed.badssl.com/").request().get(); System.out.println(response.getStatus()); 

Are there alternatives to the SSL context?

Instead of using SSLContext when creating Client , you can download KeyStore . To load the cacerts trust store, you can do the following:

 String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar); FileInputStream is = new FileInputStream(filename); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); String password = "changeit"; keystore.load(is, password.toCharArray()); 

Password cacerts ' default changeit .

Then create an instance of Client using one of the following methods:

 Client client = ClientBuilder.newBuilder().trustStore(keystore).build(); 
 Client client = ClientBuilder.newBuilder().keyStore(keystore, password).build(); 

The problem is that it does not work with RESTEasy, but it works with Jersey.


The above solutions have been tested against the following JAX-RS API implementations:

+3
source

All Articles