Tuesday, February 9, 2021

X.509 Authentication in Spring Security

 x.509 is a digital signature which is an encoded hash document that is encrypted by the private key. This certificate signature must be verified by each client before establishing an HTTPS connection to securing the application. 

In this article we will see how to generate a server CA certificate and client certificate.


Generating Server CA Certificate: 

Step 1: To signed the server side and client side certificate first we need to generate a certificate authority. To do this first open the command prompt and run below command to generate self signed CA certificate:

openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout serverCA.key -out serverCA.crt

After run the above command it will ask a pass phrase for private key. For this article we will use changeit as a passphrase. It will ask also some additional information which is optional. In this article we will provide only Common Name (CN) as localhost

Step 2: Server-side Certificate: Now run below command to generate a certificate signin request:

openssl req -new -newkey rsa:4096 -keyout localhost.key -out localhost.csr

This step will ask similar information like step 1. We will only provide password changeit for passphrase and localhost for CN

Step 3: Before we proceed, we need to create a configuration file – localhost.ext. It'll store some additional parameters needed during signing the certificate:

authorityKeyIdentifier=keyid,issuer

basicConstraints=CA:FALSE

subjectAltName = @alt_names

[alt_names]

DNS.1 = localhost

Step 4: At this stage we need to sign the request with serverCA.crt certificate. To do this run the below command:

openssl x509 -req -CA serverCA.crt -CAkey serverCA.key -in localhost.csr -out localhost.crt -days 365 -CAcreateserial -extfile localhost.ext

It will ask for passphrase and we have to provide the same passphrase that we used to created CA certificate. After providing the required information it will generate a file localhost.crt which is the certificate signed by our own certificate authority.

Step 5: Now we have to import the signed certificate and private key into the keystore. Before importing in keystore we will bundle the certificate and private key using pkcs12 archiveing. Run below command for packing:

openssl pkcs12 -export -out localhost.p12 -name "localhost" -inkey localhost.key -in localhost.crt

After executing the avove command it will generate localhost.p12 file as a bundle of privatekey and certificate. Now we will import the  localhost.p12 file and generate the keystore.jks file using keytool with below command:

keytool -importkeystore -srckeystore localhost.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS

Step 6: Imports the server CA certificate to the Java truststore. Run below command:

keytool -import -trustcacerts -noprompt -alias ca -ext san=dns:localhost,ip:127.0.0.1 -file serverCA.crt -keystore truststore.jks

The stored password in this case is changeit.


Spring Security Configuration: 

Step 1: If you complete the above steps then we are ready to implement the authentication in server side. Add below configuration in application.properties file:

server.ssl.key-store=../store/keystore.jks

server.ssl.key-store-password=${PASSWORD}

server.ssl.key-alias=localhost

server.ssl.key-password=${PASSWORD}

server.ssl.trust-store=../store/truststore.jks

server.ssl.trust-store-password=${PASSWORD}

server.ssl.enabled=true

server.ssl.client-auth=need


Note: In this article we are using changeit for all password field.

Step 2: Add spring-boot-starter-security dependency in POM file:

<dependency> 

                   <groupId>org.springframework.boot</groupId> 

                   <artifactId>spring-boot-starter-security</artifactId> 

            </dependency>

Step 3: Add below class SecurityConfig.java file to enable the x509 authentication.

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests().anyRequest().authenticated()

.and().x509()

.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)

.and().csrf().disable();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication().withUser("localhost").password("none").roles("USER");

}

}

Now create your own API endpoint which will be secured using x509 certificate.

Generating Client Certificate: 

To create the client-side certificate follow the similar process that already describe in Generating Server CA Certificate section.If the server certificate is  signed from a trusted authority then follow below step to generate the keystore.jks and truststore.jks file:

Step 1: We have to create a certificate signing request:

openssl req -new -newkey rsa:4096 -nodes -keyout client.key -out client.csr

Step 2: Need to sign the request with server CA certificate (serverCA.crt) and private key (serverCA.key):

    openssl x509 -req -CA ../serverCA.crt -CAkey ../serverCA.key -in client.csr -out client.crt -days 365 -CAcreateserial

Step 3: The last step we need to take is to package the signed certificate and the private key into the PKCS file and then create keystore.jks using keytool:

openssl pkcs12 -export -out client.p12 -name "client" -inkey client.key -in client.crt

keytool -importkeystore -srckeystore client.p12 -srcstoretype PKCS12 -destkeystore clientkeystore.jks -deststoretype JKS

Step 4: Generate client trust key:

keytool -import -trustcacerts -noprompt -alias localhost -ext san=dns:localhost,ip:127.0.0.1 -file client.crt -keystore clienttruststore.jks

Client Side Java Code: Finally the client is ready to send a authenticated request to server application. To make a valid request you must add below code in your consumer class of client application:       

        // Configuration for x 509 certificate.

System.setProperty("javax.net.ssl.keyStoreType", "jks");

System.setProperty("javax.net.ssl.trustStoreType", "jks");

System.setProperty("javax.net.ssl.keyStore", "..\\keystore.jks");

System.setProperty("javax.net.ssl.trustStore","..\\truststore.jks");

System.setProperty("javax.net.ssl.keyStorePassword", "changeit");

System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

Note: Here our server certificate is self signed so we using directly server keystore.jks and truststore.jks file. But when the certificate is signed from a Certificate Authority then you generate client certificate using above instruction and referenced client keystore.jks and truststore.jks file.


2 comments: