Kumar
Kumar

Reputation: 1114

ApacheConnectorProvider with Jersey for SSL throws Error

I am trying to post a service call using the SSL URL. I am getting an exception.

javax.ws.rs.ProcessingException: 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.glassfish.jersey.apache.connector.ApacheConnector.apply(ApacheConnector.java:517)
    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:246)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:667)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:664)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315).

I am using Spring, Jersey and trying to use ApacheClient(Reason to use, On Weblogic it is taking Weblogic specific HTTP handler, I know we can use "DUseSunHttpHandler=true" but I don't want to do that on production).

Please note, with only Jersey implementation it worked with http on both Tomcat and Weblogic. But with HTTPS it worked in Tomcat not in Weblogic. So went for ApacheConnectionProvider, from there it's not working in Tomcat as well.

POM entry

<dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>2.15</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-common</artifactId>
        <version>2.15</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>javax.ws.rs-api</artifactId>
        <version>2.0.1</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.4</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.connectors</groupId>
        <artifactId>jersey-apache-connector</artifactId>
        <version>2.15</version>
    </dependency>

Code

public Client getClient() {
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingHttpClientConnectionManager());
        //config your ssl for apache connector
        SslConfigurator sslConfig = SslConfigurator.newInstance();

        String trustStoreFile = "C:\\Development\\svn\\ecomm-webapp\\profiles\\uat\\workflowtrust.jks";
        String trustStorePassword ="ABC12";
        String keyStoreFile = "C:\\Development\\svn\\ecomm-webapp\\profiles\\uat\\workflow.jks";
        String keyPassword ="abc12";
        sslConfig.trustStoreFile(trustStoreFile).keyStoreFile(keyStoreFile).keyStorePassword(trustStorePassword).trustStorePassword(trustStorePassword).securityProtocol("SSL");
        clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);
        ApacheConnectorProvider connector = new ApacheConnectorProvider();
        clientConfig.connectorProvider(connector);

        return  ClientBuilder.newClient(clientConfig);
    }

Calling Code

public <T> T postXmlFile(final File inputXml, final String targetUrl, Class<T> resultResponse, final RestfulServiceVerifier<T> restfulServiceVerifier) throws FunctionalException {
        return post(MediaType.APPLICATION_XML_TYPE, MediaType.TEXT_XML_TYPE, inputXml, targetUrl, resultResponse, restfulServiceVerifier);
    }

    private <T> T post(final MediaType type, final MediaType accept, final Object entity, final String targetUrl, Class<T> resultResponse, final RestfulServiceVerifier<T> restfulServiceVerifier) throws FunctionalException {
        Response response = null;
        int responseStatus = -1;
        T result = null;
        while (restfulServiceVerifier.hasNextWorkFlowHit()) {
            try {
                response = restFulWebTargetFactory.getClient().target(targetUrl)
                        .request(type)
                        .post(Entity.entity(entity, accept));

                responseStatus = response.getStatus();
                if(EcommConstants.WORKFLOW_ORDER_PROPOSAL_SUCCESS_CODE == responseStatus) {
                    final String resultInString =  response.readEntity(String.class);
                    //for audit purpose
                    if (LOG_XML_MESSAGE) {
                        log.info("XML Response "+ resultInString);
                    }
                    result = unMarshalXML(resultInString, resultResponse);
                    restfulServiceVerifier.checkResponse(result, responseStatus);
                }

            } catch (WorkFlowRetryException workFlowException) {
                throw new FunctionalException("WebService post failed. ", workFlowException);
            }catch (WorkFlowValidationException workFlowValidationException) {
                log.error("Service Response Validation Exception " + workFlowValidationException.getErrorCode() + " Error Description " + workFlowValidationException.getDescription(), workFlowValidationException);
            }catch (final Exception e) {
                log.error("Exception occurred" , e);
            } finally {
                if(null != response) {
                    response.close();
                }
            }
        }
        if(-1 == responseStatus) {
            throw new FunctionalException("WebService post failed. ", new Exception());
        }
        return result;
    }

Upvotes: 2

Views: 3192

Answers (1)

Kumar
Kumar

Reputation: 1114

I fixed it, please find the solution. I changed getClient() method.

  1. I created org.apache.http.conn.ssl.DefaultHostnameVerifier. Here is the code snippet I changed.DefaultHostnameVerifier (I just need default one)
  2. Configured SslConfigurator with Trust Store and Key profile
  3. Created the LayeredConnectionSocketFactory and injected SSLContext from SslConfigurator and defaultHostnameVerifier
  4. Created a Registry for http and https
  5. Create ClientConfig to set Connection Manager with PoolingHttpClientConnectionManager with injected registry
  6. And set the SSL Config to Client Config
  7. Created ApacheConnectorProvider and set to clientConfig

Code Sample

private SslConfigurator createSSLContext() {
        return SslConfigurator.newInstance()
                .trustStoreFile(trustStoreFile)
                .trustStorePassword(trustStorePassword)
                .keyStoreFile(keyStoreFile)
                .keyPassword(keyPassword).securityProtocol("SSL");
    }


    @Override
    public Client getClient() {

        HostnameVerifier defaultHostnameVerifier=new DefaultHostnameVerifier();
        SslConfigurator sslConfig = createSSLContext();
        LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslConfig.createSSLContext(),
                defaultHostnameVerifier);

        final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingHttpClientConnectionManager(registry));

        clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);

        ApacheConnectorProvider connector = new ApacheConnectorProvider();
        clientConfig.connectorProvider(connector);
        return ClientBuilder.newBuilder().withConfig(clientConfig).build();
    }

Upvotes: 4

Related Questions