Implementing 2-way SSL with spring boot

I create some soothing web services and use Spring-Boot to create the tomcat built-in container.

One of the requirements is that it implements 2-way SSL. I looked at the HttpSecurity object and can only get it to run web services through the SSL channel using this: -

@Override protected void configure(HttpSecurity http) throws Exception { System.out.println("CONFIGURED"); http // ... .requiresChannel() .anyRequest().requiresSecure(); } 

What I cannot find is a way to make the web service available only to applications that provide a valid client certificate.

I only have basic SSL knowledge, so even a generic pointer in the right direction will be appreciated.

Many applications will be created on the server on which the deployment is being performed - this is the only one that needs to be blocked using two-way SSL. What I'm really looking for is a way to block one application in order to accept client certificates only.

+7
java spring-boot spring-security ssl tomcat8
source share
2 answers

You can configure clientAuth=want , see Apache Tomcat 8 configuration link :

Set to true if you want the SSL stack to require the client to have the correct certificate chain before accepting the connection. Set the value to want if you want the SSL stack to request a client certificate, but does not break if it is not present. false (which is the default) does not require a certificate chain if the client does not request a resource protected by a security restriction that uses the CLIENT-CERT check.

and then read the client certificate using Spring Security - X.509 Authentication :

You can also use SSL with "mutual authentication"; the server will request a valid certificate from the client as part of the SSL handshake. The server will authenticate the client by verifying that its certificate is signed with an acceptable credential. If a valid certificate is provided, it can be obtained through the servlet API in the application. Spring X.509 Security Module retrieves the certificate using a filter. It maps the certificate to the application user and downloads the set of installed privileges for use with the standard Spring security infrastructure.

and

clientAuth can also be set to want if you still want SSL connections to be successful, even if the client did not provide a certificate. Clients who do not submit a certificate will not be able to access any objects protected by Spring Security unless you use an authentication mechanism other than X.509, such as form authentication.

+4
source share

I ran into a similar problem and thought Id was using the solution I came up with.

First, you need to understand that SSL certificate authentication will be processed on the side of your web server (cfr. Durs explanation, with the setting "clientAuth = want"). Then your web application should be configured to process the provided (and allowed) certificate, matching it with the user, etc.

The little difference I have is that Im packs my spring boot application into a WAR archive, which is then deployed to an existing Tomcat application server.

The My Tomcats server.xml configuration file defines the HTTPS connector as follows:

 <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" keystoreFile="/opt/tomcat/conf/key-stores/ssl-keystore.jks" keystorePass="some-complex-password" clientAuth="want" sslProtocol="TLS" truststoreFile="/opt/tomcat/conf/trust-stores/ssl-truststore.jks" truststorePass="some-other-complex-password" /> 

A small comment to avoid confusion: keystoreFile contains a pair of certificates / private keys used for SSL (only), and truststoreFile contains allowed CA certificates for SSL authentication for clients (note that you can also add client certificates directly to this trust - store).

If you use the tomcat built-in container with your spring boot application, you can configure these parameters in your application properties file using the following property key / values:

 server.ssl.key-store=/opt/tomcat/conf/key-stores/ssl-keystore.jks server.ssl.key-store-password=some-complex-password server.ssl.trust-store=/opt/tomcat/conf/trust-stores/ssl-truststore.jks server.ssl.trust-store-password=some-other-complex-password server.ssl.client-auth=want 

Then, in my web application, I declare a specific SSL configuration as follows:

 @Configuration @EnableWebSecurity //In order to use @PreAuthorise() annotations later on... @EnableGlobalMethodSecurity(prePostEnabled = true) public class SSLAuthConfiguration extends WebSecurityConfigurerAdapter { @Value("${allowed.user}") private String ALLOWED_USER; @Value("${server.ssl.client.regex}") private String CN_REGEX; @Autowired private UserDetailsService userDetailsService; @Override protected void configure (final HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/url-path-to-protect").authenticated() //Specify the URL path(s) requiring authentication... .and() .x509() //... and that x509 authentication is enabled .subjectPrincipalRegex(CN_REGEX) .userDetailsService(userDetailsService); } @Autowired //Simplified case, where the application has only one user... public void configureGlobal (final AuthenticationManagerBuilder auth) throws Exception { //... whose username is defined in the application properties. auth .inMemoryAuthentication() .withUser(ALLOWED_USER).password("").roles("SSL_USER"); } 

}

Then I need to declare a UserDetailsService bean (for example, in my main application class):

 @Value("${allowed.user}") private String ALLOWED_USER; @Bean public UserDetailsService userDetailsService () { return new UserDetailsService() { @Override public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { if (username.equals(ALLOWED_USER)) { final User user = new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER")); return user; } return null; } }; } 

And here it is! Then I can add @PreAuthorize ("hasRole ('ROLE_SSL_USER") annotations to the methods that I want to protect.

To summarize, the authentication flow will look like this:

  • The user provides an SSL certificate;
  • Tomcat checks its trust store;
  • The custom WebSecurityConfigurerAdapter extracts the "username" from the CN certificates;
  • The application authenticates the user associated with the received username;
  • At the method level, if annotated with @PreAuthorize ("hasRole ('SSL_USER')"), the application checks to see if the user has the required role.
+5
source share

All Articles