From 66b00e9995a2dbefbc126e6c850f0ae3769bd826 Mon Sep 17 00:00:00 2001 From: Felix Dittrich <31076102+f11h@users.noreply.github.com> Date: Thu, 9 Mar 2023 11:33:12 +0100 Subject: [PATCH] Fix: Revocation Date must be in future (#214) * Fix: Revocation Date must be in future * Update suppressions.xml --- owasp/suppressions.xml | 4 ++ .../CertificateRevocationListController.java | 3 ++ .../service/RevocationListService.java | 9 ++++ ...tificateRevocationListIntegrationTest.java | 50 +++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/owasp/suppressions.xml b/owasp/suppressions.xml index 02f3e651..431c3754 100644 --- a/owasp/suppressions.xml +++ b/owasp/suppressions.xml @@ -26,4 +26,8 @@ Still WIP CVE-2022-41862 + + False positive + CVE-2018-14335 + diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java index b9fe0874..6de321d6 100644 --- a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java +++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java @@ -264,6 +264,9 @@ public ResponseEntity uploadBatch( case INVALID_COUNTRY: throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x000", "Invalid Country sent", "", e.getMessage()); + case INVALID_DATE: + throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x000", "Invalid Dates", "", + e.getMessage()); case UPLOADER_CERT_CHECK_FAILED: throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x000", "Invalid Upload Certificate", batch.getSignerCertificate().getSubject().toString(), "Certificate used to sign the batch " diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java index 56f53840..5341b005 100644 --- a/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java +++ b/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java @@ -104,6 +104,7 @@ public RevocationBatchEntity addRevocationBatch( contentCheckUploaderCertificate(signerCertificate, authenticatedCountryCode); RevocationBatchDto parsedBatch = contentCheckValidJson(uploadedRevocationBatch, RevocationBatchDto.class); + contentCheckValidDate(parsedBatch); contentCheckValidValues(parsedBatch); contentCheckUploaderCountry(parsedBatch, authenticatedCountryCode); @@ -331,6 +332,13 @@ private T contentCheckValidJson(String json, Class clazz) throws Revocati } } + private void contentCheckValidDate(RevocationBatchDto parsedBatch) throws RevocationBatchServiceException { + if (!parsedBatch.getExpires().isAfter(ZonedDateTime.now())) { + throw new RevocationBatchServiceException( + RevocationBatchServiceException.Reason.INVALID_DATE, + "Expiration date must be in future."); + } + } private void contentCheckValidValues(RevocationBatchDto parsedBatch) throws RevocationBatchServiceException { @@ -443,6 +451,7 @@ public enum Reason { INVALID_JSON, INVALID_JSON_VALUES, INVALID_COUNTRY, + INVALID_DATE, UPLOADER_CERT_CHECK_FAILED, NOT_FOUND, GONE diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java index 72f66477..84dc2fd0 100644 --- a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java +++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java @@ -379,6 +379,56 @@ void testUploadFailedInvalidCountry() throws Exception { Assertions.assertEquals(auditEventEntitiesInDb, auditEventRepository.count()); } + @Test + void testUploadFailedInvalidExpirationDate() throws Exception { + long revocationBatchesInDb = revocationBatchRepository.count(); + long auditEventEntitiesInDb = auditEventRepository.count(); + + X509Certificate signerCertificate = + trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + PrivateKey signerPrivateKey = + trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode); + + RevocationBatchDto revocationBatchDto = new RevocationBatchDto(); + revocationBatchDto.setCountry(countryCode); + revocationBatchDto.setExpires(ZonedDateTime.now().minusSeconds(10)); + revocationBatchDto.setHashType(RevocationHashTypeDto.SIGNATURE); + revocationBatchDto.setKid("UNKNOWN_KID"); + revocationBatchDto.setEntries(List.of( + new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString( + new byte[] {0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa})), + new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString( + new byte[] {0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb})), + new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString( + new byte[] {0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc})), + new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString( + new byte[] {0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd})), + new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString( + new byte[] {0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe})) + )); + + String payload = new SignedStringMessageBuilder() + .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey) + .withPayload(objectMapper.writeValueAsString(revocationBatchDto)) + .buildAsString(); + + String authCertHash = + trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode); + trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER); + + mockMvc.perform(post("/revocation-list") + .content(payload) + .contentType("application/cms") + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash) + .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject) + ) + .andExpect(status().isBadRequest()) + .andExpect(header().doesNotExist(HttpHeaders.ETAG)); + + Assertions.assertEquals(revocationBatchesInDb, revocationBatchRepository.count()); + Assertions.assertEquals(auditEventEntitiesInDb, auditEventRepository.count()); + } + @Test void testDeleteRevocationBatch() throws Exception { RevocationBatchEntity entity = new RevocationBatchEntity();