Skip to content

Commit

Permalink
HMAC implementation for digiLocker Impl (#505)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sahil-tarento authored Mar 15, 2024
1 parent ba004ce commit 89de371
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 14 deletions.
11 changes: 11 additions & 0 deletions src/main/java/org/sunbird/common/util/CbExtServerProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,9 @@ public void setRedisWheeboxKey(String redisWheeboxKey) {
@Value("${es.default.result.limit}")
private int esDefaultResultLimit;

@Value("${digilocker-api-key}")
private String digiLockerAPIKey;

public boolean qListFromCacheEnabled() {
return qListFromCacheEnabled;
}
Expand Down Expand Up @@ -2380,4 +2383,12 @@ public String getSbRoleRead() {
public void setSbRoleRead(String sbRoleRead) {
this.sbRoleRead = sbRoleRead;
}

public String getDigiLockerAPIKey() {
return digiLockerAPIKey;
}

public void setDigiLockerAPIKey(String digiLockerAPIKey) {
this.digiLockerAPIKey = digiLockerAPIKey;
}
}
1 change: 1 addition & 0 deletions src/main/java/org/sunbird/common/util/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,7 @@ public class Constants {
public static final String MDO_ADMIN = "MDO_ADMIN";
public static final String REPORT_ACCESS_EXPIRY_TABLE = "report_access_expiry";
public static final String REPORT_EXPIRY_DATE = "reportExpiryDate";
public static final String X_DIGILOCKER_HMAC = "x-digilocker-hmac";


private Constants() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.sunbird.digilocker.model.PullDocRequest;
import org.sunbird.common.util.Constants;
import org.sunbird.digilocker.model.PullDocResponse;
import org.sunbird.digilocker.model.PullURIRequest;
import org.sunbird.digilocker.model.PullURIResponse;
import org.sunbird.digilocker.service.DigiLockerIntegrationService;

Expand All @@ -17,15 +16,15 @@ public class DigiLockerIntegrationController {
DigiLockerIntegrationService digiLockerIntegrationService;

@PostMapping(value = "/v1/retrieveURI", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public @ResponseBody ResponseEntity<PullURIResponse> getURIRequestV1(@RequestBody PullURIRequest request) {
public @ResponseBody ResponseEntity<PullURIResponse> getURIRequestV1(@RequestHeader(value= Constants.X_DIGILOCKER_HMAC) String digiLockerHmac, @RequestBody String requestBody) {

PullURIResponse response = digiLockerIntegrationService.generateURIResponse(request);
PullURIResponse response = digiLockerIntegrationService.generateURIResponse(digiLockerHmac, requestBody);
return ResponseEntity.ok().body(response);
}

@PostMapping(value = "/v1/retrieveDoc", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public @ResponseBody ResponseEntity<PullDocResponse> getDocRequestV1(@RequestBody PullDocRequest request) {
PullDocResponse response = digiLockerIntegrationService.generateDocResponse(request);
public @ResponseBody ResponseEntity<PullDocResponse> getDocRequestV1(@RequestHeader(value= Constants.X_DIGILOCKER_HMAC) String digiLockerHmac, @RequestBody String requestBody) {
PullDocResponse response = digiLockerIntegrationService.generateDocResponse(digiLockerHmac, requestBody);
return ResponseEntity.ok().body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

public interface DigiLockerIntegrationService {

PullURIResponse generateURIResponse(PullURIRequest request);
PullURIResponse generateURIResponse(String digiLockerHmac, String requestBody);

PullDocResponse generateDocResponse(PullDocRequest request);
PullDocResponse generateDocResponse(String digiLockerHmac, String requestBody);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.sunbird.digilocker.service;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
Expand All @@ -20,10 +22,15 @@
import org.sunbird.digilocker.model.*;
import org.sunbird.user.service.UserUtilityService;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
Expand All @@ -44,16 +51,33 @@ public class DigiLockerIntegrationServiceImpl implements DigiLockerIntegrationSe
@Autowired
CbExtServerProperties serverProperties;

@Autowired
ObjectMapper objectMapper;
private Logger logger = LoggerFactory.getLogger(getClass().getName());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
@Override
public PullURIResponse generateURIResponse(PullURIRequest request) {
public PullURIResponse generateURIResponse(String digiLockerHmac, String requestBody) {
PullURIResponse response = new PullURIResponse();
ResponseStatus responseStatus = response.getResponseStatus();
responseStatus.setTs(dateFormat.format(new Date()));
if (!validateRequest(digiLockerHmac, requestBody)) {
responseStatus.setStatus("0");
logger.error("Not able to validate the request for hmac: " + digiLockerHmac + " requestBody:" + requestBody);
return response;
}
PullURIRequest request = null;
try {
objectMapper = new XmlMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
request = objectMapper.readValue(requestBody, PullURIRequest.class);
} catch (IOException e) {
logger.error("Not able to process the request requestBody: " + requestBody, e);
responseStatus.setStatus("0");
return response;
}
CertificateAddInfoDTO certificateAddInfoDTO = new CertificateAddInfoDTO();
try {
responseStatus.setTs(dateFormat.format(new Date()));
responseStatus.setTxn(request.getTxn());
Map<String, Object> getUserInfo = userUtilityService.getUserDetails(Constants.PHONE, request.getDocDetails().getMobile());
String certificateAccessCode = request.getDocDetails().getCertificateAccessCode();
Expand Down Expand Up @@ -167,9 +191,24 @@ public PullURIResponse generateURIResponse(PullURIRequest request) {
}

@Override
public PullDocResponse generateDocResponse(PullDocRequest request) {
public PullDocResponse generateDocResponse(String digiLockerHmac, String requestBody) {
PullDocResponse response = new PullDocResponse();
ResponseStatus responseStatus = response.getResponseStatus();
if (!validateRequest(digiLockerHmac, requestBody)) {
responseStatus.setStatus("0");
logger.error("Not able to validate the request for hmac: " + digiLockerHmac + " requestBody:" + requestBody);
return response;
}
PullDocRequest request = null;
try {
objectMapper = new XmlMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
request = objectMapper.readValue(requestBody, PullDocRequest.class);
} catch (IOException e) {
logger.error("Not able to process the request requestBody: " + requestBody, e);
responseStatus.setStatus("0");
return response;
}
CertificateAddInfoDTO certificateAddInfoDTO = new CertificateAddInfoDTO();
try {
DocResponseDetails docDetails = response.getDocDetails();
Expand Down Expand Up @@ -313,8 +352,42 @@ private CertificateInfo addCertificateInfo(CertificateAddInfoDTO certificateAddI
return certificateInfo;
}

public static byte[] convertObjectToJsonBytes(Object object) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
public byte[] convertObjectToJsonBytes(Object object) throws Exception {
return objectMapper.writeValueAsBytes(object);
}

private boolean validateRequest(String digiLockerHmac, String request) {
logger.debug("The digiLocker Hmac shared: " + digiLockerHmac);
String hmacRequestValue = calculateHMACSHA256(request, serverProperties.getDigiLockerAPIKey());
logger.debug("The hmacRequestValue is: "+ hmacRequestValue);
if (hmacRequestValue.equals(digiLockerHmac)) {
return true;
}
return false;
}

private String calculateHMACSHA256(String requestBody, String apiKey) {
try {
logger.debug("The API key is: " + apiKey);
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] hmacBytes = sha256Hmac.doFinal(requestBody.getBytes());

// Convert bytes to hexadecimal string
StringBuilder hexString = new StringBuilder();
for (byte b : hmacBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
byte[] base64EncodedHmacBytes = Base64.getEncoder().encode(hexString.toString().getBytes(StandardCharsets.UTF_8));
return new String(base64EncodedHmacBytes);
} catch (NoSuchAlgorithmException ex) {
logger.error("Not able to convert SHA256: ", ex);
} catch (InvalidKeyException e) {
logger.error("Invalid Key for converting: ", e);
}
return "";
}
}
3 changes: 2 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ redis.wheebox.key=wheebox_usersMapping
cert-registry-service-host=http://cert-registry-service:9000
cert-registry-certificate-download-url=/certs/v2/registry/download/

digilocker-issuer-id=in.gov.karamyogi
digilocker-issuer-id=karmayogibharat.gov.in
digilocker-api-key=api-key
report.property.file.allMdo=ContentCompetencyMapping.csv

operational.reports.passwordlength=15
Expand Down

0 comments on commit 89de371

Please sign in to comment.