Spring Boot password encryption with Jasypt

Reading Time: 5 minutes

Securing sensitive data is extremely important. In the following tutorial, we will go through the process of encrypting sensitive data in a Spring Boot application. We will take an easy approach to this very common procedure which takes place in any software project. This will be easy in the context of setup and usage of the given high-security java library. Without the need for deep knowledge/in-depth understanding of cryptography, encryption capabilities and encryption algorithms. Just following a simple setup with a few configuration steps. It is recommended to rely on the secure default configuration, but also Jasypt offers quite some customization if one needs it.

Jasypt (Java Simplified Encryption) provides utilities for encrypting user sensitive information, such as DB passwords, servers’ credentials, or other sensitive personal data. This information is key to users privacy, so we as developers need to make sure that no one gets the right to access them, irrelevant of the place where they are stored, they always need to be encrypted. Never store sensitive data in plain mode. It’s common sense we need to follow and it’s also something we need to honour if we want to gain our user’s trust. For this tutorial, we will use a specific library, Jasypt Spring Boot Starter, widely used across the Spring Boot community.

Jasypt setup steps

  1. Add jasypt-spring-boot-starter maven dependency in the pom.xml of the Spring Boot project
  2. Select a secret key to be used for encryption and decryption
  3. Generate Encrypted Key
  4. Add the Encrypted key in the config file
  5. Run the application

Let’s go into details in all of these steps:

Step 1. Adding maven dependency

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

This comes as a regular entry in the pom.xml file list of dependencies.

We should try to use latest stable versions. This version is the latest one at this moment and it offers better support for newer versions of Spring Boot. starting from 2.1.x and upwards. Also would advise using this because it comes with a more secure encryption algorithm by default, “PBEWITHHMACSHA512ANDAES_256”.

Step 2. Select a secret key to be used for encryption and decryption

This secret key will be used to encrypt and descript the data. You can think of it as a safeguard to further improve security. What it does, it actually adds a random string to the beginning or end of the input text prior to hashing or encrypting the value. This secret key goes in the property file, application.yml/application.properties in the Spring Boot project itself.

jasypt:
     encryptor:
           password: salting

Step 3. Generate Encrypted Key

Jasypt supplies a lot of (CLI) tools. In order to use these tools, you should download the distribution zip file (named jasypt-$VERSION-dist.zip) and unzip it. There will be an appropriate .bat or .sh file for the needed operation digest/encrypt/decrypt.

Example for encryption

$ ./encrypt.sh input="This is my message to be encrypted" password=MYPASSWORD

Example for decryption

$ ./decrypt.sh input="8fsdfdsafdsa9ffsad0fdsa0fdsfdsa3231x" password=MYPASSWORD

Another way of using Jasypt for encrypting your data is by using some online tools that provide Jasypt operations.

The simplest and most convenient way is a maven plugin. Not only that you can use it for a single value, it offers the capabilities to encrypt all sensitive data with a single command, meaning all placeholders will be updated in one step.

<build>
  <plugins>
    <plugin>
      <groupId>com.github.ulisesbocchio</groupId>
      <artifactId>jasypt-maven-plugin</artifactId>
      <version>3.0.3</version>
    </plugin>
  </plugins>
</build>

This jasypt-maven-plugin, by default, will check for configuration files under ./src/main/resources, or the regular Spring Boot resource folders. But also, Environment variables can be used to supply this master password. Instead of exposing the password “salting” inside the project itself, an Environment Variable can be created with, for instance, ENCRYPTION_MASTER_PASSWORD and then in the config file, password: ${ENCRYPTION_MASTER_PASSWORD}.

Example for encrypting a single value from a terminal.

This example uses the encryption password as an argument. Important, the terminal session needs to be opened where the pom.xml file with the maven plugin is located.

mvn jasypt:encrypt-value -Djasypt.encryptor.password=salting -Djasypt.plugin.value="secureDataWeNeedToEncrypt"

Example for encrypting all strings within projects property file.

The last argument is optional since Jasypt will scan that location anyway. What is important is that sensitive placeholders in the application property file MUST be wrapped in DEC() parenthesis. Activedirectory:password: DEC(supersecret) OracleDB:password: DEC(alsosupersecret).

mvn jasypt:encrypt -Djasypt.encryptor.password=salting -Djasypt.plugin.path="file:src/main/resources/application.yml"

If the previous statement completed successfully then, all sensitive data should be updated with their encrypted value. Updated properties output should be something like, Activedirectory:password: ENC(sFJDfdsfjjA8saT7YC65bsf71d0) OracleDB:password: ENC(34jjfsdfds+fds/fsd7Hs)

Step 4. Add the encrypted key in the config file

If you have been using the latest approach, then the application.properties/application.yaml files have already been updated with the newly encrypted values. All sensitive data wrapped with a DEC() is now encrypted, and all other strings in the configuration remained unchanged. If some of the other approaches were chosen, going one placeholder at a time, or using the cli, then we need to update the configuration file entries one by one. Still, the properties need to be wrapped in ENC() parenthesis anyway, since the output of the cli is only the encrypted value.

For the reverse process, it’s vice-versa, the first argument of the statement is: decrypt and all placeholders must be wrapped in ENC() parenthesis before execution.

Step 5. Run the application

That’s it. Your Spring Boot project will automatically decrypt all sensitive data when you start the application, no additional configuration is needed. Let me know in the comments section how was your experience. Was it smooth or are there some ongoing issues?

Securing your microservices with OAuth 2.0. Building Authorization and Resource server

Reading Time: 8 minutes

We live in a world of microservices. They give us an easy opportunity to scale our application. But as we scale our application it becomes more and more vulnerable. We need to think of a way of how to protect our services and how to keep the wrong people from accessing protected resources. One way to do that is by enabling user authorization and authentication. With authorization and authentication, we need a way to manage credentials, check the access of the requester and make sure people are doing what they suppose to.

When we speak about Spring (Cloud) Security, we are talking about Service authorization powered by OAuth 2.0. This is how it exactly works:

 

The actors in this OAuth 2.0 scenario that we are going to discuss are:

  • Resource Owner – Entity that grants access to a resource, usually you!
  • Resource Server – Server hosting the protected resource
  • Client – App making protected resource requests on behalf of a resource owner
  • Authorization server – server issuing access tokens to clients

The client will ask the resource owner to authorize itself. When the resource owner will provide an authorization grant with the client will send the request to the authorization server. The authorization server replies by sending an access token to the client. Now that the client has access token it will put it in the header and ask the resource server for the protected resource. And finally, the client will get the protected data.

Now that everything is clear about how the general OAuth 2.0 flow is working, let’s get our hands dirty and start writing our resource and authorization server!

Building OAuth2.0 Authorization server

Let’s start by creating our authorization server using the Spring Initializr. Create a project with the following configuration:

  • Project: Maven Project
  • Artefact: auth-server
  • Dependencies: Spring Web, Cloud Security, Cloud OAuth2

Download the project, copy it into your workspace and open it via your IDE. Go to your main class and add the @EnableAuthorizationServer annotation.

@SpringBootApplication
@EnableAuthorizationServer
public class AuthServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthServerApplication.class, args);
    }

}

Go to the application.properties file and make the following modification:

  • Change the server port to 8083
  • Set the context path to be “/api/auth”
  • Set the client id to “north47”
  • Set the client secret to “north47secret”
  • Enable all authorized grant types
  • Set the client scope to read and write
server.port=8083

server.servlet.context-path=/api/auth

security.oauth2.client.client-id=north47
security.oauth2.client.client-secret=north47secret
security.oauth2.client.authorized-grant-types=authorization,password,refresh_token,password,client_credentials
security.oauth2.client.scope=read,write

The client id is a public identifier for applications. The way that we used it is not a good practice for the production environment. It is usually a 32-character hex string so it won’t be so easy guessable.

Let’s add some users into our application. We are going to use in-memory users and we will achieve that by creating a new class ServiceConfig. Create a package called “config” with the following path: com.north47.authserver.config and in there create the above-mentioned class:

@Configuration
public class ServiceConfig extends GlobalAuthenticationConfigurerAdapter {

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("filip")
                .password(passwordEncoder().encode("1234"))
                .roles("ADMIN");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

With this we are defining one user with username: ‘filip’ and password: ‘1234’ with a role ADMIN. We are defining that BCryptPasswordEncoder bean so we can encode our password.

In order to authenticate the users that will arrive from another service we are going to add another class called UserResource into the newly created package resource (com.north47.autserver.resource):

@RestController
public class UserResource {

    @RequestMapping("/user")
    public Principal user(Principal user) {
        return user;
    }
}

When the users from other services will try to send a token for validation the user will also be validated with this method.

And that’s it! Now we have our authorization server! The authorization server is providing some default endpoints which we are going to see when we will be testing the resource server.

Building Resource Server

Now let’s build our resource server where we are going to keep our secure data. We will do that with the help of the Spring Initializr. Create a project with the following configuration:

  • Project: Maven Project
  • Artefact: resource-server
  • Dependencies: Spring Web, Cloud Security, Cloud OAuth2

Download the project and copy it in your workspace. First, we are going to create our entity called Train. Create a new package called domain into com.north47.resourceserver and create the class there.

public class Train {

    private int trainId;
    private boolean express;
    private int numOfSeats;

    public Train(int trainId, boolean express, int numOfSeats) {
        this.trainId = trainId;
        this.express = express;
        this.numOfSeats = numOfSeats;
    }

   public int getTrainId() {
        return trainId;
    }

    public void setTrainId(int trainId) {
        this.trainId = trainId;
    }

    public boolean isExpress() {
        return express;
    }

    public void setExpress(boolean express) {
        this.express = express;
    }

    public int getNumOfSeats() {
        return numOfSeats;
    }

    public void setNumOfSeats(int numOfSeats) {
        this.numOfSeats = numOfSeats;
    }

}

Let’s create one resource that will expose an endpoint from where we can get the protected data. Create a new package called resource and there create a class TrainResource. We will have one method only that will expose an endpoint behind we can get the protected data.

@RestController
@RequestMapping("/train")
public class TrainResource {


    @GetMapping
    public List<Train> getTrainData() {

        return Arrays.asList(new Train(1, true, 100),
                new Train(2, false, 80),
                new Train(3, true, 90));
    }
}

Let’s start the application and send a GET request to http://localhost:8082/api/services/train. You will be asked to enter a username and password. The username is user and the password you can see from the console where the application was started. By entering this credentials will give the protected data.

Let’s change the application now to be a resource server by going to the main class ResourceServerApplication and adding the annotation @EnableResourceServer.

@SpringBootApplication
@EnableResourceServer
public class ResourceServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ResourceServerApplication.class, args);
    }

}

Go to the application properties file and do the following changes:

server.port=8082
server.servlet.context-path=/api/services
security.oauth2.resource.user-info-uri=http://localhost:8083/api/auth/user 

What we have done here is:

  • Changed our server port to 8082
  • Set context path: /api/services
  • Gave user info URI where the user will be validated when he will try to pass a token

Now if you try to get the protected data by sending a GET request to http://localhost:8082/api/services/train the server will return to you a message that you are unauthorized and that full authentication is required. That means that without a token you won’t be able to access the resource.

So that means that we need a fresh new token in order to get the data. We will ask the authorization server to give us a token for the user that we previously created. Our client in this scenario will be the postman. The authorization server that we previously created is exposing some endpoints out of the box. To ask the authorization server for a fresh new token send a POST request to the following URL: localhost:8083/api/auth/oauth/token.

As it was said previously that postman in this scenario is the client that is accessing the resource, it will need to send the client credentials to the authorization server. Those are the client id and the client secret. Go to the authorization tab and add as a username the client id (north47) and the password will be the client secret (north47secret). On the picture below is presented how to set the request:

What is left is to say the username and password of the user. Open the body tab and select x-www-form-urlencoded and add the following values:

  • key: ‘grant_type’, value: ‘password’
  • key: ‘ client_id’, value: ‘north47’
  • key: ‘ username’, value: ‘filip’
  • key: ‘password’, value ‘1234’

Press send and you will get a response with the access_token:

{
    "access_token": "ae27c519-b3da-4da8-bacd-2ffc98450b18",
    "token_type": "bearer",
    "refresh_token": "d97c9d2d-31e7-456d-baa2-c2526fc71a5a",
    "expires_in": 43199,
    "scope": "read write"
}

Now that we have the access token we can call our protected resource by inserting the token into the header of the request. Open postman again and send a GET request to localhost:8082/api/services/train. Open the header tab and here is the place where we will insert the access token. For a key add “Authorization” and for value add “Bearer ae27c519-b3da-4da8-bacd-2ffc98450b18”.

 

And there it is! You have authorized itself and got a new token which allowed you to get the protected data.

You can find the projects in our repository:

And that’s it! Hope you enjoyed it!