DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Establish Trust Chain From Kafka to Microservices REST APIs With JWT
  • REST API Error Handling With Spring Boot
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Develop a Spring Boot REST API in AWS: PART 4 (CodePipeline / CI/CD)

Trending

  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • GDPR Compliance With .NET: Securing Data the Right Way
  • How to Build Scalable Mobile Apps With React Native: A Step-by-Step Guide
  • Accelerating AI Inference With TensorRT
  1. DZone
  2. Coding
  3. Frameworks
  4. Securing REST APIs With Client Certificates

Securing REST APIs With Client Certificates

Securing REST APIs with server-side certificates is a best practice. But what if you want to take security to the next level and require client certificates?

By 
Pavel Sklenar user avatar
Pavel Sklenar
·
Updated Mar. 31, 19 · Tutorial
Likes (36)
Comment
Save
Tweet
Share
290.5K Views

Join the DZone community and get the full member experience.

Join For Free

This post is about an example of securing a REST API with a client certificate (a.k.a. X.509 certificate authentication).

In other words, a client verifies a server according to its certificate and the server identifies that client according to a client certificate (so-called mutual authentication).

In connection with Spring Security, we will be able to perform some additional authentication and authorization.

Technologies used:

  1. Spring Boot 2.0.5.RELEASE
  2. Spring Web + Security 5.0.8.RELEASE
  3. Embedded Tomcat 8.5.34

Quick post overview:

  • Create a simple REST API service (without any security)
  • Create certificates for server and client
  • Configure the server to serve HTTPS content
  • Configure the server to require a client certificate
  • Spring Security for further client authentication and authorization
  • Test our secured REST API

Final Project Structure

Creating a New Base Spring Boot Project

We will start with a new project generated by Spring Initializr. We just need two Spring dependencies, i.e. Spring Web + Spring Security.

All required dependencies are shown here:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Create a Simple REST API Service

Let's create a simple REST controller serving a detail about a customer using an HTTP GET method:

@RestController
@RequestMapping("/customer")
public class CustomerController {

    @GetMapping("/{id}")
    public Customer GetCustomer(@PathVariable Long id) {
        return new Customer(id, "Customer" + id);
    }
}

Displaying URL http://localhost:8080/customer/1 returns this JSON object:

{
    "id":1,
    "name":"Customer1"
}

Create Certificates for Server and Client

I want to stay focused on securing REST APIs so I will show you how to generate all required files in a very concise way. For more details about commands, visit my other blog post about creating a PKCS #12 key store.

#Create folders to generate all files (separated for client and server)
mkdir ssl && cd ssl && mkdir client && mkdir server

## Server
# Generate server private key and self-signed certificate in one step
openssl req -x509 -newkey rsa:4096 -keyout server/serverPrivateKey.pem -out server/server.crt -days 3650 -nodes
# Create PKCS12 keystore containing private key and related self-sign certificate
openssl pkcs12 -export -out server/keyStore.p12 -inkey server/serverPrivateKey.pem -in server/server.crt
# Generate server trust store from server certificate 
keytool -import -trustcacerts -alias root -file server/server.crt -keystore server/trustStore.jks

## Client
# Generate client's private key and a certificate signing request (CSR)
openssl req -new -newkey rsa:4096 -out client/request.csr -keyout client/myPrivateKey.pem -nodes

## Server
# Sign client's CSR with server private key and a related certificate
openssl x509 -req -days 360 -in request.csr -CA server/server.crt -CAkey server/serverPrivateKey.pem -CAcreateserial -out client/pavel.crt -sha256

## Client
# Verify client's certificate
openssl x509 -text -noout -in client/pavel.crt
# Create PKCS12 keystore containing client's private key and related self-sign certificate 
openssl pkcs12 -export -out client/client_pavel.p12 -inkey client/myPrivateKey.pem -in client/pavel.crt -certfile server/myCertificate.crt

You can find the SSL folder with all generated files on the project's GitHub page.

We will use files in the server folder to configure our server.

The final client's file client/client_pavel.p12 can be either imported into your browser or used in another client application.

On Windows, just open this file and import it into your system to test the REST API with any browser.

Configure the Server to Serve HTTPS Content

Basically, there are two options.

You can use any standalone server (e.g. Tomcat, WildFly, etc.) so the configuration would be specific to your choice. I prefer this choice for production environments.

Instead of configuring an application server, I will show you the second, simpler way of using an embedded Tomcat server inside Spring Boot.

The configuration is quite easy, we will change the port to 8443 and configure the server key store generated in the previous steps:

# Define a custom port (instead of the default 8080)
server.port=8443
# The format used for the keystore
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=classpath:keyStore.p12
# The password used to generate the certificate
server.ssl.key-store-password=changeit

Configure the Server to Require a Client Certificate

The configuration of any server to require a client certificate (i.e. the mutual authentication) is very similar to the server side configuration except using words like a trust store instead of a key store.
So the embedded Tomcat configuration seems like:

# Trust store that holds SSL certificates.
server.ssl.trust-store=classpath:trustStore.jks
# Password used to access the trust store.
server.ssl.trust-store-password=changeit
# Type of the trust store.
server.ssl.trust-store-type=JKS
# Whether client authentication is wanted ("want") or needed ("need").
server.ssl.client-auth=need

The embedded server now ensures (without any other configuration) that the clients with a valid certificate only are able to call our REST API.

Other clients will be declined by the server due to being unable to make correct SSL/TLS handshake (required by mutual authentication).

Please note that all configuration items starting server.* are related to an embedded Tomcat server only. You do not need it when using any standalone application server.

Spring Security for Further Client Authentication and Authorization

It would be fine to get an incoming client for our application as a logged user.

That gives us the possibility to perform some other authentications and authorizations using Spring Security (e.g. securing method calls to specific roles).

Until now, no Spring Security was needed, but all clients with any valid certificate may perform any call in our application without knowing who the caller is.

So we must configure Spring Security to create a logged user using a username from a client certificate (usually from the CN field, see the method call subjectPrincipalRegex):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated().and()
                .x509()
                    .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                    .userDetailsService(userDetailsService());
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return (UserDetailsService) username -&amp;amp;amp;amp;gt; {
            if (username.equals("pavel")) {
                return new User(username, "",
                        AuthorityUtils
                                .commaSeparatedStringToAuthorityList("ROLE_USER"));
            } else {
                throw new UsernameNotFoundException(String.format("User %s not found", username));
            }
        };
    }
}

Using the bean UserDetailsService is a kind of fake, but it shows an example of an additional authentication to accept only the username "pavel".

In other words, it accepts a client with a certificate containing the value "pavel" only in the certificate's CN field (as mentioned before, configured with subjectPrincipalRegex).

As you might have noticed, only the user "pavel" is a member of the role "user", so now we are able to restrict method calls to specific roles:

    @GetMapping("/{id}")
    @Secured("ROLE_USER")
    public Customer GetCustomer(@PathVariable Long id) {
        return new Customer(id, "Customer" + id);
    }

Test Secured REST API

When you successfully import client/client_pavel.p12 into your system and the application runs, you can visit URL https://localhost:8443/customer/1.

The first access of this page displays a window to select the correct certificate to authenticate with the server:

When you submit an incorrect certificate, you will see the "access denied" page (otherwise JSON object returned):

That is all! You can find all my source code on my GitHub profile.

And a useful link for this topic:

  • Spring Boot Official: Configure SSL
REST Web Protocols Spring Framework Spring Security

Published at DZone with permission of Pavel Sklenar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Establish Trust Chain From Kafka to Microservices REST APIs With JWT
  • REST API Error Handling With Spring Boot
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Develop a Spring Boot REST API in AWS: PART 4 (CodePipeline / CI/CD)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!