From edc00dc69c175e6909adf187a70eb7f83c437ef1 Mon Sep 17 00:00:00 2001 From: Artur T <81235055+a-trzewik@users.noreply.github.com> Date: Mon, 31 May 2021 11:17:44 +0200 Subject: [PATCH] fix cert hash overwrite (#72) * fix cert hash overwrite * fix junit --- .../ec/dgc/issuance/config/ErrorHandler.java | 13 ++++++++++ .../restapi/controller/DgciController.java | 2 +- .../issuance/restapi/dto/DgciIdentifier.java | 2 +- .../ec/dgc/issuance/service/DgciConflict.java | 7 ++++++ .../ec/dgc/issuance/service/DgciService.java | 25 ++++++++++++++++--- .../dgc/issuance/service/DgciServiceTest.java | 2 +- 6 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/main/java/eu/europa/ec/dgc/issuance/service/DgciConflict.java diff --git a/src/main/java/eu/europa/ec/dgc/issuance/config/ErrorHandler.java b/src/main/java/eu/europa/ec/dgc/issuance/config/ErrorHandler.java index 9b5e285..1f65f97 100644 --- a/src/main/java/eu/europa/ec/dgc/issuance/config/ErrorHandler.java +++ b/src/main/java/eu/europa/ec/dgc/issuance/config/ErrorHandler.java @@ -22,6 +22,7 @@ import eu.europa.ec.dgc.issuance.restapi.dto.ProblemReportDto; import eu.europa.ec.dgc.issuance.service.DdcGatewayException; +import eu.europa.ec.dgc.issuance.service.DgciConflict; import eu.europa.ec.dgc.issuance.service.DgciNotFound; import eu.europa.ec.dgc.issuance.service.WrongRequest; import javax.validation.ConstraintViolationException; @@ -73,6 +74,18 @@ public ResponseEntity handleException(DgciNotFound e) { .body(new ProblemReportDto("", "DGCI not found", "", e.getMessage())); } + /** + * Exception Handler to handle {@link DgciConflict} Exceptions. + */ + @ExceptionHandler(DgciConflict.class) + public ResponseEntity handleException(DgciConflict e) { + log.error(e.getMessage()); + return ResponseEntity + .status(HttpStatus.CONFLICT) + .contentType(MediaType.APPLICATION_JSON) + .body(new ProblemReportDto("", "DGCI conflict", "", e.getMessage())); + } + /** * Exception Handler to handle {@link DdcGatewayException} Exceptions. */ diff --git a/src/main/java/eu/europa/ec/dgc/issuance/restapi/controller/DgciController.java b/src/main/java/eu/europa/ec/dgc/issuance/restapi/controller/DgciController.java index 5f75e50..46905c6 100644 --- a/src/main/java/eu/europa/ec/dgc/issuance/restapi/controller/DgciController.java +++ b/src/main/java/eu/europa/ec/dgc/issuance/restapi/controller/DgciController.java @@ -67,7 +67,7 @@ public ResponseEntity initDgci(@Valid @RequestBody DgciInit dgci @ApiResponse(responseCode = "404", description = "dgci with related id not found"), @ApiResponse(responseCode = "400", description = "wrong issue data")}) @PutMapping(value = "/issue/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity finalizeDgci(@PathVariable long id, @Valid @RequestBody IssueData issueData) + public ResponseEntity finalizeDgci(@PathVariable String id, @Valid @RequestBody IssueData issueData) throws Exception { return ResponseEntity.ok(dgciService.finishDgci(id, issueData)); } diff --git a/src/main/java/eu/europa/ec/dgc/issuance/restapi/dto/DgciIdentifier.java b/src/main/java/eu/europa/ec/dgc/issuance/restapi/dto/DgciIdentifier.java index 8310f3e..08ff1bf 100644 --- a/src/main/java/eu/europa/ec/dgc/issuance/restapi/dto/DgciIdentifier.java +++ b/src/main/java/eu/europa/ec/dgc/issuance/restapi/dto/DgciIdentifier.java @@ -26,7 +26,7 @@ @Data @AllArgsConstructor public class DgciIdentifier { - private long id; + private String id; private String dgci; private String kid; private int algId; diff --git a/src/main/java/eu/europa/ec/dgc/issuance/service/DgciConflict.java b/src/main/java/eu/europa/ec/dgc/issuance/service/DgciConflict.java new file mode 100644 index 0000000..18744e0 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/issuance/service/DgciConflict.java @@ -0,0 +1,7 @@ +package eu.europa.ec.dgc.issuance.service; + +public class DgciConflict extends RuntimeException { + public DgciConflict(String message) { + super(message); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java b/src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java index ad496df..3f58b1f 100644 --- a/src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java +++ b/src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java @@ -26,6 +26,7 @@ import com.nimbusds.jose.jwk.Curve; import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.util.Base64URL; import com.upokecenter.cbor.CBORObject; import com.upokecenter.cbor.CBORType; import ehn.techiop.hcert.data.Eudgc; @@ -78,6 +79,7 @@ @Component @RequiredArgsConstructor public class DgciService { + private static final String ID_SEP = "_"; public enum DgciStatus { EXISTS, NOT_EXISTS, LOCKED @@ -119,8 +121,12 @@ public DgciIdentifier initDgci(DgciInit dgciInit) { log.info("init dgci: {} id: {}", dgci, dgciEntity.getId()); long expirationSec = expiration.toInstant().getEpochSecond(); + byte[] dgciHash = Base64.getDecoder().decode(dgciEntity.getDgciHash()); + // We need Base64URL encoding because Base64 contains slashes that are not allowed + // by tomcat + String id = dgciEntity.getId().toString() + ID_SEP + Base64URL.encode(dgciHash); return new DgciIdentifier( - dgciEntity.getId(), + id, dgci, certificateService.getKidAsBase64(), certificateService.getAlgorithmIdentifier(), @@ -141,9 +147,22 @@ private String generateDgci() { * @param issueData issueData * @return signature data */ - public SignatureData finishDgci(long dgciId, IssueData issueData) { - Optional dgciEntityOpt = dgciRepository.findById(dgciId); + public SignatureData finishDgci(String dgciId, IssueData issueData) { + int colIdx = dgciId.indexOf(ID_SEP); + if (colIdx < 0) { + throw new WrongRequest("id unknown"); + } + long id = Long.parseLong(dgciId.substring(0,colIdx)); + byte[] dgciHash = Base64URL.from(dgciId.substring(colIdx + 1)).decode(); + String dgciHashBase64 = Base64.getEncoder().encodeToString(dgciHash); + Optional dgciEntityOpt = dgciRepository.findById(id); if (dgciEntityOpt.isPresent()) { + if (dgciEntityOpt.get().getCertHash() != null) { + throw new DgciConflict("already signed"); + } + if (!dgciEntityOpt.get().getDgciHash().equals(dgciHashBase64)) { + throw new DgciNotFound("dgci not found"); + } var dgciEntity = dgciEntityOpt.get(); Tan tan = Tan.create(); dgciEntity.setHashedTan(tan.getHashedTan()); diff --git a/src/test/java/eu/europa/ec/dgc/issuance/service/DgciServiceTest.java b/src/test/java/eu/europa/ec/dgc/issuance/service/DgciServiceTest.java index fc0114d..cedd7d5 100644 --- a/src/test/java/eu/europa/ec/dgc/issuance/service/DgciServiceTest.java +++ b/src/test/java/eu/europa/ec/dgc/issuance/service/DgciServiceTest.java @@ -275,7 +275,6 @@ void signFromHash() throws Exception { String hash64 = "ZALr2hyVD4l5veh7+Auq78TQeS4PKOMAgVyy4GVSi9g="; DgciInit dgciInit = new DgciInit(); dgciInit.setGreenCertificateType(GreenCertificateType.Vaccination); - DgciIdentifier dgciIdentifier = dgciService.initDgci(dgciInit); java.security.interfaces.ECPublicKey pubKey = (java.security.interfaces.ECPublicKey) certificateService.getPublicKey(); AsymmetricKeyParameter keyParameter = ECUtil.generatePublicKeyParameter(pubKey); @@ -286,6 +285,7 @@ void signFromHash() throws Exception { IssueData issueData = new IssueData(); // Try more time to get all possible byte paddings options for (int i = 0;i<1000;i++) { + DgciIdentifier dgciIdentifier = dgciService.initDgci(dgciInit); Random rnd = new Random(); byte[] hash = new byte[32]; rnd.nextBytes(hash);