17

I have written Spring controller. This get's requests from clients. It's just REST style.

This is very good. But I need certificate authentication. Only the clients must have access to the rest service (spring controller), which have client certificates with key (In the other words client should have keystore with key).

How can I configure this security to spring? Could you give me an example or link where this is written?

Thanks

grep
  • 5,465
  • 12
  • 60
  • 112

2 Answers2

20

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.

hooknc
  • 4,854
  • 5
  • 31
  • 60
  • Yes, I want example of X.509 authentication, to the Spring controller (Which receives REST style requests). Could u give me another reference, where everything will be expanded inside out? – grep Oct 29 '14 at 22:27
  • Wow, that is a tall order. I'll google around and see if there is anything like that and update the answer. – hooknc Oct 29 '14 at 23:00
  • As you tell me "It is the servers responsibility". if it is done in server side (for example we can do it in tomcat), why spring security have X.509 Authentication? – grep Feb 02 '16 at 14:48
  • 1
    I think you took a bit of my sentence out of context. I said "It is the servers responsibility to make/request the client to send its certificate." This sentence means that tomcat needs to be setup for what is called mutual authentication. In your server.xml for your connector the parameter clientAuth must be set to either want or true. Tomcat will then pass the certificate information to spring and spring will then determine if the request should be authenticated or not. I have updated the answer to have a link that explains how to configure tomcat with mutual authentication. – hooknc Feb 03 '16 at 22:57
  • **truststore** is also required (at least Tomcat 8 needs it, not sure about other versions). My config: `` – manikanta Jul 11 '16 at 10:43
  • @manikanta I finally did some testing on your comment. The truststore can be set either though VM options (using `-Djavax.net.ssl.trustStore` and `-Djavax.net.ssl.trustStorePassword`) or through the ssl connector found in the Tomcat server.xml file. As far as I can tell the truststore is not 'required' in the server.xml. But, if the server is trusting certificates not signed by a well known CA that is found in the default cacerts/jssecacerts, then yes, as some point the correct truststore has to be set, either though VM options or the server.xml file. – hooknc Aug 24 '16 at 20:29
  • Link to pdf file is broken, it would be better to remove it or find a mirror site for downloading it – Salvioner Nov 29 '18 at 10:46
6

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.

jonashackt
  • 12,022
  • 5
  • 67
  • 124
  • This is a good example for sending a request with a certificate. OP is talking about already having a controller that receives requests (with certificates) from clients not making API calls with a certificate as your solution presents. – veritas Dec 06 '22 at 12:01