diff --git a/ReleaseNotes.md b/ReleaseNotes.md index b98d301..f1f654d 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,7 @@ +# Release 3.0.4 + +- fix missing iat and iss in signedJwks structure + # Release 3.0.2 - structure of signed Jwks of relying party fixed diff --git a/gsi-coverage-report/pom.xml b/gsi-coverage-report/pom.xml index 3c49a6e..762db6a 100644 --- a/gsi-coverage-report/pom.xml +++ b/gsi-coverage-report/pom.xml @@ -6,7 +6,7 @@ de.gematik.idp gemSekIdp-global - 3.0.2 + 3.0.4 ../pom.xml diff --git a/gsi-server/pom.xml b/gsi-server/pom.xml index dcaef7f..23e56d0 100644 --- a/gsi-server/pom.xml +++ b/gsi-server/pom.xml @@ -7,13 +7,13 @@ de.gematik.idp gemSekIdp-global - 3.0.2 + 3.0.4 ../pom.xml gsi-server - 3.0.2 + 3.0.4 jar gsi-server diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/FlowBeanCreation.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/FlowBeanCreation.java index b6ed4b7..f81ba57 100644 --- a/gsi-server/src/main/java/de/gematik/idp/gsi/server/FlowBeanCreation.java +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/FlowBeanCreation.java @@ -17,6 +17,7 @@ package de.gematik.idp.gsi.server; import de.gematik.idp.gsi.server.services.EntityStatementBuilder; +import de.gematik.idp.gsi.server.services.JwksBuilder; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,4 +30,9 @@ public class FlowBeanCreation { public EntityStatementBuilder entityStatementBuilder() { return new EntityStatementBuilder(); } + + @Bean + public JwksBuilder jwksBuilder() { + return new JwksBuilder(); + } } diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/KeyConfiguration.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/KeyConfiguration.java index 280a57f..03afd98 100644 --- a/gsi-server/src/main/java/de/gematik/idp/gsi/server/KeyConfiguration.java +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/KeyConfiguration.java @@ -41,23 +41,22 @@ public class KeyConfiguration implements KeyConfigurationBase { private final GsiConfiguration gsiConfiguration; @Bean - public FederationPrivKey entityStatementSigKey() { - return getFederationPrivKey(gsiConfiguration.getSigKeyConfig()); + public FederationPrivKey esSigKey() { + return getFederationPrivKey(gsiConfiguration.getEsSigKeyConfig()); } @Bean public FederationPrivKey tokenSigKey() { - return getFederationPrivKey(gsiConfiguration.getTokenKeyConfig()); + return getFederationPrivKey(gsiConfiguration.getTokenSigKeyConfig()); } @Bean - public IdpJwtProcessor jwtProcessorSigKey() { - return new IdpJwtProcessor( - entityStatementSigKey().getIdentity(), entityStatementSigKey().getKeyId()); + public IdpJwtProcessor jwtProcessorEsSigKey() { + return new IdpJwtProcessor(esSigKey().getIdentity(), esSigKey().getKeyId()); } @Bean - public IdpJwtProcessor jwtProcessorTokenKey() { + public IdpJwtProcessor jwtProcessorTokenSigKey() { return new IdpJwtProcessor(tokenSigKey().getIdentity(), tokenSigKey().getKeyId()); } diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/configuration/GsiConfiguration.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/configuration/GsiConfiguration.java index 37bb007..ee1149d 100644 --- a/gsi-server/src/main/java/de/gematik/idp/gsi/server/configuration/GsiConfiguration.java +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/configuration/GsiConfiguration.java @@ -36,7 +36,7 @@ public class GsiConfiguration { private String serverUrl; private String fedmasterUrl; - private KeyConfig sigKeyConfig; - private KeyConfig tokenKeyConfig; + private KeyConfig esSigKeyConfig; + private KeyConfig tokenSigKeyConfig; private String loglevel; } diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/controller/FedIdpController.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/controller/FedIdpController.java index 19e3c67..b043079 100644 --- a/gsi-server/src/main/java/de/gematik/idp/gsi/server/controller/FedIdpController.java +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/controller/FedIdpController.java @@ -54,6 +54,7 @@ import de.gematik.idp.gsi.server.services.AuthenticationService; import de.gematik.idp.gsi.server.services.EntityStatementBuilder; import de.gematik.idp.gsi.server.services.EntityStatementRpService; +import de.gematik.idp.gsi.server.services.JwksBuilder; import de.gematik.idp.gsi.server.services.SektoralIdpAuthenticator; import de.gematik.idp.gsi.server.services.ServerUrlService; import de.gematik.idp.gsi.server.token.IdTokenBuilder; @@ -75,7 +76,6 @@ import lombok.extern.slf4j.Slf4j; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -103,13 +103,13 @@ public class FedIdpController { private final SektoralIdpAuthenticator sektoralIdpAuthenticator; private final AuthenticationService authenticationService; private final ServerUrlService serverUrlService; - private final IdpJwtProcessor jwtProcessorSigKey; - private final IdpJwtProcessor jwtProcessorTokenKey; + private final IdpJwtProcessor jwtProcessorEsSigKey; + private final IdpJwtProcessor jwtProcessorTokenSigKey; private final ObjectMapper objectMapper; private final GsiConfiguration gsiConfiguration; - private final ResourceLoader resourceLoader; + private final JwksBuilder jwksBuilder; - @Autowired FederationPrivKey entityStatementSigKey; + @Autowired FederationPrivKey esSigKey; @Autowired FederationPrivKey tokenSigKey; // TODO: delete oldest entry @@ -127,7 +127,7 @@ private static void setNoCacheHeader(final HttpServletResponse response) { produces = "application/entity-statement+jwt;charset=UTF-8") public String getEntityStatement() { return JwtHelper.signJson( - jwtProcessorSigKey, + jwtProcessorEsSigKey, objectMapper, entityStatementBuilder.buildEntityStatement( serverUrlService.determineServerUrl(), gsiConfiguration.getFedmasterUrl()), @@ -138,9 +138,9 @@ public String getEntityStatement() { @GetMapping(value = FED_SIGNED_JWKS_ENDPOINT, produces = "application/jwk-set+json;charset=UTF-8") public String getSignedJwks() { return JwtHelper.signJson( - jwtProcessorSigKey, + jwtProcessorEsSigKey, objectMapper, - JwtHelper.getJwks(entityStatementSigKey, tokenSigKey), + jwksBuilder.build(serverUrlService.determineServerUrl()), "jwk-set+json"); } @@ -300,7 +300,7 @@ public TokenResponse getTokensForCode( final IdTokenBuilder idTokenBuilder = new IdTokenBuilder( - jwtProcessorTokenKey, + jwtProcessorTokenSigKey, serverUrlService.determineServerUrl(), session.getRequestedScopes(), session.getFachdienstNonce(), diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/data/SignedJwksBody.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/data/SignedJwksBody.java new file mode 100644 index 0000000..d6d03a9 --- /dev/null +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/data/SignedJwksBody.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.idp.gsi.server.data; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import de.gematik.idp.data.IdpKeyDescriptor; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class SignedJwksBody { + + private String iss; + private long iat; + private List keys; +} diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/EntityStatementBuilder.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/EntityStatementBuilder.java index 6384630..db73381 100644 --- a/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/EntityStatementBuilder.java +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/EntityStatementBuilder.java @@ -37,7 +37,7 @@ public class EntityStatementBuilder { private static final int ENTITY_STATEMENT_TTL_DAYS = 7; - @Autowired FederationPrivKey entityStatementSigKey; + @Autowired FederationPrivKey esSigKey; @Autowired FederationPrivKey tokenSigKey; public EntityStatement buildEntityStatement(final String serverUrl, final String fedmasterUrl) { @@ -47,7 +47,7 @@ public EntityStatement buildEntityStatement(final String serverUrl, final String .iat(currentTime.toEpochSecond()) .iss(serverUrl) .sub(serverUrl) - .jwks(JwtHelper.getJwks(entityStatementSigKey, tokenSigKey)) + .jwks(JwtHelper.getJwks(esSigKey, tokenSigKey)) .authorityHints(new String[] {fedmasterUrl}) .metadata(getMetadata(serverUrl)) .build(); diff --git a/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/JwksBuilder.java b/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/JwksBuilder.java new file mode 100644 index 0000000..85bc05e --- /dev/null +++ b/gsi-server/src/main/java/de/gematik/idp/gsi/server/services/JwksBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.idp.gsi.server.services; + +import de.gematik.idp.data.FederationPrivKey; +import de.gematik.idp.data.JwtHelper; +import de.gematik.idp.gsi.server.data.SignedJwksBody; +import java.time.ZonedDateTime; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; + +@RequiredArgsConstructor +@Slf4j +public class JwksBuilder { + + @Autowired FederationPrivKey esSigKey; + @Autowired FederationPrivKey tokenSigKey; + + public SignedJwksBody build(final String serverUrl) { + final ZonedDateTime currentTime = ZonedDateTime.now(); + return SignedJwksBody.builder() + .iat(currentTime.toEpochSecond()) + .iss(serverUrl) + .keys(JwtHelper.getJwks(esSigKey, tokenSigKey).getKeys()) + .build(); + } +} diff --git a/gsi-server/src/main/resources/application.yml b/gsi-server/src/main/resources/application.yml index 3982f9f..86f5729 100644 --- a/gsi-server/src/main/resources/application.yml +++ b/gsi-server/src/main/resources/application.yml @@ -1,9 +1,9 @@ gsi: - sigKeyConfig: + esSigKeyConfig: fileName: classpath:cert/ref-es-sig.p12 keyId: puk_idp_sig use: sig - tokenKeyConfig: + tokenSigKeyConfig: fileName: classpath:cert/ref-es-sig.p12 keyId: puk_fed_idp_token use: sig diff --git a/gsi-server/src/test/java/de/gematik/idp/gsi/server/configuration/GsiConfigurationTest.java b/gsi-server/src/test/java/de/gematik/idp/gsi/server/configuration/GsiConfigurationTest.java index 02cf8cc..fb94d5f 100644 --- a/gsi-server/src/test/java/de/gematik/idp/gsi/server/configuration/GsiConfigurationTest.java +++ b/gsi-server/src/test/java/de/gematik/idp/gsi/server/configuration/GsiConfigurationTest.java @@ -36,27 +36,26 @@ class GsiConfigurationTest { @Test void fullIntTestComponent() { assertThat(gsiConfiguration).isNotNull(); - assertThat(gsiConfiguration.getSigKeyConfig()).isNotNull(); - assertThat(gsiConfiguration.getTokenKeyConfig()).isNotNull(); + assertThat(gsiConfiguration.getEsSigKeyConfig()).isNotNull(); + assertThat(gsiConfiguration.getTokenSigKeyConfig()).isNotNull(); } @Test void testBuildComponent() { final GsiConfiguration gsiConfig = GsiConfiguration.builder() - .sigKeyConfig(new KeyConfig("a", "b", "c", false)) - .tokenKeyConfig(new KeyConfig("d", "e", "f", false)) + .esSigKeyConfig(new KeyConfig("a", "b", "c", false)) + .tokenSigKeyConfig(new KeyConfig("d", "e", "f", false)) .serverUrl("serverurl") .build(); gsiConfig.setServerUrl("newUrl"); assertThat(gsiConfig).isNotNull(); assertThat(gsiConfig.getServerUrl()).isEqualTo("newUrl"); - assertThat(gsiConfig.getSigKeyConfig()).isNotNull(); - assertThat(gsiConfig.getTokenKeyConfig()).isNotNull(); + assertThat(gsiConfig.getEsSigKeyConfig()).isNotNull(); + assertThat(gsiConfig.getTokenSigKeyConfig()).isNotNull(); assertThat(GsiConfiguration.builder().toString()).hasSizeGreaterThan(0); - assertThatThrownBy( - () -> new KeyConfiguration(resourceLoader, gsiConfig).entityStatementSigKey()) + assertThatThrownBy(() -> new KeyConfiguration(resourceLoader, gsiConfig).esSigKey()) .isInstanceOf(GsiException.class); } } diff --git a/gsi-server/src/test/java/de/gematik/idp/gsi/server/controller/FedIdpControllerTest.java b/gsi-server/src/test/java/de/gematik/idp/gsi/server/controller/FedIdpControllerTest.java index 352e6c3..2ebc4fd 100644 --- a/gsi-server/src/test/java/de/gematik/idp/gsi/server/controller/FedIdpControllerTest.java +++ b/gsi-server/src/test/java/de/gematik/idp/gsi/server/controller/FedIdpControllerTest.java @@ -248,7 +248,7 @@ void signedJwksResponse_JoseHeader() { @Test void signedJwksResponse_BodyClaims() { - assertThat(sigendJwks.extractBodyClaims()).containsOnlyKeys("keys"); + assertThat(sigendJwks.extractBodyClaims()).containsOnlyKeys("keys", "iss", "iat"); } @Test diff --git a/gsi-testsuite/pom.xml b/gsi-testsuite/pom.xml index 537164c..3525570 100644 --- a/gsi-testsuite/pom.xml +++ b/gsi-testsuite/pom.xml @@ -7,12 +7,12 @@ de.gematik.idp gemSekIdp-global - 3.0.2 + 3.0.4 ../pom.xml gsi-testsuite - 3.0.2 + 3.0.4 jar Testsuite fuer sektorale IDPs diff --git a/gsi-testsuite/src/test/resources/features/idpsektoralSignedJwks.feature b/gsi-testsuite/src/test/resources/features/idpsektoralSignedJwks.feature index c0b9d8e..62fb4c9 100644 --- a/gsi-testsuite/src/test/resources/features/idpsektoralSignedJwks.feature +++ b/gsi-testsuite/src/test/resources/features/idpsektoralSignedJwks.feature @@ -84,6 +84,14 @@ Feature: Test signed Jwks of IdpSektoral Given TGR clear recorded messages And Send Get Request to "${signed_jwks_uri}" And TGR find request to path ".*" + Then TGR current response at "$.body.body" matches as JSON: + """ + { + iss: '.*', + iat: "${json-unit.ignore}", + keys: "${json-unit.ignore}" + } + """ Then TGR current response at "$.body.body.keys.0" matches as JSON: """ { diff --git a/pom.xml b/pom.xml index d6e8eb9..529b1c2 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ de.gematik.idp gemSekIdp-global - 3.0.2 + 3.0.4 pom gsi - gematik sektoraler IDP