This is a working demonstration of client certificate authentication using springboot 3 sslbundles.
It is based somewhat on https://www.baeldung.com/spring-boot-security-ssl-bundles.
In this demo, we have two web service apps:
- The "server" app which listens on port 8443 and requires client certificate authentication
- The "client" app which listens on port 8444 and uses client certificate authentication to communicate with the above server
The client has a springboot restful service that communicates with the server's restful service that is protected with client certificate authentication.
The ./install_certs.sh script is used to generate and install the certificates into both the client and server src/main/resources directories. Essentially it does the following:
- creates client cert and key and installs them in the client keystore (which it creates and protects with a password)
- installs the client cert in the server truststore - this is so the server knows to trust any client presenting this certificate
- creates server cert and key and installs them in the server resources folder (they are referenced by filename in the server's application.yaml)
- installs the server cert in the client truststore - this is so the client knows to trust the server with a hostname that matches the certificate's DN (Distinguished Name) or SAN (Subject Alternate Name)
The client and the server will pick their respective keystores and truststores up at launch time and use them for client certificate authentication.
- Java 17+ (
sudo apt install openjdk-17-jre) - Maven 3 (
sudo apt install maven) - Git (
sudo apt install git) - OpenSSL (
sudo apt install openssl) - Curl (
sudo apt install curl)
After cloning this repo, run the certificate generation script to create the certificates, keys, keystores and truststores.
git clone https://github.com/baeldung/spring-boot-ssl-bundles
cd spring-boot-ssl-bundles
./install_certs.sh
In one window/tab, kick off the client:
cd client
mvn spring-boot:run
IMPORTANT: You will need to make note of the generated password in the springboot:run output - look for the 'Using generated security password' line. This will be needed later for testing.
Then in another window/tab kick off the server:
cd server
mvn spring-boot:run
And finally, in another window/tab run the test script. This script will invoke the client API using curl, and the client will then invoke the server API with client cert auth:
./test_client_using_curl.sh "the password obtained from the client startup log"
Note the above script takes a single argument - that being the password generated by springboot:run on the client.
You can independently verify the server using curl by running ./test_server_using_curl.sh.
You can invoke the unit tests on the client and there is no need to install certificates as the test's application.yaml doesn't reference any sslbundles:
mvn clean test
The client tests illustrate both unit testing (with mocks) of the service class, as well as integration testing of the controller (with springboot mvc containers).
I didn't create any tests for the server as the focus for me was on the client implementation and unit testing.
- You don't need to add the client or server certs to the jvm cacerts or to the linux trusted certs
- The client cert doesn't need to match the client/origin hostname
- PEM files don't appear to work with ssl bundles when specifying the direct path to the cert and key files, so we use JKS instead.
- You only need to add server cert (or internal corporate CA) to client truststore if the server cert isn't trusted by a well known CA (e.g. when signed with internal corporate CAs)