joninx
joninx

Reputation: 1780

SOAP Web Service with SSL authentication in Spring Boot

I made 2 SOAP web services in java using Metro stack. To prevent undesired requests, they can only be made as long as the requester owns a client certificate. To do so, the web.xml looked like the following piece of code:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>PadronExterno</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <listener>
    <listener-class>
            com.sun.xml.ws.transport.http.servlet.WSServletContextListener
        </listener-class>
  </listener>
  <servlet>
    <servlet-name>WebServicePort</servlet-name>
    <servlet-class>
            com.sun.xml.ws.transport.http.servlet.WSServlet
        </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet>
    <servlet-name>TomcatStartupServlet</servlet-name>
    <servlet-class>com.company.TomcatStartupServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>WebServicePort</servlet-name>
    <url-pattern>/theWebService</url-pattern>
  </servlet-mapping>
  <security-constraint>
    <display-name>Constraint1</display-name>
    <web-resource-collection>
      <web-resource-name>theWebService</web-resource-name>
      <description></description>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
      <description></description>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
  <login-config>
    <auth-method>CLIENT-CERT</auth-method>
  </login-config>
</web-app>

Recently I've developed a brand-new soap ws, but I wanted to try Spring Boot. I started using Spring Initializr. The web service is entirely coded, finished, but it lacks the part of SSL authentication/authorization.

Edit:

I've come up with a possible solution, but I've something missing. this is so far what I've been able to write:

@Configuration
@EnableWebSecurity
public class SecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    private Configuracion config;
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // Add support for HSTS
        http
            .headers()
                .httpStrictTransportSecurity()
                    .includeSubDomains(true)
                    .maxAgeInSeconds(31536000);

        // Disable HTTP         
        http.httpBasic().disable();

        Integer httpPuerto = config.getHttpPuerto();
        Integer httpsPuerto = config.getHttpsPuerto();
        http
            .portMapper()
                .http(httpPuerto)
                .mapsTo(httpsPuerto);

        http.requiresChannel().anyRequest().requiresSecure();
    }
}

Unfortunately, when running into my app server (Tomcat 8.5 with SSL/TLS enabled), you can run it without owning the client certificate. As requested, here you're my Tomcat's connector config:

<Connector 
    connectionTimeout="20000" 
    port="9090" 
    protocol="HTTP/1.1" 
    redirectPort="9443"/>
<Connector 
    SSLEnabled="true" 
    keystorePass="***d" 
    keystoreType="JKS" 
    maxThreads="200" 
    port="9443" 
    protocol="org.apache.coyote.http11.Http11Nio2Protocol" 
    scheme="https" 
    secure="true" 
    sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation" 
    sslProtocol="TLSv1.2"
    clientAuth="want" 
    keystoreFile="D:\apache\Tomcat8.5\certs\tomcat.jks" 
<Connector port="9009" protocol="AJP/1.3" redirectPort="9443"/>
    ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_RC4_128_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA" />

Am I missing something within my WebSecurityConfigurerAdapter? Thank you

Upvotes: 0

Views: 11792

Answers (1)

Sergio Otero Lopez
Sergio Otero Lopez

Reputation: 282

You first say that you have a working example without Spring, but looking at the Tomcat configuration it seems that is missing some parts (for example truststorefile with the client certificate authority)

Follow the steps provided in here Mutual authentication with Tomcat 7

For example to generate a server and client certificate:

keytool -genkeypair -alias tomcat -keyalg RSA -dname "CN=tomcat.com" -keystore tomcat.keystore -keypass tomcat -storepass tomcat

keytool -genkeypair -alias user -keyalg RSA -dname "CN=user" -keypass usertomcat -keystore client.keystore -storepass usertomcat

keytool -exportcert -rfc -alias user -file client.cer -keypass usertomcat -keystore client.keystore -storepass usertomcat

keytool -importcert -alias user -file client.cer -keystore tomcat.keystore -storepass tomcat -noprompt

keytool -importkeystore -srckeystore client.keystore -destkeystore client.p12 -deststoretype PKCS12 -srcalias user -deststorepass usertomcat -destkeypass usertomcat

And then configure server.xml:

<!-- remove AprLifecycleListener!! -->
<Connector port="9443"
maxThreads="150"
scheme="https"
secure="true"
SSLEnabled="true"
truststoreFile="/path-to/tomcat.keystore"
truststorePass="tomcat"
keystoreFile="/path-to/tomcat.keystore"
keystorePass="tomcat"
clientAuth="true"
keyAlias="tomcat"
sslProtocol="TLS"/> 

With Tomcat configured and the client certificate in your browser, it should ask for it, but the app will fail with a HTTP403 error because your spring configuration requires HTTPS but it doesn't specify that it needs client certification (as you are doing in web.xml without spring).

Yo need to specify in SecurityConfigurerAdapter:

http.x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)");

And it should finally work.

If you want to execute it as a standalone spring-boot outside Tomcat, you should also configure in application.properties

server.port: 8443
server.ssl.key-store: tomcat.keystore
server.ssl.key-store-password: tomcat
server.ssl.keyStoreType: JKS
server.ssl.keyAlias: tomcat
server.ssl.trust-store=tomcat.keystore
server.ssl.trust-store-password=tomcat
server.ssl.client-auth:need

Edit: The truststore in Tomcat should contain only the root certificate used by clients, not the actual client certificate

Upvotes: 2

Related Questions