Skip to content

Example With Bcrypt In Spring Boot#

Example With Bcrypt In Spring Boot#

  • In this example we will use Bcrypt which is strong hashing function to encode the password. Bcrypt is a password-hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher

Dependencies#

  • In Spring Boot we need to add the spring-security dependency to do the Hashing with Bcrypt.
pom.xml
1
2
3
4
5
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-security</artifactId>  
    <version>2.6.4</version>  
</dependency>
  • So, why don't we use other dependencies to do the Bcrypt hashing example?

    If you use the Maven Repository to search other Bcrypt dependencies then you can see they are out of update and contain many vulnerabilities. So In this example, we should the one from the spring-security which currently contains no Vulnerabilities.

Controller#

  • Let's create an controller with some apis as below:
BcryptController.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.springboot.security.hash.app.controller;

import com.springboot.security.hash.app.model.DataRequest;
import com.springboot.security.hash.app.model.MatchDataRequest;
import com.springboot.security.hash.app.service.BcryptService;
import com.springboot.security.hash.app.service.Pbkdf2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BcryptController {

    @Autowired
    private BcryptService bcryptService;

    @RequestMapping(method = RequestMethod.POST, path = "v1/cipher/hash/bcrypt", produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity<String> hashSHA256(@RequestBody DataRequest inputData) {
        return new ResponseEntity<>(this.bcryptService.hashBcrypt(inputData.getData()), HttpStatus.CREATED);
    }

    @RequestMapping(method = RequestMethod.POST, path = "v1/cipher/hash/bcrypt/check", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Boolean> checkMatchSha256(@RequestBody MatchDataRequest inputData) {
        return new ResponseEntity<>(this.bcryptService.isBcryptMatch(inputData.getRawData(), inputData.getHashedData()), HttpStatus.OK);
    }

}
  • We will need to create 2 simple models for request body as below. One is used for hashing data and the other one is used for checking raw data and hashed data.
DataRequest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.springboot.security.hash.app.model;

import org.springframework.lang.NonNull;

public class DataRequest {

    private String data;

    @NonNull
    public String getData() {
        return data;
    }

    public void setData(@NonNull String data) {
        this.data = data;
    }

}
MatchDataRequest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.springboot.security.hash.app.model;

public class MatchDataRequest {

    private String rawData;
    private String hashedData;

    public String getRawData() {
        return rawData;
    }

    public void setRawData(String rawData) {
        this.rawData = rawData;
    }

    public String getHashedData() {
        return hashedData;
    }

    public void setHashedData(String hashedData) {
        this.hashedData = hashedData;
    }
}
  • Then we also need to create a model that loads environment variables into a spring bean using @ConfigurationProperties as below.
HashConfigProperties.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.springboot.security.hash.app.model;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "hash")
public class HashConfigProperties {

    private Bcrypt bcrypt;

    public Sha256 getSha256() {
        return sha256;
    }

    public Bcrypt getBcrypt() {
        return bcrypt;
    }

    public void setBcrypt(Bcrypt bcrypt) {
        this.bcrypt = bcrypt;
    }
}
Bcrypt.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.springboot.security.hash.app.model;  

public class Bcrypt {  

    private int strength;  

    public int getStrength() {  
        return strength;  
    }  

    public void setStrength(int strength) {  
        this.strength = strength;  
    }  
}

Configuration#

  • Now, let create a configuration class name PasswordEncoderConfig and but the code as below.
PasswordEncoderConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.springboot.security.hash.app.config;

import com.springboot.security.hash.app.model.HashConfigProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.security.SecureRandom;

@Configuration
public class PasswordEncoderConfig {

    @Autowired
    private HashConfigProperties hashConfigProperties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        SecureRandom random = new SecureRandom();
        return new BCryptPasswordEncoder(hashConfigProperties.getBcrypt().getStrength(), random);
    }

}
  • In which, we will override the default passwordEncoder bean of spring-security by a BCryptPasswordEncoder which is initialized by a strength and a SecureRandom.
  • We will configure the strength in the application.yml as below.

application.yml
1
2
3
hash:  
  bcrypt:  
    strength: 12
- One more thing we should note that, in this example, we just focus on the Bcrypt hashing so we will ignore the default spring-security configuration in the dependency spring-boot-starter-security. So we will add one more configuration as below to disable the default spring-security configuration.

application.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
      - org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration


hash:  
  bcrypt:  
    strength: 12

Service#

  • Now let's create a service with name BcryptService with the code as below. In which, we will Inject the passwordEncoder bean that we have just configured in the step above. Then we will use encode and matches methods that the PasswordEncoder provided to encode and check the hashed data.
BcryptService.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.springboot.security.hash.app.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class BcryptService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    public String hashBcrypt(String data) {
        return this.passwordEncoder.encode(data);
    }

    public boolean isBcryptMatch(String data, String hashedData) {
        return this.passwordEncoder.matches(data, hashedData);
    }

}

Testing#

  • Now, run the Spring Boot project and try to call api v1/cipher/hash/bcrypt for testing hasing data. Then you will receive the result as below

 #zoom

  • Then with the hash result above, we will use it to check with the original data by calling api v1/cipher/hash/bcrypt/check. Then you will see the original data and hashed data are matched.

 #zoom

  • Now let's try to change a single character in original data and check again with hashed data. Then you will see the api return failed.

 #zoom

See Also#

References#