0

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

joninx
  • 1,775
  • 6
  • 31
  • 59

1 Answers1

2

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

  • I thought my local configuration of Tomcat was enough to make tests; but I gotta say that we've a production server whose configuration is unknown to me (maintained by another company). What you are providing me to be used within `SecurityConfigurerAdapter` was tested by me, but perhaps wasn't working OK due to a misconfiguration of Tomcat... You are using the same file (tomcat.keystore) to be used as keystore and truststore, why? – joninx May 29 '17 at 06:58
  • Only to simplify test. You should put in the trust store the root authority you want to admit – Sergio Otero Lopez May 29 '17 at 07:06
  • Thanks for your help, this is what I was looking for. Despite the fact that my code wasn't too bad, the lack of configuration within Tomcat was preventing me from achieving my goal. Thank you again @Sergio Otero Lopez – joninx May 29 '17 at 07:49