From 4d2bc78756b9ea29c7e2e4cf695e6dc2b1d5b2cd Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 09:44:39 +0200 Subject: [PATCH 1/6] refactoring validation service endpoint --- .../decorator/config/IdentityProperties.java | 4 +- .../ValidationStatusController.java | 2 +- .../BookingServiceTokenContentResponse.java | 2 + .../repository/BookingServiceRepository.java | 24 ++++++++- .../ValidationServiceRepository.java | 54 ++++++------------- .../decorator/service/DccTokenService.java | 8 +-- .../decorator/service/InitializeService.java | 1 - .../service/ValidationStatusService.java | 35 ++++++++++-- src/main/resources/application.yml | 17 ++---- 9 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/config/IdentityProperties.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/config/IdentityProperties.java index a9126a8..c33e2e2 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/config/IdentityProperties.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/config/IdentityProperties.java @@ -34,9 +34,7 @@ public class IdentityProperties { private String protocolVersion; private String serviceIdentityUrl; - - private String validationIdentityUrl; - + private String consent; private String servicePovider; diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/ValidationStatusController.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/ValidationStatusController.java index da88084..f16b7ce 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/ValidationStatusController.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/ValidationStatusController.java @@ -62,7 +62,7 @@ public class ValidationStatusController { @ApiResponse(responseCode = "410", description = "Gone. Subject does not exist anymore"), @ApiResponse(responseCode = "500", description = "Internal Server Error") }) - @GetMapping(value = PATH, consumes = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity reject(@RequestHeader("Authorization") final String token) { log.debug("Incoming GET request to '{}' with token '{}'", PATH, token); diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/BookingServiceTokenContentResponse.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/BookingServiceTokenContentResponse.java index 735e863..9dd7d1d 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/BookingServiceTokenContentResponse.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/BookingServiceTokenContentResponse.java @@ -51,6 +51,8 @@ public static final class PassengerResponse { private LocalDate birthDate; private DccStatusResponse dccStatus; + + private String serviceIdUsed; } @Data diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/BookingServiceRepository.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/BookingServiceRepository.java index d8a7438..db04afc 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/BookingServiceRepository.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/BookingServiceRepository.java @@ -20,10 +20,12 @@ package eu.europa.ec.dgc.validation.decorator.repository; +import eu.europa.ec.dgc.validation.decorator.config.DgcProperties.ServiceProperties; import eu.europa.ec.dgc.validation.decorator.entity.BookingServiceResultRequest; import eu.europa.ec.dgc.validation.decorator.entity.BookingServiceTokenContentResponse; import eu.europa.ec.dgc.validation.decorator.service.AccessTokenService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -31,7 +33,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +@Slf4j @Service @RequiredArgsConstructor public class BookingServiceRepository { @@ -55,13 +59,30 @@ public class BookingServiceRepository { * @return {@link BookingServiceTokenContentResponse} */ public BookingServiceTokenContentResponse tokenContent(final String subject) { - final String url = this.tokenContentUrl.replace(PLACEHOLDER_SUBJECT, subject); + return this.tokenContent(subject, null); + } + + /** + * Booking service token content endpoint. + * + * @param subject {@link String} + * @param service Used service + * @return {@link BookingServiceTokenContentResponse} + */ + public BookingServiceTokenContentResponse tokenContent(final String subject, final ServiceProperties service) { + final UriComponentsBuilder urlBuilder = UriComponentsBuilder + .fromUriString(this.tokenContentUrl.replace(PLACEHOLDER_SUBJECT, subject)); + if (service != null) { + urlBuilder.queryParam("service", service.getId()); + } + final String url = urlBuilder.toUriString(); final HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", accessTokenService.buildHeaderToken(subject)); final HttpEntity entity = new HttpEntity<>(headers); + log.debug("REST Call to '{}' starting", url); final ResponseEntity response = this.restTpl.exchange(url, HttpMethod.GET, entity, BookingServiceTokenContentResponse.class); return response.getBody(); @@ -81,6 +102,7 @@ public void result(final String subject, final BookingServiceResultRequest body) final HttpEntity entity = new HttpEntity<>(body, headers); + log.debug("REST Call to '{}' starting", url); this.restTpl.exchange(url, HttpMethod.PUT, entity, String.class); } } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/ValidationServiceRepository.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/ValidationServiceRepository.java index 7afcc26..5285d02 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/ValidationServiceRepository.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/repository/ValidationServiceRepository.java @@ -22,7 +22,6 @@ import eu.europa.ec.dgc.validation.decorator.config.DgcProperties.ServiceProperties; import eu.europa.ec.dgc.validation.decorator.dto.DccTokenRequest; -import eu.europa.ec.dgc.validation.decorator.entity.DccValidationRequest; import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceIdentityResponse; import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceInitializeRequest; import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceInitializeResponse; @@ -33,27 +32,19 @@ import java.util.Random; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; @Slf4j @Service @RequiredArgsConstructor public class ValidationServiceRepository { - private static final String PLACEHOLDER_SUBJECT = "{subject}"; - - @Value("${validation.urls.identity}") - private String identityUrl; - - @Value("${validation.urls.status}") - private String statusUrl; - private final RestTemplate restTpl; private final AccessTokenService accessTokenService; @@ -64,8 +55,12 @@ public class ValidationServiceRepository { * * @return {@link ValidationServiceIdentityResponse} */ - public ValidationServiceIdentityResponse identity() { - final String url = this.identityUrl; + public ValidationServiceIdentityResponse identity(final ServiceProperties service) { + final String url = UriComponentsBuilder.fromUriString(service.getServiceEndpoint()) + .path("identity") + .toUriString(); + + log.debug("REST Call to '{}' starting", url); final ResponseEntity response = restTpl .getForEntity(url, ValidationServiceIdentityResponse.class); return response.getBody(); @@ -81,7 +76,9 @@ public ValidationServiceIdentityResponse identity() { */ public ValidationServiceInitializeResponse initialize( final ServiceProperties service, DccTokenRequest dccToken, String subject) { - final String url = String.format("%s/%s", service.getServiceEndpoint(), subject); + final String url = UriComponentsBuilder.fromUriString(service.getServiceEndpoint()) + .pathSegment("initialize", subject) + .toUriString(); final ValidationServiceInitializeRequest body = new ValidationServiceInitializeRequest(); body.setPubKey(dccToken.getPubKey()); @@ -98,7 +95,6 @@ public ValidationServiceInitializeResponse initialize( log.debug("REST Call to '{}' starting", url); final ResponseEntity response = restTpl .exchange(url, HttpMethod.PUT, entity, ValidationServiceInitializeResponse.class); - log.debug("REST Call to '{}' done", url); return response.getBody(); } @@ -108,8 +104,10 @@ public ValidationServiceInitializeResponse initialize( * @param subject {@link String} * @return {@link ValidationServiceStatusResponse} */ - public ValidationServiceStatusResponse status(final String subject) { - final String url = this.statusUrl.replace(PLACEHOLDER_SUBJECT, subject); + public ValidationServiceStatusResponse status(final ServiceProperties service, final String subject) { + final String url = UriComponentsBuilder.fromUriString(service.getServiceEndpoint()) + .pathSegment("status", subject) + .toUriString(); final HttpHeaders headers = new HttpHeaders(); headers.add("X-Version", "1.0"); @@ -117,6 +115,7 @@ public ValidationServiceStatusResponse status(final String subject) { final HttpEntity entity = new HttpEntity<>(headers); + log.debug("REST Call to '{}' starting", url); final ResponseEntity response = restTpl.exchange(url, HttpMethod.GET, entity, String.class); switch (response.getStatusCode()) { case OK: @@ -128,29 +127,6 @@ public ValidationServiceStatusResponse status(final String subject) { } } - /** - * Validation service validate endpoint. - * - * @param service {@link ServiceProperties} - * @param subject {@link String} - * @return {@link String} - */ - public String validate(final ServiceProperties service, final String subject) { - final String url = String.format("%s/%s", service.getServiceEndpoint(), subject); - - final HttpHeaders headers = new HttpHeaders(); - headers.add("X-Version", "1.0"); - headers.add("Authorization", accessTokenService.buildHeaderToken(subject)); - - final DccValidationRequest body = new DccValidationRequest(); - // TODO content source? - - final HttpEntity entity = new HttpEntity<>(body, headers); - - final ResponseEntity response = restTpl.exchange(url, HttpMethod.POST, entity, String.class); - return response.getBody(); - } - private String buildNonce() { byte[] randomBytes = new byte[16]; new Random().nextBytes(randomBytes); diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java index 9a992dc..8e7cb9b 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java @@ -74,14 +74,14 @@ public AccessTokenPayload getAccessTockenForValidationService( final ValidationServiceInitializeResponse initialize = validationServiceRepository .initialize(service, dccToken, subject); - final BookingServiceTokenContentResponse tokenContent = bookingServiceRepository.tokenContent(subject); + final BookingServiceTokenContentResponse tokenContent = bookingServiceRepository.tokenContent(subject, service); if (tokenContent.getPassengers() == null || tokenContent.getPassengers().isEmpty()) { throw new NotFoundException("Passenger not found by subject"); } final PassengerResponse passenger = tokenContent.getPassengers().get(0); final FlightInfoResponse flightInfo = tokenContent.getFlightInfo(); - return buildAccessToken(subject, initialize, passenger, flightInfo); + return this.buildAccessToken(subject, initialize, passenger, flightInfo); } private AccessTokenPayload buildAccessToken( @@ -89,7 +89,7 @@ private AccessTokenPayload buildAccessToken( final ValidationServiceInitializeResponse initialize, final PassengerResponse passenger, final FlightInfoResponse flightInfo) { - AccessTokenConditions accessTokenConditions = new AccessTokenConditions(); + final AccessTokenConditions accessTokenConditions = new AccessTokenConditions(); accessTokenConditions.setLang("en-en"); // TODO Selected language accessTokenConditions.setFnt(passenger.getForename()); accessTokenConditions.setGnt(passenger.getLastname()); @@ -106,7 +106,7 @@ private AccessTokenPayload buildAccessToken( accessTokenConditions.setValidationClock(flightInfo.getArrivalTime().format(FORMATTER)); accessTokenConditions.setValidTo(departureTime.plusDays(2).format(FORMATTER)); - AccessTokenPayload accessTokenPayload = new AccessTokenPayload(); + final AccessTokenPayload accessTokenPayload = new AccessTokenPayload(); accessTokenPayload.setIss(dgcProperties.getToken().getIssuer()); accessTokenPayload.setIat(Instant.now().getEpochSecond()); accessTokenPayload.setExp(initialize.getExp()); diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/InitializeService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/InitializeService.java index d962f8e..f8c1a6f 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/InitializeService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/InitializeService.java @@ -44,7 +44,6 @@ public QrCodeDto getBySubject(String subject) { .protocol(properties.getProtocol()) .protocolVersion(properties.getProtocolVersion()) .serviceIdentity(properties.getServiceIdentityUrl()) - //.validationIdentity(properties.getValidationIdentityUrl()) .token(accessTokenService.buildAccessToken(subject)) .consent(properties.getConsent()) .subject(subject) diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java index 6b3f1c1..2d01ce0 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java @@ -20,7 +20,12 @@ package eu.europa.ec.dgc.validation.decorator.service; +import eu.europa.ec.dgc.validation.decorator.config.DgcProperties.ServiceProperties; +import eu.europa.ec.dgc.validation.decorator.entity.BookingServiceTokenContentResponse; +import eu.europa.ec.dgc.validation.decorator.entity.BookingServiceTokenContentResponse.PassengerResponse; import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceStatusResponse; +import eu.europa.ec.dgc.validation.decorator.exception.NotFoundException; +import eu.europa.ec.dgc.validation.decorator.repository.BookingServiceRepository; import eu.europa.ec.dgc.validation.decorator.repository.ValidationServiceRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -28,10 +33,32 @@ @Service @RequiredArgsConstructor public class ValidationStatusService { - + + private final BookingServiceRepository bookingServiceRepository; + + private final IdentityService identityService; + private final ValidationServiceRepository validationServiceRepository; - - public ValidationServiceStatusResponse determineStatus(String subject) { - return validationServiceRepository.status(subject); + + /** + * Determines the status of the validation service + * + * @param subject Subject ID + * @return {@link ValidationServiceStatusResponse} + */ + public ValidationServiceStatusResponse determineStatus(final String subject) { + final BookingServiceTokenContentResponse tokenContent = bookingServiceRepository.tokenContent(subject); + if (tokenContent.getPassengers() == null || tokenContent.getPassengers().isEmpty()) { + throw new NotFoundException("Passenger not found by subject"); + } + + final PassengerResponse passenger = tokenContent.getPassengers().get(0); + final String serviceId = passenger.getServiceIdUsed(); + if (serviceId == null || serviceId.isBlank()) { + throw new NotFoundException("Passenger without service ID"); + } + + final ServiceProperties service = identityService.getServicePropertiesById(serviceId); + return validationServiceRepository.status(service, subject); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c2da5cb..555e195 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,8 +3,6 @@ identity: protocolVersion: 1.0.0 #### serviceIdentity serviceIdentityUrl: https://validation-decorator.example - #### validationIdentity - validationIdentityUrl: https://validation-decorator.example/identity consent: I want to check your DCC to confirm your booking! :) servicePovider: Service Provider.com restTemplate: @@ -16,11 +14,6 @@ booking: tokenContent: http://localhost:8082/tokencontent/{subject} result: http://localhost:8082/result/{subject} -validation: - urls: - identity: http://localhost:8081/identity - status: http://localhost:8081/status/{subject} - #### Create Key: keytool -genkey -alias [ALIAS NAME] -keyalg EC -validity 3650 -keystore dev-decorator.jks -storetype JKS dgc: businessRulesDownload: @@ -53,14 +46,10 @@ dgc: keyAlgorithm: EC services: #### Validation Service - - id: http://localhost:8081/initialize + - id: http://localhost:8080/identity/ValidationService type: ValidationService - serviceEndpoint: http://localhost:8081/initialize - name: Validation Service Initialize - - id: http://localhost:8081/validate - type: DccValidatorService - serviceEndpoint: http://localhost:8081/validate - name: Validation Service Validate + serviceEndpoint: http://localhost:8081 + name: Validation Service #### Validation Decorator - id: http://localhost:8080/token type: AccessCredentialService From bd501d5c7248224b041da7200c9e80dbddd10be7 Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 09:46:52 +0200 Subject: [PATCH 2/6] fix check style --- .../validation/decorator/service/ValidationStatusService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java index 2d01ce0..e95bc47 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/ValidationStatusService.java @@ -41,7 +41,7 @@ public class ValidationStatusService { private final ValidationServiceRepository validationServiceRepository; /** - * Determines the status of the validation service + * Determines the status of the validation service. * * @param subject Subject ID * @return {@link ValidationServiceStatusResponse} From 016fddf5af81224dab29118fc55d2177366b49a7 Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 10:18:35 +0200 Subject: [PATCH 3/6] refactoring identity --- .../decorator/config/DgcProperties.java | 8 ++--- .../decorator/service/IdentityService.java | 29 ++++++++-------- src/main/resources/application.yml | 33 ++++++++----------- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/config/DgcProperties.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/config/DgcProperties.java index b447061..cb92e6f 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/config/DgcProperties.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/config/DgcProperties.java @@ -35,12 +35,6 @@ @ConfigurationProperties("dgc") public class DgcProperties { - private final GatewayDownload businessRulesDownload = new GatewayDownload(); - - private final GatewayDownload valueSetsDownload = new GatewayDownload(); - - private final GatewayDownload countryListDownload = new GatewayDownload(); - @DurationUnit(ChronoUnit.SECONDS) private Duration validationExpire = Duration.ofMinutes(60); @@ -61,6 +55,8 @@ public class DgcProperties { private TokenProperties token; private List services = new ArrayList<>(); + + private List endpoints = new ArrayList<>(); @Data public static final class GatewayDownload { diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java index 3aede5d..e939ca5 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java @@ -29,9 +29,9 @@ import eu.europa.ec.dgc.validation.decorator.entity.KeyType; import eu.europa.ec.dgc.validation.decorator.exception.DccException; import eu.europa.ec.dgc.validation.decorator.exception.NotFoundException; +import io.vavr.collection.Stream; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; -import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @@ -78,7 +78,7 @@ public IdentityResponse getIdentity(final String element, final String id) { final IdentityResponse identityResponse = new IdentityResponse(); identityResponse.setId(identityId); identityResponse.setVerificationMethod(verificationMethods); - identityResponse.setService(getServices()); + identityResponse.setService(getServices(element, id)); return identityResponse; } @@ -98,19 +98,18 @@ public ServiceProperties getServicePropertiesById(final String serviceId) { throw new NotFoundException("Verification method not found. No ID available."); } - private List getServices() { - List services = new ArrayList<>(); - if (dgcProperties.getServices() != null) { - dgcProperties.getServices().stream().map(service -> { - ServiceIdentityResponse response = new ServiceIdentityResponse(); - response.setId(service.getId()); - response.setType(service.getType()); - response.setServiceEndpoint(service.getServiceEndpoint()); - response.setName(service.getName()); - return response; - }).forEach(services::add); - } - return services; + private List getServices(final String element, final String id) { + // TODO impl filter for id + return Stream.concat(dgcProperties.getServices(), dgcProperties.getEndpoints()) + .filter(service -> element == null || element.equalsIgnoreCase(service.getType())) + .map(service -> { + final ServiceIdentityResponse response = new ServiceIdentityResponse(); + response.setId(service.getId()); + response.setType(service.getType()); + response.setServiceEndpoint(service.getServiceEndpoint()); + response.setName(service.getName()); + return response; + }).collect(Collectors.toList()); } private PublicKeyJwkIdentityResponse buildPublicKey(String keyName) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 555e195..1440429 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,15 +16,7 @@ booking: #### Create Key: keytool -genkey -alias [ALIAS NAME] -keyalg EC -validity 3650 -keystore dev-decorator.jks -storetype JKS dgc: - businessRulesDownload: - timeInterval: 1800000 - lockLimit: 3600000 - certificatesDownloader: - timeInterval: 1800000 - lockLimit: 3600000 - valueSetsDownload: - timeInterval: 1800000 - lockLimit: 3600000 + #### Validation Decorator URI without trailing slash serviceUrl: http://localhost:8080 keyStoreFile: certs/dev-decorator.jks keyStorePassword: changeMe @@ -44,28 +36,29 @@ dgc: publicKey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIPrtYsW9+Juwp/mt7h8FJ3LgFRIUl2Vlmcl1DUm5gNHl0LnHIL4Jff6mg6yVhehdQiMvkhUtTvmFIUWONSJEnw== privateKey: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBSuPIbykwH24sjQsTneeN6EyjiA1NK5W7uca+HxmGmWw== keyAlgorithm: EC + #### Validation Service services: - #### Validation Service - - id: http://localhost:8080/identity/ValidationService + - id: ${dgc.serviceUrl}/identity/ValidationService type: ValidationService serviceEndpoint: http://localhost:8081 name: Validation Service - #### Validation Decorator - - id: http://localhost:8080/token + #### Validation Decorator + endpoints: + - id: ${dgc.serviceUrl}/identity/AccessCredentialService type: AccessCredentialService - serviceEndpoint: http://localhost:8080/token + serviceEndpoint: ${dgc.serviceUrl}/token name: Validation Decorator Token - - id: http://localhost:8080/identity + - id: ${dgc.serviceUrl}/identity/ServiceProvider type: ServiceProvider - serviceEndpoint: http://localhost:8080/identity + serviceEndpoint: ${dgc.serviceUrl}/identity name: Validation Decorator Identity - - id: http://localhost:8080/reject + - id: ${dgc.serviceUrl}/identity/CancellationService type: CancellationService - serviceEndpoint: http://localhost:8080/reject + serviceEndpoint: ${dgc.serviceUrl}/reject name: Validation Decorator Reject - - id: http://localhost:8080/status + - id: ${dgc.serviceUrl}/identity/StatusService type: StatusService - serviceEndpoint: http://localhost:8080/status + serviceEndpoint: ${dgc.serviceUrl}/status name: Validation Decorator Status server: From fe69c4485e21c9577fe976c43b7e4a3dc9df0cd3 Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 10:22:54 +0200 Subject: [PATCH 4/6] clean up --- .../validation/decorator/controller/IdentityController.java | 2 +- .../ec/dgc/validation/decorator/service/IdentityService.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/IdentityController.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/IdentityController.java index 321a7ce..5687ca2 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/IdentityController.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/controller/IdentityController.java @@ -37,7 +37,7 @@ @RequiredArgsConstructor public class IdentityController { - private static final String PATH_ALL = "/identity"; + public static final String PATH_ALL = "/identity"; private static final String PATH_ELEMENT = "/identity/{element}"; diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java index e939ca5..46e4ca9 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java @@ -22,6 +22,7 @@ import eu.europa.ec.dgc.validation.decorator.config.DgcProperties; import eu.europa.ec.dgc.validation.decorator.config.DgcProperties.ServiceProperties; +import eu.europa.ec.dgc.validation.decorator.controller.IdentityController; import eu.europa.ec.dgc.validation.decorator.dto.IdentityResponse; import eu.europa.ec.dgc.validation.decorator.dto.IdentityResponse.PublicKeyJwkIdentityResponse; import eu.europa.ec.dgc.validation.decorator.dto.IdentityResponse.ServiceIdentityResponse; @@ -42,7 +43,7 @@ @RequiredArgsConstructor public class IdentityService { - private static final String IDENTITY_PATH = "/identity"; + private static final String IDENTITY_PATH = IdentityController.PATH_ALL; private static final String VERIFICATION_TYPE = "JsonWebKey2020"; From 882f6fddfed9c1281b370f50e787a21b2aebcb08 Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 12:45:36 +0200 Subject: [PATCH 5/6] refactoring JWT and KeyStore --- certs/dev-decorator.jks | Bin 1401 -> 8563 bytes certs/dev-test.jks | Bin 3492 -> 0 bytes .../decorator/config/DgcProperties.java | 35 +++---- .../decorator/dto/IdentityResponse.java | 2 + .../validation/decorator/entity/KeyType.java | 4 +- .../decorator/service/AccessTokenService.java | 92 +++--------------- .../decorator/service/IdentityService.java | 11 ++- .../service/KeyStoreKeyProvider.java | 65 +++++++------ src/main/resources/application.yml | 30 +++--- 9 files changed, 91 insertions(+), 148 deletions(-) delete mode 100644 certs/dev-test.jks diff --git a/certs/dev-decorator.jks b/certs/dev-decorator.jks index f815f532fafe2b7e502649e84f9ec869ffdb1d49..12591c9ab45a1adbc423e6385a9f98e0c26762d4 100644 GIT binary patch literal 8563 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 literal 1401 zcmezO_TO6u1_mZ5W@L~mOU%hkNi50C&r3;7&M!(V$uBC-OwY?stz=+etiI3x4yS)RpP{_`#)uRG^Mz`IBg!#35*P^5qhQumO$2LgC@rJKs=+_k z7$V#lBFu0R137VCBSQmALt{fD14|>zC~;n65Z4gOrL$`y4TRa)!C}F~2n`fwMs{W= z1{T-TVWJnZCSUSq?>Nq5yXeOIsd>q;w92mcsF??s@2?lRRvkE|5&%Vqm};|<%>=Z9N9ala(#*BYke7IOLF z)z7ZGMfTN2SWft}IIVxjgN)pds~27Cvsfk=-mG1G-{kEnr|Ofu0XrjpNZK#Dtajk` z1)G#J_|s}VpM3;s7|}U(Ag5Ie15@O*3gXh)HMrAiXiUM?G$L-8|0;csrfhS(w*lgwVJzIPAgwwm6Y$l(|_h$;k}jalP2@MnjQQ$j_07FyWpv1 RYt43r*G-?g#xS8M5dey($!-7u diff --git a/certs/dev-test.jks b/certs/dev-test.jks deleted file mode 100644 index b344179e1dea587cad38ee1f462991cf8d3cdcad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3492 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 encAliases; + private List encAliases = new ArrayList<>(); - private List signAliases; + private List signAliases = new ArrayList<>(); + + private List keyAliases = new ArrayList<>(); private TokenProperties token; - + private List services = new ArrayList<>(); - + private List endpoints = new ArrayList<>(); @Data @@ -69,33 +70,19 @@ public static final class GatewayDownload { @Data public static final class TokenProperties { - public static final String ALGORITHM_ELLIPTIC_CURVE = "ES"; - - // use "ks" (keyStore) or "config" - private String provider; - private String issuer; private String type; - private String algorithm; + private TokenInitializeProperties initialize; + } - private int keysize; + @Data + public static final class TokenInitializeProperties { private int validity; - - private String publicKey; - - private String privateKey; - - private String keyAlgorithm; - - public SignatureAlgorithm getSignatureAlgorithm() { - final String name = String.format("%s%d", this.algorithm.toUpperCase(), this.keysize); - return SignatureAlgorithm.forName(name); - } } - + @Data public static final class ServiceProperties { diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java index f6bc0ee..721adb5 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java @@ -60,6 +60,8 @@ public static final class PublicKeyJwkIdentityResponse { private String kid; private String alg; + + private String use; } @Data diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java index fc8b258..8d38637 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java @@ -26,5 +26,7 @@ public enum KeyType { VALIDATION_DECORATOR_ENC_KEY, - VALIDATION_DECORATOR_SIGN_KEY; + VALIDATION_DECORATOR_SIGN_KEY, + + VALIDATION_DECORATOR_KEY; } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java index 35cb3aa..bac69c5 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java @@ -22,22 +22,14 @@ import eu.europa.ec.dgc.validation.decorator.config.DgcProperties; import eu.europa.ec.dgc.validation.decorator.exception.DccException; -import eu.europa.ec.dgc.validation.decorator.exception.NotImplementedException; -import eu.europa.ec.dgc.validation.decorator.exception.UncheckedInvalidKeySpecException; -import eu.europa.ec.dgc.validation.decorator.exception.UncheckedNoSuchAlgorithmException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; +import io.jsonwebtoken.SignatureAlgorithm; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; import java.time.Instant; -import java.util.Base64; import java.util.Collections; import java.util.Date; import java.util.Map; @@ -50,10 +42,6 @@ public class AccessTokenService { public static final String TOKEN_PREFIX = "Bearer "; - private static final String KEY_PROVIDER = "ks"; - - private static final String CONFIG_PROVIDER = "config"; - private final DgcProperties properties; private final KeyProvider keyProvider; @@ -103,8 +91,11 @@ public String buildAccessToken(final String subject) { */ public Map parseAccessToken(String token) { final String tokenContent = token.startsWith(TOKEN_PREFIX) ? token.replace(TOKEN_PREFIX, "") : token; + final String activeSignKey = this.keyProvider.getActiveSignKey(); + final PublicKey publicKey = this.keyProvider.receiveCertificate(activeSignKey).getPublicKey(); + final Jws parsedToken = Jwts.parser() - .setSigningKey(this.getPublicKey()) + .setSigningKey(publicKey) .requireIssuer(properties.getToken().getIssuer()) .parseClaimsJws(tokenContent); final Claims body = parsedToken.getBody(); @@ -135,71 +126,18 @@ public boolean isValid(final String token) { } private JwtBuilder getAccessTokenBuilder() { + final String activeSignKey = this.keyProvider.getActiveSignKey(); + final PrivateKey privateKey = this.keyProvider.receivePrivateKey(activeSignKey); + final String algorithm = this.keyProvider.getAlg(activeSignKey); + final SignatureAlgorithm signatureAlgorithm = io.jsonwebtoken.SignatureAlgorithm.valueOf(algorithm); + final String keyId = this.keyProvider.getKid(activeSignKey); + final int validity = properties.getToken().getInitialize().getValidity(); + return Jwts.builder() - .signWith(properties.getToken().getSignatureAlgorithm(), this.getPrivateKey()) + .signWith(signatureAlgorithm, privateKey) .setHeaderParam("typ", properties.getToken().getType()) - .setHeaderParam("kid", this.getKeyId()) + .setHeaderParam("kid", keyId) .setIssuer(properties.getToken().getIssuer()) - .setExpiration(new Date(Instant.now().plusSeconds(properties.getToken().getValidity()).toEpochMilli())); - } - - private PublicKey parsePublicKey(String privateKeyBase64) { - try { - final byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64); - final X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); - final KeyFactory kf = KeyFactory.getInstance(this.getKeyAlgorithm()); - return kf.generatePublic(spec); - } catch (NoSuchAlgorithmException e) { - throw new UncheckedNoSuchAlgorithmException(e); - } catch (InvalidKeySpecException e) { - throw new UncheckedInvalidKeySpecException(e); - } - } - - private PrivateKey parsePrivateKey(String privateKeyBase64) { - try { - final byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64); - final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); - final KeyFactory kf = KeyFactory.getInstance(this.getKeyAlgorithm()); - return kf.generatePrivate(spec); - } catch (NoSuchAlgorithmException e) { - throw new UncheckedNoSuchAlgorithmException(e); - } catch (InvalidKeySpecException e) { - throw new UncheckedInvalidKeySpecException(e); - } - } - - private String getKeyAlgorithm() { - return properties.getToken().getKeyAlgorithm(); - } - - private String getKeyId() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return "config"; - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.getKid(keyProvider.getActiveSignKey()); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); - } - - private PublicKey getPublicKey() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return this.parsePublicKey(this.properties.getToken().getPublicKey()); - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.receiveCertificate(keyProvider.getActiveSignKey()).getPublicKey(); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); - } - - private PrivateKey getPrivateKey() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return this.parsePrivateKey(this.properties.getToken().getPrivateKey()); - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.receivePrivateKey(keyProvider.getActiveSignKey()); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); + .setExpiration(new Date(Instant.now().plusSeconds(validity).toEpochMilli())); } } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java index 46e4ca9..fd1c6ca 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java @@ -47,7 +47,7 @@ public class IdentityService { private static final String VERIFICATION_TYPE = "JsonWebKey2020"; - private static final String PUBLIC_KEY_ALGORITM = "ES256"; + private final DgcProperties dgcProperties; @@ -114,15 +114,16 @@ private List getServices(final String element, final St } private PublicKeyJwkIdentityResponse buildPublicKey(String keyName) { - final Certificate certificate = keyProvider.receiveCertificate(keyName); - final PublicKeyJwkIdentityResponse publicKeyJwk = new PublicKeyJwkIdentityResponse(); + final Certificate certificate = keyProvider.receiveCertificate(keyName); try { + final PublicKeyJwkIdentityResponse publicKeyJwk = new PublicKeyJwkIdentityResponse(); publicKeyJwk.setX5c(Base64.getEncoder().encodeToString(certificate.getEncoded())); publicKeyJwk.setKid(keyProvider.getKid(keyName)); - publicKeyJwk.setAlg(PUBLIC_KEY_ALGORITM); + publicKeyJwk.setAlg(keyProvider.getAlg(keyName)); + publicKeyJwk.setUse(keyProvider.getKeyUse(keyName).name().toLowerCase()); + return publicKeyJwk; } catch (CertificateEncodingException e) { throw new DccException("Can not encode certificate", e); } - return publicKeyJwk; } } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java index 67c0a52..df90cb9 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java @@ -85,47 +85,52 @@ public void createKeys() throws NoSuchAlgorithmException, IOException, Certifica final Path filePath = Path.of(dgcConfigProperties.getKeyStoreFile()); if (!Files.exists(filePath)) { - log.error("keyfile not found on: {} please adapt the configuration property: issuance.keyStoreFile", + final String msg = String.format( + "keyfile not found on '%s' please adapt the configuration property: issuance.keyStoreFile", filePath); - throw new DccException("keyfile not found on: " + filePath - + " please adapt the configuration property: issuance.keyStoreFile"); + log.error(msg); + throw new DccException(msg); } - final CertificateUtils certificateUtils = new CertificateUtils(); final KeyStore keyStore = KeyStore.getInstance("JKS"); final char[] keyStorePassword = dgcConfigProperties.getKeyStorePassword().toCharArray(); try (InputStream is = new FileInputStream(dgcConfigProperties.getKeyStoreFile())) { final char[] privateKeyPassword = dgcConfigProperties.getPrivateKeyPassword().toCharArray(); keyStore.load(is, privateKeyPassword); - KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection(keyStorePassword); - - for (String alias : this.getKeyNames(KeyType.ALL)) { - final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, keyPassword); - - if (privateKeyEntry != null) { - final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); - PrivateKey privateKey = privateKeyEntry.getPrivateKey(); - certificates.put(alias, cert); - privateKeys.put(alias, privateKey); - - final String kid = certificateUtils.getCertKid((X509Certificate) cert); - kids.put(alias, kid); - kidToName.put(kid, alias); - - if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.1")) { - algs.put(alias, "RS256"); - } else if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.10")) { - algs.put(alias, "PS256"); - } else if (cert.getSigAlgOID().contains("1.2.840.10045.4.3.2")) { - algs.put(alias, "ES256"); - } else { - throw new NotImplementedException(String.format("SigAlg OID '{}'", cert.getSigAlgOID())); + final KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection(keyStorePassword); + + for (final String alias : this.getKeyNames(KeyType.ALL)) { + if (keyStore.isKeyEntry(alias)) { + final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, keyPassword); + if (privateKeyEntry != null) { + final PrivateKey privateKey = privateKeyEntry.getPrivateKey(); + privateKeys.put(alias, privateKey); } - } + } + final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + this.handleCertificate(alias, cert); } } } + private void handleCertificate(final String alias, final X509Certificate cert) { + this.certificates.put(alias, cert); + + final String kid = new CertificateUtils().getCertKid((X509Certificate) cert); + this.kids.put(alias, kid); + this.kidToName.put(kid, alias); + + if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.1")) { + this.algs.put(alias, "RS256"); + } else if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.10")) { + this.algs.put(alias, "PS256"); + } else if (cert.getSigAlgOID().contains("1.2.840.10045.4.3.2")) { + this.algs.put(alias, "ES256"); + } else { + throw new NotImplementedException(String.format("SigAlg OID '{}'", cert.getSigAlgOID())); + } + } + @Override public Certificate receiveCertificate(String keyName) { return certificates.get(keyName); @@ -146,9 +151,13 @@ public List getKeyNames(KeyType type) { case VALIDATION_DECORATOR_SIGN_KEY: keyNames.addAll(dgcConfigProperties.getSignAliases()); break; + case VALIDATION_DECORATOR_KEY: + keyNames.addAll(dgcConfigProperties.getKeyAliases()); + break; case ALL: keyNames.addAll(dgcConfigProperties.getEncAliases()); keyNames.addAll(dgcConfigProperties.getSignAliases()); + keyNames.addAll(dgcConfigProperties.getKeyAliases()); break; default: throw new NotImplementedException(String.format("Key type '%s'", type)); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1440429..4235278 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,22 +20,26 @@ dgc: serviceUrl: http://localhost:8080 keyStoreFile: certs/dev-decorator.jks keyStorePassword: changeMe - privateKeyPassword: changeMe - activeSignKey: validationdecoratorsignkey + privateKeyPassword: changeMe + #### activeSignKey must be one of signAliases + activeSignKey: ValidationDecoratorSignKey-1 encAliases: - - validationdecoratorenckey + - ValidationDecoratorEncKey-1 signAliases: - - validationdecoratorsignkey + - ValidationDecoratorSignKey-1 + keyAliases: + - AccessTokenServiceKey-1 + - ServiceProviderKey-1 + - CancellationServiceKey-1 + - StatusServiceKey-1 + - DccValidatorServiceKey-1 + token: - provider: config #### use "ks" (keyStore) or "config" issuer: Validation Decorator Dervice type: JWT - algorithm: ES - keysize: 256 - validity: 3600 #### in seconds. Expiration is calculated - publicKey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIPrtYsW9+Juwp/mt7h8FJ3LgFRIUl2Vlmcl1DUm5gNHl0LnHIL4Jff6mg6yVhehdQiMvkhUtTvmFIUWONSJEnw== - privateKey: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBSuPIbykwH24sjQsTneeN6EyjiA1NK5W7uca+HxmGmWw== - keyAlgorithm: EC + initialize: + #### in seconds. Expiration is calculated + validity: 3600 #### Validation Service services: - id: ${dgc.serviceUrl}/identity/ValidationService @@ -44,8 +48,8 @@ dgc: name: Validation Service #### Validation Decorator endpoints: - - id: ${dgc.serviceUrl}/identity/AccessCredentialService - type: AccessCredentialService + - id: ${dgc.serviceUrl}/identity/AccessTokenService + type: AccessTokenService serviceEndpoint: ${dgc.serviceUrl}/token name: Validation Decorator Token - id: ${dgc.serviceUrl}/identity/ServiceProvider From cd0563a5dae3c830852f1239f63f61791b0de10b Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 12:48:28 +0200 Subject: [PATCH 6/6] remove old JKS --- docker/CloudFoundry | 1 - docker/Native | 1 - 2 files changed, 2 deletions(-) diff --git a/docker/CloudFoundry b/docker/CloudFoundry index 6de0cff..019ab02 100644 --- a/docker/CloudFoundry +++ b/docker/CloudFoundry @@ -6,7 +6,6 @@ FROM nginx:alpine COPY --from=build ./app /app COPY ./nginx/default.conf.template /etc/nginx/conf.d/default.conf COPY ./entrypoint/entrypoint.sh /entrypoint.sh -COPY ./certs/dev-test.jks /certs/dev-test.jks COPY ./certs/dev-decorator.jks /certs/dev-decorator.jks RUN apk --no-cache add openjdk11-jre EXPOSE 80 diff --git a/docker/Native b/docker/Native index e6d6f51..4073959 100644 --- a/docker/Native +++ b/docker/Native @@ -1,6 +1,5 @@ FROM adoptopenjdk:11-jre-hotspot COPY ./target/*.jar /app/app.jar -COPY ./certs/dev-test.jks /app/certs/dev-test.jks COPY ./certs/dev-decorator.jks /certs/dev-decorator.jks WORKDIR /app ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./app.jar" ]