Spring REST Service Certificate auth

I created a 100% comprehensible example project with everything needed to setup a Spring Boot app with a REST endpoint that is secured by client certificate - and a Testcase with the RestTemplate that is configured to use the client certificate to communicate with the secured Server: https://github.com/jonashackt/spring-boot-rest-clientcertificate

It also contains all steps needed to generate the .key, .crt and .jks files. Just adjust the steps accordingly, if you don´t want to use a self-signed certificate.

The RestTemplate is configured like this:

package de.jonashackt.restexamples;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;

@Configuration
public class RestClientCertTestConfiguration {

    private String allPassword = "allpassword";

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {

        SSLContext sslContext = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray())
                .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray())
                .build();

        HttpClient client = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        return builder
                .requestFactory(new HttpComponentsClientHttpRequestFactory(client))
                .build();
    }
}

Then you can use it just like you´re used to with the @Autowired annotation inside your Test.class.


What you are looking for is called Mutual Authentication.

It is the servers responsibility to make/request the client to send its certificate. Each server does this differently and you'll have to look up how to configure your particular server.

For Spring Security, I would recommend looking into X.509 Authentication. This type of authentication is fairly easy to use and extend as needed.

EDIT

So, here are a couple of references that show examples of what you are asking:

http://whiteycode.blogspot.com/2012/04/part-3-x509-authentication-with-spring.html

PDF Warning

http://www.promixis.com/pdfs/SpringSecurityAndX509ClientCertificates.pdf

The above pdf file is no longer reachable...

This example is really good about explaining how to setup your certificates and creating your own personal CA (Certificate Authority). Warning, the way that they show making the client certificate is just A WAY, not the way. Your client (IE web browser or java httpclient client) should determine which way to create your client certificate. Java likes to use its java keystore of course and browsers tend to like the p12 style of certificates.

Final advice/warning... I don't know your level of knowledge with certificates, but... Mutual Authentication is all about who trusts whom. It is the severs responsibility to say, I need you to authenticate yourself with a certificate and here is a list of certificate providers I trust. It is then the clients responsibility to reply with a certificate signed by one of those server trusted certificate providers. It is the applications responsibility to then say, do I trust this person based on their name inside of the certificate? If and when things start to go wrong think about who is and or isn't trusting who.

One great tool is using -Djavax.net.debug=ssl on your application. It will show the entire ssl handshake and what is being requested and what the specific responses are. That option is a bit verbose, but it is nice to have when needed.

EDIT X 2

Here is how to enable mutual authentication on Tomcat 7.

In your server.xml config file you should see close to the following for an SSL connector:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="want" sslProtocol="TLS"
           keystoreFile="C:\Java\Certs\localhost.jks"
           keystorePass="changeit"
           URIEncoding="UTF-8" />

The important value to note is the clientAuth value.

Setting clientAuth to 'want' tells the client to send a signed client ssl certificate from a list of certificates that the server trusts if you have one. If not, go ahead and make your request as normal.

Setting clientAuth to 'true' tells the client that they HAVE to send a signed client ssl certificate from a list of certificates that the server trusts. If you do not have a certificate signed by a list of certificates that the server trusts, the client is NOT allowed to make the request.

The list of certificates that the server trusts either comes from the default java truststore or can be set using the -Djavax.net.ssl.trustStore="C:\Java\Certs\jssecacerts1" VM option.

Generally, when having a specific CA Certificate that you trust that isn't in the default Java truststore, the default truststore is copied, the new CA certificate is imported into the copied truststore and then used with above VM option.

WARNING

It is super important NOT to change the default Java truststore in place. If you do, all java applications by default on that machine will be using the new updated truststore. Not always what people want and can possible cause security risks.