Reputation: 3848
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
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