OTUser
OTUser

Reputation: 3848

Invoking HTTPS SOAP webservice in Spring Boot application

This is is my first assignment on spring boot application and I am trying to consume HTTPS SOAP webservice but its failing with handshake_failure

I have spring boot application with below server setting in application.yml

server:
  #address:
  port: 8443
  sessionTimeout: 30
  ssl:
    client-auth: need
    key-store: keystore.jks
    key-store-password: 123456
    key-alias: host
    key-password: 123456
    protocol: TLS
    trust-store: truststore.jks
    trust-store-password: 123456

Environment Info:

springBootVersion = '1.5.2.RELEASE'
Gradle 3.4
Build time:   2017-02-20 14:49:26 UTC
Revision:     73f32d68824582945f5ac1810600e8d87794c3d4

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_40 (Oracle Corporation 25.40-b25)
OS:           Windows 7 6.1 amd64

And my keystore.jks has 2 pfx entries 1 is host.pfx(alias host) with server.cer and server.key (which is used for service URL https://localhost:8443) and the other one is digsig.pfx(alias trustweaver) for authentication with SOAP webservice, both pfx's inside keystore.jks have same password as 123456 and the truststore.jks has the root CAs

When am trying to invoke the SOAP java stub which eventually call the web service(requires client certificate authentication which is part of keystore.jks) am getting handshake_failure with below stack trace.

2017-04-17 18:18:31.155 ERROR 20440 --- [io-8443-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.xml.ws.WebServiceException: Failed to access the WSDL at: https://tseiod-test.trustweaver.com/ts/svs.asmx?wsdl. It failed with: 
Received fatal alert: handshake_failure.] with root cause

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2011)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1113)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
    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.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1512)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at java.net.URL.openStream(URL.java:1038)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.createReader(RuntimeWSDLParser.java:984)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.resolveWSDL(RuntimeWSDLParser.java:385)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:216)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:194)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:163)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:348)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:306)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:215)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:196)
    at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:192)
    at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:104)
    at javax.xml.ws.Service.<init>(Service.java:77)
    at com.api.trustweaver.SwitchService.<init>(SwitchService.java:48)

Looks the issue is spring boot app is not using the trustweaver alias from keystore.jks for webservice authentication(or something else which am not sure), can someone help me to fix this issue?

Upvotes: 1

Views: 6694

Answers (1)

OTUser
OTUser

Reputation: 3848

Created custom SSL context with keystore and truststore and loading it during the application starting time as below

Below Changes in Main method

public static void main(String[] args) {
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation","true"); // for non prod systems
    SpringApplication.run(Application.class, args);
}
@Bean
CommandLineRunner init() {
    def customHttpsSSLConfig = new CustomHttpsSSLConfig();
    customHttpsSSLConfig.init(instance,hostnameVerification,trustweaverKeystore, trustweaverKeystoreType, trustweaverKeystorePassword, trustweaverCATrustStore, trustweaverCATrustStoreType, trustweaverCATrustStorePassword);
}

Below is my CustomHttpsSSLConfig class

import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.stream.Stream;

public class CustomHttpsSSLConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomHttpsSSLConfig.class);
    private static final String PROD_INSTANCE = "prod";
    private static final String STG_INSTANCE1 = "staging";
    private static final String STG_INSTANCE2 = "stg";
    private static final String QA_INSTANCE   = "qa";
    private static final String DEV_INSTANCE  = "dev";
    public static void init(String instance, String hostnameVerification, String trustweaverKeystore, String trustweaverKeystoreType, String trustweaverKeystorePassword, String trustweaverCATrustStore, String trustweaverCATrustStoreType, String trustweaverCATrustStorePassword) {

        // Install the all-trusting trust manager
        try {
            // Client keystore
            KeyStore keyStore = KeyStore.getInstance(trustweaverKeystoreType);
            keyStore.load(new FileInputStream(trustweaverKeystore), trustweaverKeystorePassword.toCharArray());

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keyStore, trustweaverKeystorePassword.toCharArray());

            // Trusted CA keystore
            KeyStore truststore = KeyStore.getInstance(trustweaverCATrustStoreType);
            truststore.load(new FileInputStream(trustweaverCATrustStore), trustweaverCATrustStorePassword.toCharArray());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(truststore);

            SSLContext sc = SSLContext.getInstance("TLSv1.2");
            sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(getHostnameVerifierForInstance(instance,hostnameVerification));
            LOGGER.info("Initialized CustomHttpsSSLConfig with keyStore = {}, truststore = {}",trustweaverKeystore,trustweaverCATrustStore);
        } catch (Exception e) {
            LOGGER.error("CustomHttpsSSLConfig Initialization failed with keyStore = {}, truststore = {}, exception = {}, stacktrace = {} ",trustweaverKeystore,trustweaverCATrustStore,e.getMessage(),e.getStackTrace());
        }
    }

    private static HostnameVerifier getHostnameVerifierForInstance(String instance,String enabled){
        return Boolean.parseBoolean(enabled)&&reject(instance) ? customHostnameVerifier() : permitAll();
    }

    private static boolean reject(String instance) {
        return rejections().anyMatch(instance.trim().toLowerCase()::startsWith);
    }

    private static Stream<String> rejections() {
        return Stream.of(PROD_INSTANCE, STG_INSTANCE1,STG_INSTANCE2, QA_INSTANCE, DEV_INSTANCE).map(String::toLowerCase);
    }

    private static HostnameVerifier customHostnameVerifier() {
         return new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
                return verifier.verify(hostname, session);
            }
        };
    }

    private static HostnameVerifier permitAll() {
        return (hostname, session) -> true;
    }
}

Upvotes: 1

Related Questions