Hmac (Hash-based Message Authentication Code) authentication is the technique used to simultaneously verify both the data integrity and authenticity of a message. It consists of the Secret Key and the Hash Function which guarantees the integrity of the message between two parties.
HMAC uses cryptographic hash functions such as MD5 and SHA-*(MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512)
Java provides built-in support for Mac class. We need to add only dependencies for parsing data and ease of code.
- Java
- Spring Boot
- Maven
- Basic Cryptographic knowledge
- Lombok
- Eclipse or IntelliJ IDEA (or any preferred IDE) with embedded Maven
- Maven (version >= 3.6.0)
- Postman (or any RESTful API testing tool)
GOTO > ~/absolute-path-to-directory/spring-boot-hmac-auth
and try below command in terminal
mvn spring-boot:run
it will run application as spring boot application
or
mvn clean install
it will build application and create jar file under target directory
Run jar file from below path with given command
java -jar ~/path-to-spring-boot-hmac-auth/target/spring-boot-hmac-auth-0.0.1-SNAPSHOT.jar
Or
run main method from
SpringBootHmacAuthApplication.java
as spring boot application.
-
Need to add below dependencies to enable web support related config in pom.xml. Lombok's dependency is to get rid of boiler-plate code.
<dependency> <goupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
-
Reading H2 DB related properties from application.properties file and configuring JPA connection factory for H2 database.
src/main/resources/application.properties
hmac-auth: header: nonce: nonce access-key: accesskey authorization: authorization access-key: 8c2ea66e-abfc-4394-8adb-fa52890bdce3 secret-key: 982064c6-c265-11ed-afa1-0242ac120002 expires: 60 # seconds
-
Below model class which we will use to perform CRUD operations if authentication is successful.
SuperHero.java@Data @AllArgsConstructor @NoArgsConstructor @Builder public class SuperHero implements Serializable { @Id @GeneratedValue private int id; private String name; private String superName; private String profession; private int age; private boolean canFly; // Constructor, Getter and Setter }
-
All classes from config package are used to authenticate users with Hmac.
- config
- filter
- HmacInterceptor - Which will intercept each request and validate the auth token is valid and not expired
- properties
- HmacProperties - Read properties from application.properties and configure Hmac objects.
- verifier
- CredentialsVerifier - Will verify the access kay and nonce (token is expired or not).
- HmacAuthVerifier - This class is the core of Hmac authentication. It will validate all the things regarding request metadata and body under verify method. Will verify the cryptographic algorithm, headers and token as well for coming requests.
- web
- HmacVerifyConfig - Here we'll register HmacInterceptor interceptor in the spring interceptor registry.
- filter
- CredentialsProvider - Plain interface with some abstract methods for credentials provider.
- HmacAuthBeanConfig - Hmac auth related beans like HmacInterceptor and CredentialsProvider for spring application by reading HmacProperties.
- HmacHelper - Helper class to read Http request and extract data from it generate signature request of Hmac authentication.
- HmacSignature - Class which contains signature methods to return Hmac token by using cryptographic algorithm, http request and secret.
- config
-
In HmacKeyController.java class, we have exposed 5 endpoints for basic CRUD operations which are under SuperHeroController. This controller will generate token for each request which we'll be validating under superHeroController.
- GET token for All Super Heroes
- GET token for ID
- POST token to store Super Hero in DB
- PUT token for to update Super Hero
- DELETE token for ID
@RestController @RequestMapping("/hmac-key/super-heroes") public class HmacKeyController { @GetMapping("/{id}") public ResponseEntity<?> findById(HttpServletRequest httpServletRequest) throws IOException; @GetMapping public ResponseEntity<List<?>> findAll(HttpServletRequest httpServletRequest) throws IOException; @PostMapping public ResponseEntity<?> save(HttpServletRequest httpServletRequest) throws IOException; @PutMapping("/{id}") public ResponseEntity<?> update(HttpServletRequest httpServletRequest) throws IOException; @DeleteMapping("/{id}") public ResponseEntity<?> delete(HttpServletRequest httpServletRequest) throws IOException; }
-
In SuperHeroController.java class, we have exposed 5 endpoints for basic CRUD operations
- GET All Super Heroes
- GET by ID
- POST to store Super Hero in DB
- PUT to update Super Hero
- DELETE by ID
But here we'll validate request before executing the actual logic and check the token is valid or not under HmacInterceptor class. This will intercept each request and allow for only valid tokens.
@RestController @RequestMapping("/super-hero") public class SuperHeroController { @GetMapping public ResponseEntity<List<?>> findAll(); @GetMapping("/{id}") public ResponseEntity<?> findById(@PathVariable String id); @PostMapping public ResponseEntity<?> save(@RequestBody SuperHero superHero); @PutMapping("/{id}") public ResponseEntity<?> update(@PathVariable int id, @RequestBody SuperHero superHero); @DeleteMapping("/{id}") public ResponseEntity<?> delete(@PathVariable String id); }
-
GET http://localhost:8080/hmac-key/super-heroes # /hmac-key is important here
{ authorization: "HmacSHA256:3+na/n6Htt2MnRQzEtYYISy5l7O/GzHDtVyisDhNT/Q=", nonce: "1678793259142" }
accessKey: 8c2ea66e-abfc-4394-8adb-fa52890bdce3 authorization: HmacSHA256:3+na/n6Htt2MnRQzEtYYISy5l7O/GzHDtVyisDhNT/Q= nonce: 1678793259142
{ authorization: "HmacSHA256:mHQeVAoGpDv7aSGLtDQ664gr7t47JL71NHNktj4w6hQ=' nonce: "1678791858860" }
accessKey: 8c2ea66e-abfc-4394-8adb-fa52890bdce3 authorization: HmacSHA256:mHQeVAoGpDv7aSGLtDQ664gr7t47JL71NHNktj4w6hQ= nonce: 1678791858860
curl --location --request POST 'http://localhost:8080/hmac-key/super-heroes' \ --header 'Content-Type: application/json' \ --data-raw ' { "id": 10, "name": "Tony", "superName": "Iron Man", "profession": "Business", "age": 50, "canFly": true }'
{ "authorization": "HmacSHA256:GGNhJT44kpnAozc/wf+a5I6IS+TyVV/nBTvrFh6Miwc=", "nonce": "1678796605841" }
curl --location --request POST 'http://localhost:8080/v1/super-heroes' \ --header 'accessKey: 8c2ea66e-abfc-4394-8adb-fa52890bdce3' \ --header 'authorization: HmacSHA256:GGNhJT44kpnAozc/wf+a5I6IS+TyVV/nBTvrFh6Miwc=' \ --header 'nonce: 1678796605841' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": 10, "name": "Tony", "superName": "Iron Man", "profession": "Business", "age": 50, "canFly": true }'