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