diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 51cfc367..00000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index f5d73387..0eb47666 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,4 @@ build/
!/tools/*.sh
certs/*
-.DS_Store
+**/.DS_Store
diff --git a/CODEOWNERS b/CODEOWNERS
index 0f8a2d3b..b7948371 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -5,5 +5,4 @@
# For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/.
# These are the default owners for the whole content of this repository. The default owners are automatically added as reviewers when you open a pull request, unless different owners are specified in the file.
-* @eu-digital-green-certificates/dgc-gateway-members
-* @ascheibal @f11h @litlfred
+* @ascheibal @f11h @litlfred @tence
diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md
index ae39d6cc..2a3dc66b 100644
--- a/THIRD-PARTY.md
+++ b/THIRD-PARTY.md
@@ -11,28 +11,29 @@ ThirdParty Licenses
| Dependency | License |
| --- | --- |
-| ch.qos.logback:logback-classic:1.4.6 | Eclipse Public License - v 1.0 GNU Lesser General Public License |
-| ch.qos.logback:logback-core:1.4.6 | Eclipse Public License - v 1.0 GNU Lesser General Public License |
+| ch.qos.logback:logback-classic:1.4.8 | Eclipse Public License - v 1.0 GNU Lesser General Public License |
+| ch.qos.logback:logback-core:1.4.8 | Eclipse Public License - v 1.0 GNU Lesser General Public License |
| com.apicatalog:titanium-json-ld:1.1.0 | Apache License, Version 2.0 |
-| com.azure:azure-core:1.34.0 | The MIT License (MIT) |
-| com.azure:azure-core-http-netty:1.12.7 | The MIT License (MIT) |
-| com.azure:azure-identity:1.7.0 | The MIT License (MIT) |
-| com.azure:azure-security-keyvault-keys:4.5.2 | The MIT License (MIT) |
-| com.azure:azure-storage-blob:12.20.1 | The MIT License (MIT) |
-| com.azure:azure-storage-common:12.19.1 | The MIT License (MIT) |
-| com.azure:azure-storage-internal-avro:12.5.1 | The MIT License (MIT) |
+| com.azure:azure-core:1.43.0 | The MIT License (MIT) |
+| com.azure:azure-core-http-netty:1.13.7 | The MIT License (MIT) |
+| com.azure:azure-identity:1.10.1 | The MIT License (MIT) |
+| com.azure:azure-json:1.1.0 | The MIT License (MIT) |
+| com.azure:azure-security-keyvault-keys:4.7.0 | The MIT License (MIT) |
+| com.azure:azure-storage-blob:12.24.0 | The MIT License (MIT) |
+| com.azure:azure-storage-common:12.23.0 | The MIT License (MIT) |
+| com.azure:azure-storage-internal-avro:12.9.0 | The MIT License (MIT) |
| com.damnhandy:handy-uri-templates:2.1.8 | The Apache Software License, Version 2.0 |
| com.danubetech:key-formats-java:1.2.0 | Unknown license |
| com.fasterxml:classmate:1.5.1 | Apache License, Version 2.0 |
-| com.fasterxml.jackson.core:jackson-annotations:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.core:jackson-core:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.core:jackson-databind:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.jackson.module:jackson-module-parameter-names:2.14.2 | The Apache Software License, Version 2.0 |
-| com.fasterxml.woodstox:woodstox-core:6.4.0 | The Apache License, Version 2.0 |
+| com.fasterxml.jackson.core:jackson-annotations:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.core:jackson-core:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.core:jackson-databind:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.jackson.module:jackson-module-parameter-names:2.14.3 | The Apache Software License, Version 2.0 |
+| com.fasterxml.woodstox:woodstox-core:6.5.0 | The Apache License, Version 2.0 |
| com.github.everit-org.json-schema:org.everit.json.schema:1.14.2 | Apache License, Version 2.0 |
| com.github.jnr:jffi:1.2.9 | The Apache Software License, Version 2.0 |
| com.github.jnr:jnr-ffi:2.0.5 | The Apache Software License, Version 2.0 |
@@ -48,19 +49,19 @@ ThirdParty Licenses
| com.google.j2objc:j2objc-annotations:1.3 | The Apache Software License, Version 2.0 |
| com.google.protobuf:protobuf-javalite:3.21.12 | BSD-3-Clause |
| com.google.re2j:re2j:1.6 | Go License |
-| com.h2database:h2:2.1.214 | EPL 1.0 MPL 2.0 |
+| com.h2database:h2:2.2.220 | EPL 1.0 MPL 2.0 |
| com.jayway.jsonpath:json-path:2.7.0 | The Apache Software License, Version 2.0 |
-| com.microsoft.azure:msal4j:1.13.3 | MIT License |
-| com.microsoft.azure:msal4j-persistence-extension:1.1.0 | MIT License |
-| com.mysql:mysql-connector-j:8.0.32 | The GNU General Public License, v2 with Universal FOSS Exception, v1.0 |
+| com.microsoft.azure:msal4j:1.13.9 | MIT License |
+| com.microsoft.azure:msal4j-persistence-extension:1.2.0 | MIT License |
+| com.mysql:mysql-connector-j:8.0.33 | The GNU General Public License, v2 with Universal FOSS Exception, v1.0 |
| com.nimbusds:content-type:2.2 | The Apache Software License, Version 2.0 |
-| com.nimbusds:lang-tag:1.6 | The Apache Software License, Version 2.0 |
+| com.nimbusds:lang-tag:1.7 | The Apache Software License, Version 2.0 |
| com.nimbusds:nimbus-jose-jwt:9.9 | The Apache Software License, Version 2.0 |
-| com.nimbusds:oauth2-oidc-sdk:9.35 | Apache License, version 2.0 |
+| com.nimbusds:oauth2-oidc-sdk:10.7.1 | Apache License, version 2.0 |
| com.opencsv:opencsv:5.7.1 | Apache 2 |
| com.squareup.okhttp3:okhttp:4.10.0 | The Apache Software License, Version 2.0 |
| com.squareup.okio:okio-jvm:3.0.0 | The Apache Software License, Version 2.0 |
-| com.sun.istack:istack-commons-runtime:4.1.1 | Eclipse Distribution License - v 1.0 |
+| com.sun.istack:istack-commons-runtime:4.1.2 | Eclipse Distribution License - v 1.0 |
| com.upokecenter:cbor:4.5.2 | CC0-1.0 |
| com.vaadin.external.google:android-json:0.0.20131108.vaadin1 | Apache License 2.0 |
| com.zaxxer:HikariCP:5.0.1 | The Apache Software License, Version 2.0 |
@@ -74,39 +75,39 @@ ThirdParty Licenses
| decentralized-identity:jsonld-common-java:1.0.0 | Unknown license |
| info.weboftrust:ld-signatures-java:1.1.0 | Unknown license |
| io.github.erdtman:java-json-canonicalization:1.1 | Apache License, Version 2.0 |
-| io.github.openfeign:feign-core:12.1 | The Apache Software License, Version 2.0 |
-| io.github.openfeign:feign-httpclient:12.1 | The Apache Software License, Version 2.0 |
-| io.github.openfeign:feign-slf4j:12.1 | The Apache Software License, Version 2.0 |
+| io.github.openfeign:feign-core:12.4 | The Apache Software License, Version 2.0 |
+| io.github.openfeign:feign-httpclient:12.4 | The Apache Software License, Version 2.0 |
+| io.github.openfeign:feign-slf4j:12.4 | The Apache Software License, Version 2.0 |
| io.github.openfeign.form:feign-form:3.8.0 | The Apache Software License, Version 2.0 |
| io.github.openfeign.form:feign-form-spring:3.8.0 | The Apache Software License, Version 2.0 |
-| io.micrometer:micrometer-commons:1.10.5 | The Apache Software License, Version 2.0 |
-| io.micrometer:micrometer-core:1.10.5 | The Apache Software License, Version 2.0 |
-| io.micrometer:micrometer-observation:1.10.5 | The Apache Software License, Version 2.0 |
-| io.micrometer:micrometer-registry-prometheus:1.10.5 | The Apache Software License, Version 2.0 |
-| io.netty:netty-buffer:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-codec:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-codec-dns:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-codec-http:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-codec-http2:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-codec-socks:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-common:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-handler:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-handler-proxy:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-resolver:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-resolver-dns:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-resolver-dns-classes-macos:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-resolver-dns-native-macos:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-tcnative-boringssl-static:2.0.59.Final | The Apache Software License, Version 2.0 |
-| io.netty:netty-tcnative-classes:2.0.59.Final | The Apache Software License, Version 2.0 |
-| io.netty:netty-transport:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-transport-classes-epoll:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-transport-classes-kqueue:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-transport-native-epoll:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-transport-native-kqueue:4.1.90.Final | Apache License, Version 2.0 |
-| io.netty:netty-transport-native-unix-common:4.1.90.Final | Apache License, Version 2.0 |
-| io.projectreactor:reactor-core:3.5.4 | Apache License, Version 2.0 |
-| io.projectreactor.netty:reactor-netty-core:1.1.5 | The Apache Software License, Version 2.0 |
-| io.projectreactor.netty:reactor-netty-http:1.1.5 | The Apache Software License, Version 2.0 |
+| io.micrometer:micrometer-commons:1.10.9 | The Apache Software License, Version 2.0 |
+| io.micrometer:micrometer-core:1.10.9 | The Apache Software License, Version 2.0 |
+| io.micrometer:micrometer-observation:1.10.9 | The Apache Software License, Version 2.0 |
+| io.micrometer:micrometer-registry-prometheus:1.10.9 | The Apache Software License, Version 2.0 |
+| io.netty:netty-buffer:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-codec:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-codec-dns:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-codec-http:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-codec-http2:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-codec-socks:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-common:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-handler:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-handler-proxy:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-resolver:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-resolver-dns:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-resolver-dns-classes-macos:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-resolver-dns-native-macos:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-tcnative-boringssl-static:2.0.61.Final | The Apache Software License, Version 2.0 |
+| io.netty:netty-tcnative-classes:2.0.61.Final | The Apache Software License, Version 2.0 |
+| io.netty:netty-transport:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-transport-classes-epoll:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-transport-classes-kqueue:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-transport-native-epoll:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-transport-native-kqueue:4.1.94.Final | Apache License, Version 2.0 |
+| io.netty:netty-transport-native-unix-common:4.1.94.Final | Apache License, Version 2.0 |
+| io.projectreactor:reactor-core:3.5.8 | Apache License, Version 2.0 |
+| io.projectreactor.netty:reactor-netty-core:1.1.9 | The Apache Software License, Version 2.0 |
+| io.projectreactor.netty:reactor-netty-http:1.1.9 | The Apache Software License, Version 2.0 |
| io.prometheus:simpleclient:0.16.0 | The Apache Software License, Version 2.0 |
| io.prometheus:simpleclient_common:0.16.0 | The Apache Software License, Version 2.0 |
| io.prometheus:simpleclient_tracer_common:0.16.0 | The Apache Software License, Version 2.0 |
@@ -116,7 +117,7 @@ ThirdParty Licenses
| io.swagger.core.v3:swagger-annotations-jakarta:2.2.9 | Apache License 2.0 |
| io.swagger.core.v3:swagger-core-jakarta:2.2.9 | Apache License 2.0 |
| io.swagger.core.v3:swagger-models-jakarta:2.2.9 | Apache License 2.0 |
-| jakarta.activation:jakarta.activation-api:2.1.1 | EDL 1.0 |
+| jakarta.activation:jakarta.activation-api:2.1.2 | EDL 1.0 |
| jakarta.annotation:jakarta.annotation-api:2.1.1 | EPL 2.0 GPL2 w/ CPE |
| jakarta.inject:jakarta.inject-api:2.0.0 | The Apache Software License, Version 2.0 |
| jakarta.persistence:jakarta.persistence-api:3.1.0 | Eclipse Distribution License v. 1.0 Eclipse Public License v. 2.0 |
@@ -128,14 +129,14 @@ ThirdParty Licenses
| joda-time:joda-time:2.10.2 | Apache 2 |
| net.bytebuddy:byte-buddy:1.12.23 | Apache License, Version 2.0 |
| net.bytebuddy:byte-buddy-agent:1.12.23 | Apache License, Version 2.0 |
-| net.java.dev.jna:jna:5.5.0 | Apache License v2.0 LGPL, version 2.1 |
+| net.java.dev.jna:jna:5.13.0 | Apache-2.0 LGPL-2.1-or-later |
| net.java.dev.jna:jna-platform:5.6.0 | Apache License v2.0 LGPL, version 2.1 |
| net.javacrumbs.shedlock:shedlock-core:5.2.0 | The Apache Software License, Version 2.0 |
| net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.2.0 | The Apache Software License, Version 2.0 |
| net.javacrumbs.shedlock:shedlock-spring:5.2.0 | The Apache Software License, Version 2.0 |
| net.jcip:jcip-annotations:1.0 | Unknown license |
-| net.minidev:accessors-smart:2.4.9 | The Apache Software License, Version 2.0 |
-| net.minidev:json-smart:2.4.10 | The Apache Software License, Version 2.0 |
+| net.minidev:accessors-smart:2.4.11 | The Apache Software License, Version 2.0 |
+| net.minidev:json-smart:2.4.11 | The Apache Software License, Version 2.0 |
| org.abstractj.kalium:kalium:0.8.0 | The Apache Software License, Version 2.0 |
| org.antlr:antlr4-runtime:4.10.1 | The BSD License |
| org.apache.commons:commons-collections4:4.4 | Apache License, Version 2.0 |
@@ -145,47 +146,43 @@ ThirdParty Licenses
| org.apache.httpcomponents:httpcore:4.4.16 | Apache License, Version 2.0 |
| org.apache.logging.log4j:log4j-api:2.19.0 | Apache License, Version 2.0 |
| org.apache.logging.log4j:log4j-to-slf4j:2.19.0 | Apache License, Version 2.0 |
-| org.apache.tomcat.embed:tomcat-embed-core:10.1.7 | Apache License, Version 2.0 |
-| org.apache.tomcat.embed:tomcat-embed-el:10.1.7 | Apache License, Version 2.0 |
-| org.apache.tomcat.embed:tomcat-embed-websocket:10.1.7 | Apache License, Version 2.0 |
+| org.apache.tomcat.embed:tomcat-embed-core:10.1.11 | Apache License, Version 2.0 |
+| org.apache.tomcat.embed:tomcat-embed-el:10.1.11 | Apache License, Version 2.0 |
+| org.apache.tomcat.embed:tomcat-embed-websocket:10.1.11 | Apache License, Version 2.0 |
| org.apiguardian:apiguardian-api:1.1.2 | The Apache License, Version 2.0 |
| org.aspectj:aspectjweaver:1.9.19 | Eclipse Public License - v 2.0 |
| org.assertj:assertj-core:3.23.1 | Apache License, Version 2.0 |
| org.bitcoinj:bitcoinj-core:0.16.2 | The Apache Software License, Version 2.0 |
-| org.bouncycastle:bcpkix-jdk15on:1.69 | Bouncy Castle Licence |
-| org.bouncycastle:bcpkix-jdk18on:1.73 | Bouncy Castle Licence |
-| org.bouncycastle:bcprov-jdk15on:1.69 | Bouncy Castle Licence |
-| org.bouncycastle:bcprov-jdk15to18:1.69 | Bouncy Castle Licence |
-| org.bouncycastle:bcprov-jdk18on:1.73 | Bouncy Castle Licence |
-| org.bouncycastle:bcutil-jdk15on:1.69 | Bouncy Castle Licence |
-| org.bouncycastle:bcutil-jdk18on:1.73 | Bouncy Castle Licence |
+| org.bouncycastle:bcpkix-jdk18on:1.76 | Bouncy Castle Licence |
+| org.bouncycastle:bcprov-jdk18on:1.76 | Bouncy Castle Licence |
+| org.bouncycastle:bcutil-jdk18on:1.76 | Bouncy Castle Licence |
| org.checkerframework:checker-compat-qual:2.5.5 | GNU General Public License, version 2 (GPL2), with the classpath exception The MIT License |
| org.checkerframework:checker-qual:3.5.0 | The MIT License |
| org.codehaus.woodstox:stax2-api:4.2.1 | The BSD License |
-| org.eclipse.angus:angus-activation:2.0.0 | EDL 1.0 |
+| org.eclipse.angus:angus-activation:2.0.1 | EDL 1.0 |
| org.glassfish:jakarta.json:2.0.0 | Eclipse Public License 2.0 GNU General Public License, version 2 with the GNU Classpath Exception |
-| org.glassfish.jaxb:jaxb-core:4.0.2 | Eclipse Distribution License - v 1.0 |
-| org.glassfish.jaxb:jaxb-runtime:4.0.2 | Eclipse Distribution License - v 1.0 |
-| org.glassfish.jaxb:txw2:4.0.2 | Eclipse Distribution License - v 1.0 |
+| org.glassfish.jaxb:jaxb-core:4.0.3 | Eclipse Distribution License - v 1.0 |
+| org.glassfish.jaxb:jaxb-runtime:4.0.3 | Eclipse Distribution License - v 1.0 |
+| org.glassfish.jaxb:txw2:4.0.3 | Eclipse Distribution License - v 1.0 |
| org.hamcrest:hamcrest:2.2 | BSD License 3 |
| org.hdrhistogram:HdrHistogram:2.1.12 | BSD-2-Clause Public Domain, per Creative Commons CC0 |
| org.hibernate.common:hibernate-commons-annotations:6.0.6.Final | GNU Library General Public License v2.1 or later |
| org.hibernate.orm:hibernate-core:6.1.7.Final | GNU Library General Public License v2.1 or later |
-| org.hibernate.validator:hibernate-validator:8.0.0.Final | Apache License 2.0 |
+| org.hibernate.validator:hibernate-validator:8.0.1.Final | Apache License 2.0 |
| org.jboss:jandex:2.4.2.Final | Apache License, Version 2.0 |
-| org.jboss.logging:jboss-logging:3.5.0.Final | Apache License, version 2.0 |
+| org.jboss.logging:jboss-logging:3.5.3.Final | Apache License 2.0 |
| org.jetbrains:annotations:13.0 | The Apache Software License, Version 2.0 |
| org.jetbrains.kotlin:kotlin-stdlib:1.7.22 | The Apache License, Version 2.0 |
| org.jetbrains.kotlin:kotlin-stdlib-common:1.7.22 | The Apache License, Version 2.0 |
| org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.22 | The Apache License, Version 2.0 |
| org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.22 | The Apache License, Version 2.0 |
| org.json:json:20230227 | Public Domain |
-| org.junit.jupiter:junit-jupiter:5.9.2 | Eclipse Public License v2.0 |
-| org.junit.jupiter:junit-jupiter-api:5.9.2 | Eclipse Public License v2.0 |
-| org.junit.jupiter:junit-jupiter-engine:5.9.2 | Eclipse Public License v2.0 |
-| org.junit.jupiter:junit-jupiter-params:5.9.2 | Eclipse Public License v2.0 |
-| org.junit.platform:junit-platform-commons:1.9.2 | Eclipse Public License v2.0 |
-| org.junit.platform:junit-platform-engine:1.9.2 | Eclipse Public License v2.0 |
+| org.junit.jupiter:junit-jupiter:5.9.3 | Eclipse Public License v2.0 |
+| org.junit.jupiter:junit-jupiter-api:5.9.3 | Eclipse Public License v2.0 |
+| org.junit.jupiter:junit-jupiter-engine:5.9.3 | Eclipse Public License v2.0 |
+| org.junit.jupiter:junit-jupiter-params:5.9.3 | Eclipse Public License v2.0 |
+| org.junit.platform:junit-platform-commons:1.9.3 | Eclipse Public License v2.0 |
+| org.junit.platform:junit-platform-engine:1.9.3 | Eclipse Public License v2.0 |
| org.latencyutils:LatencyUtils:2.0.3 | Public Domain, per Creative Commons CC0 |
| org.liquibase:liquibase-core:4.17.2 | Apache License, Version 2.0 |
| org.mapstruct:mapstruct:1.5.5.Final | The Apache Software License, Version 2.0 |
@@ -199,7 +196,7 @@ ThirdParty Licenses
| org.ow2.asm:asm-tree:5.0.3 | BSD |
| org.ow2.asm:asm-util:5.0.3 | BSD |
| org.postgresql:postgresql:42.5.4 | BSD-2-Clause |
-| org.projectlombok:lombok:1.18.26 | The MIT License |
+| org.projectlombok:lombok:1.18.28 | The MIT License |
| org.reactivestreams:reactive-streams:1.0.4 | MIT-0 |
| org.semver4j:semver4j:4.3.0 | The MIT License |
| org.skyscreamer:jsonassert:1.5.1 | The Apache Software License, Version 2.0 |
@@ -208,49 +205,49 @@ ThirdParty Licenses
| org.springdoc:springdoc-openapi-starter-common:2.1.0 | The Apache License, Version 2.0 |
| org.springdoc:springdoc-openapi-starter-webmvc-api:2.1.0 | The Apache License, Version 2.0 |
| org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0 | The Apache License, Version 2.0 |
-| org.springframework:spring-aop:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-aspects:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-beans:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-context:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-core:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-expression:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-jcl:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-jdbc:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-orm:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-test:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-tx:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-web:6.0.7 | Apache License, Version 2.0 |
-| org.springframework:spring-webmvc:6.0.7 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-actuator:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-actuator-autoconfigure:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-autoconfigure:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-actuator:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-aop:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-data-jpa:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-jdbc:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-json:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-logging:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-test:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-tomcat:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-validation:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-starter-web:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-test:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.boot:spring-boot-test-autoconfigure:3.0.5 | Apache License, Version 2.0 |
-| org.springframework.cloud:spring-cloud-commons:4.0.2 | Apache License, Version 2.0 |
-| org.springframework.cloud:spring-cloud-context:4.0.2 | Apache License, Version 2.0 |
-| org.springframework.cloud:spring-cloud-openfeign-core:4.0.2 | Apache License, Version 2.0 |
-| org.springframework.cloud:spring-cloud-starter:4.0.2 | Apache License, Version 2.0 |
-| org.springframework.cloud:spring-cloud-starter-openfeign:4.0.2 | Apache License, Version 2.0 |
-| org.springframework.data:spring-data-commons:3.0.4 | Apache License, Version 2.0 |
-| org.springframework.data:spring-data-jpa:3.0.4 | Apache License, Version 2.0 |
-| org.springframework.security:spring-security-config:6.0.2 | Apache License, Version 2.0 |
-| org.springframework.security:spring-security-core:6.0.2 | Apache License, Version 2.0 |
-| org.springframework.security:spring-security-crypto:6.0.2 | Apache License, Version 2.0 |
-| org.springframework.security:spring-security-rsa:1.0.11.RELEASE | Apache 2.0 |
-| org.springframework.security:spring-security-test:6.0.2 | Apache License, Version 2.0 |
-| org.springframework.security:spring-security-web:6.0.2 | Apache License, Version 2.0 |
+| org.springframework:spring-aop:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-aspects:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-beans:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-context:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-core:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-expression:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-jcl:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-jdbc:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-orm:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-test:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-tx:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-web:6.0.11 | Apache License, Version 2.0 |
+| org.springframework:spring-webmvc:6.0.11 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-actuator:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-actuator-autoconfigure:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-autoconfigure:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-actuator:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-aop:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-data-jpa:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-jdbc:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-json:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-logging:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-test:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-tomcat:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-validation:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-starter-web:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-test:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.boot:spring-boot-test-autoconfigure:3.0.9 | Apache License, Version 2.0 |
+| org.springframework.cloud:spring-cloud-commons:4.0.4 | Apache License, Version 2.0 |
+| org.springframework.cloud:spring-cloud-context:4.0.4 | Apache License, Version 2.0 |
+| org.springframework.cloud:spring-cloud-openfeign-core:4.0.4 | Apache License, Version 2.0 |
+| org.springframework.cloud:spring-cloud-starter:4.0.4 | Apache License, Version 2.0 |
+| org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4 | Apache License, Version 2.0 |
+| org.springframework.data:spring-data-commons:3.0.8 | Apache License, Version 2.0 |
+| org.springframework.data:spring-data-jpa:3.0.8 | Apache License, Version 2.0 |
+| org.springframework.security:spring-security-config:6.0.5 | Apache License, Version 2.0 |
+| org.springframework.security:spring-security-core:6.0.5 | Apache License, Version 2.0 |
+| org.springframework.security:spring-security-crypto:6.0.5 | Apache License, Version 2.0 |
+| org.springframework.security:spring-security-rsa:1.0.12.RELEASE | Apache 2.0 |
+| org.springframework.security:spring-security-test:6.0.5 | Apache License, Version 2.0 |
+| org.springframework.security:spring-security-web:6.0.5 | Apache License, Version 2.0 |
| org.webjars:swagger-ui:4.18.2 | Apache 2.0 |
| org.xmlunit:xmlunit-core:2.9.1 | The Apache Software License, Version 2.0 |
| org.yaml:snakeyaml:1.33 | Apache License, Version 2.0 |
diff --git a/docker-compose.yml b/docker-compose.yml
index 0024c7a9..49d6ccd6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,21 +1,21 @@
version: '3'
services:
- mysql:
- image: mysql/mysql-server:5.7
+ postgres:
+ image: postgres
ports:
- - 3306:3306
+ - 5432:5432
environment:
- - MYSQL_DATABASE=dgc
- - MYSQL_ROOT_PASSWORD=admin # do not use this for production deployments
- - MYSQL_USER=dgc_adm
- - MYSQL_PASSWORD=admin # do not use this for production deployments
+ - POSTGRES_PASSWORD=admin # do not use this for production deployments
+ - PGPASSWORD=admin # do not use this for production deployments
+ - POSTGRES_USER=postgres
+ - POSTGRES_DB=postgres
networks:
persistence:
ddcc-gateway:
build: .
- image: world-health-organization/ddcc-gateway
+ image: smarttrustnetworkgateway:latest
volumes:
- ./certs:/ec/prod/app/san/dgc
- ./logs:/logs
@@ -23,9 +23,12 @@ services:
- 8080:8080
environment:
- SERVER_PORT=8080
- - SPRING_PROFILES_ACTIVE=mysql,docker
- - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/dgc
- - SPRING_DATASOURCE_USERNAME=dgc_adm
+ - SPRING_PROFILES_ACTIVE=log2console,local
+ - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/postgres
+ - SPRING_DATASOURCE_DRIVERCLASSNAME=org.postgresql.Driver
+ - SPRING_DATASOURCE_JNDI_NAME=false
+ - SPRING_JPA_DATABASEPLATFORM=org.hibernate.dialect.PostgreSQLDialect
+ - SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=admin # do not use this for production deployments
- DGC_TRUSTANCHOR_KEYSTOREPATH=/ec/prod/app/san/dgc/ta.jks
- DGC_TRUSTANCHOR_KEYSTOREPASS=dgcg-p4ssw0rd # do not use this for production deployments
@@ -33,7 +36,7 @@ services:
- DGC_FEDERATION_KEYSTOREPATH=/ec/prod/app/san/dgc/ta.jks #TODO: change to federation.jks
- DGC_FEDERATION_KEYSTOREPASSWORD=dgcg-p4ssw0rd # do not use this for production deployments
depends_on:
- - mysql
+ - postgres
networks:
backend:
persistence:
diff --git a/owasp/suppressions.xml b/owasp/suppressions.xml
index 70fff6e0..01e8848c 100644
--- a/owasp/suppressions.xml
+++ b/owasp/suppressions.xml
@@ -44,4 +44,9 @@
CVE-2023-1370
CVE-2023-24998
+
+ No Fix available
+ CVE-2023-36414
+ CVE-2023-36415
+
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java
index 5c650986..65c2574b 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/config/DgcConfigProperties.java
@@ -52,6 +52,8 @@ public class DgcConfigProperties {
private CloudmersiveConfig cloudmersive = new CloudmersiveConfig();
+ private CountryCodeMap countryCodeMap = new CountryCodeMap();
+
@Getter
@Setter
public static class DidConfig {
@@ -170,4 +172,10 @@ public static class Federation {
public static class SignerInformation {
private int deleteThreshold = 14;
}
+
+ @Getter
+ @Setter
+ public static class CountryCodeMap {
+ private Map virtualCountries = new HashMap<>();
+ }
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/FederatedEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/FederatedEntity.java
index dac491da..b433d178 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/entity/FederatedEntity.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/FederatedEntity.java
@@ -41,7 +41,7 @@ public abstract class FederatedEntity {
@JoinColumn(name = "source_gateway_id", referencedColumnName = "gateway_id")
private FederationGatewayEntity sourceGateway;
- @Column(name = "domain", columnDefinition = "varchar(10) DEFAULT 'DCC'")
+ @Column(name = "domain", columnDefinition = "varchar(32) DEFAULT 'DCC'")
String domain = "DCC";
@Column(name = "version", columnDefinition = "int default 1", nullable = false)
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/RevocationBatchEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/RevocationBatchEntity.java
index b3fca6c3..37f34c7f 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/entity/RevocationBatchEntity.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/RevocationBatchEntity.java
@@ -28,13 +28,14 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
-import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.type.descriptor.jdbc.LongVarcharJdbcType;
@Getter
@Setter
@@ -97,7 +98,7 @@ public class RevocationBatchEntity {
* The Signed CMS with the batch.
*/
@Column(name = "signed_batch", length = 1_024_000)
- @Lob
+ @JdbcType(LongVarcharJdbcType.class)
private String signedBatch;
/**
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java
index 3a506fa8..00f4c54b 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/entity/ValuesetEntity.java
@@ -23,12 +23,14 @@
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
-import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.type.descriptor.jdbc.LongVarcharJdbcType;
+
@Getter
@Setter
@@ -45,8 +47,8 @@ public class ValuesetEntity {
/**
* Signature of the TrustAnchor.
*/
- @Column(name = "json", nullable = false, length = 1024000)
- @Lob
+ @Column(name = "json", nullable = false, length = 1_024_000)
+ @JdbcType(LongVarcharJdbcType.class)
String json;
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/model/TrustedCertificateTrustList.java b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustedCertificateTrustList.java
index 15d4cac2..06adb56b 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/model/TrustedCertificateTrustList.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/model/TrustedCertificateTrustList.java
@@ -20,6 +20,7 @@
package eu.europa.ec.dgc.gateway.model;
+import java.security.cert.X509Certificate;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -52,4 +53,6 @@ public class TrustedCertificateTrustList {
private Integer version;
+ private X509Certificate parsedCertificate;
+
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java
index a974da9b..7f0c5a46 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/SignerInformationRepository.java
@@ -30,8 +30,6 @@
public interface SignerInformationRepository extends JpaRepository {
- List getAllBySourceGatewayIsNullAndDomainIs(String domain);
-
@Query("SELECT s FROM SignerInformationEntity s WHERE "
+ "(:ignoreGroup = true OR s.certificateType IN (:group)) AND "
+ "(:ignoreCountry = true OR s.country IN (:country)) AND "
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java b/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java
index 00159ed3..9e2439c1 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/repository/TrustedPartyRepository.java
@@ -30,7 +30,8 @@
public interface TrustedPartyRepository extends JpaRepository {
- List getBySourceGatewayIsNullAndDomainIs(String domain);
+ List getBySourceGatewayIsNullAndDomainIsAndCertificateTypeIsIn(
+ String domain, List types);
List getByCountryAndCertificateType(String country, TrustedPartyEntity.CertificateType type);
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListDto.java
index acdb1106..db3661bf 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListDto.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListDto.java
@@ -21,10 +21,12 @@
package eu.europa.ec.dgc.gateway.restapi.dto.did;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.List;
import lombok.Data;
@Data
+@JsonPropertyOrder({"@context", "id", "controller", "verificationMethod"})
public class DidTrustListDto {
@JsonProperty("@context")
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListEntryDto.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListEntryDto.java
index 12e97d65..25deb323 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListEntryDto.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/dto/did/DidTrustListEntryDto.java
@@ -21,9 +21,16 @@
package eu.europa.ec.dgc.gateway.restapi.dto.did;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Base64;
import java.util.List;
import lombok.Data;
-import lombok.experimental.SuperBuilder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
@Data
public class DidTrustListEntryDto {
@@ -36,18 +43,24 @@ public class DidTrustListEntryDto {
private PublicKeyJwk publicKeyJwk;
- @Data
- @SuperBuilder
- private abstract static class PublicKeyJwk {
+ @NoArgsConstructor
+ @Setter
+ @Getter
+ public abstract static class PublicKeyJwk {
@JsonProperty("kty")
private String keyType;
@JsonProperty("x5c")
private List encodedX509Certificates;
+
+ private PublicKeyJwk(String keyType, List encodedX509Certificates) {
+ this.keyType = keyType;
+ this.encodedX509Certificates = new ArrayList<>(encodedX509Certificates);
+ }
}
- @Data
- @SuperBuilder
+ @Getter
+ @Setter
public static class EcPublicKeyJwk extends PublicKeyJwk {
@JsonProperty("crv")
@@ -58,6 +71,51 @@ public static class EcPublicKeyJwk extends PublicKeyJwk {
@JsonProperty("y")
private String valueY;
+
+ /**
+ * Instantiate EC PublicKey JWK Class.
+ *
+ * @param ecPublicKey EC Public Key that should be wrapped.
+ * @param base64EncodedCertificates List of Base64 encoded Certificates assigned to provided Public Key.
+ * They will be added within x5c property of JWK.
+ */
+ public EcPublicKeyJwk(ECPublicKey ecPublicKey, List base64EncodedCertificates) {
+ super("EC", base64EncodedCertificates);
+ valueX = Base64.getEncoder().encodeToString(ecPublicKey.getW().getAffineX().toByteArray());
+ valueY = Base64.getEncoder().encodeToString(ecPublicKey.getW().getAffineY().toByteArray());
+
+ ECNamedCurveSpec curveSpec = (ECNamedCurveSpec) ecPublicKey.getParams();
+ switch (curveSpec.getName()) {
+ case "prime256v1" -> curve = "P-256";
+ case "prime384v1" -> curve = "P-384";
+ case "prime521v1" -> curve = "P-521";
+ default -> curve = "UNKNOWN CURVE";
+ }
+ }
+ }
+
+ @Getter
+ @Setter
+ public static class RsaPublicKeyJwk extends PublicKeyJwk {
+
+ @JsonProperty("e")
+ private String valueE;
+
+ @JsonProperty("n")
+ private String valueN;
+
+ /**
+ * Instantiate RSA PublicKey JWK Class.
+ *
+ * @param rsaPublicKey RSA Public Key that should be wrapped.
+ * @param base64EncodedCertificates List of Base64 encoded Certificates assigned to provided Public Key.
+ * They will be added within x5c property of JWK.
+ */
+ public RsaPublicKeyJwk(RSAPublicKey rsaPublicKey, List base64EncodedCertificates) {
+ super("RSA", base64EncodedCertificates);
+ valueN = Base64.getEncoder().encodeToString(rsaPublicKey.getModulus().toByteArray());
+ valueE = Base64.getEncoder().encodeToString(rsaPublicKey.getPublicExponent().toByteArray());
+ }
}
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java
index 44faee95..11956bf9 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/SignerInformationService.java
@@ -70,24 +70,15 @@ public class SignerInformationService {
private static final String MDC_PROP_UPLOAD_CERT_THUMBPRINT = "uploadCertThumbprint";
private static final String MDC_PROP_CSCA_CERT_THUMBPRINT = "cscaCertThumbprint";
- /**
- * Method to query persistence layer for all stored non federated SignerInformation.
- *
- * @return List of SignerInformation
- */
- public List getNonFederatedSignerInformation() {
- return signerInformationRepository.getAllBySourceGatewayIsNullAndDomainIs("DCC");
- }
/**
- * Method to query persistence layer for SignerInformation filtered by Type.
+ * Method to query persistence layer for SignerInformation filtered by Type = DSC.
*
- * @param type type to filter for
* @return List of SignerInformation
*/
- public List getNonFederatedSignerInformation(
- SignerInformationEntity.CertificateType type) {
- return signerInformationRepository.getByCertificateTypeAndSourceGatewayIsNullAndDomainIs(type, "DCC");
+ public List getNonFederatedEuDscSignerInformation() {
+ return signerInformationRepository.getByCertificateTypeAndSourceGatewayIsNullAndDomainIs(
+ SignerInformationEntity.CertificateType.DSC, "DCC");
}
/**
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java
index 7b823da2..023a5465 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustListService.java
@@ -53,8 +53,8 @@ public class TrustListService {
*/
public List getTrustList() {
return mergeAndConvert(
- trustedPartyService.getNonFederatedTrustedParties(),
- signerInformationService.getNonFederatedSignerInformation()
+ trustedPartyService.getNonFederatedEuDscTrustedParties(),
+ signerInformationService.getNonFederatedEuDscSignerInformation()
);
}
@@ -68,11 +68,11 @@ public List getTrustList(TrustListType type) {
if (type == TrustListType.DSC) {
return mergeAndConvert(
Collections.emptyList(),
- signerInformationService.getNonFederatedSignerInformation(SignerInformationEntity.CertificateType.DSC)
+ signerInformationService.getNonFederatedEuDscSignerInformation()
);
} else {
return mergeAndConvert(
- trustedPartyService.getNonFederatedTrustedParties(map(type)),
+ trustedPartyService.getNonFederatedEuDscTrustedParties(map(type)),
Collections.emptyList()
);
}
@@ -156,7 +156,8 @@ private TrustedCertificateTrustList convertTrustedCertificate(TrustedPartyEntity
? trustedPartyEntity.getSourceGateway().getGatewayId() : null,
trustedPartyEntity.getUuid(),
trustedPartyEntity.getDomain(),
- trustedPartyEntity.getVersion()
+ trustedPartyEntity.getVersion(),
+ trustedPartyService.getX509CertificateFromEntity(trustedPartyEntity)
);
}
@@ -174,7 +175,8 @@ private TrustedCertificateTrustList convertTrustedCertificate(SignerInformationE
? signerInformationEntity.getSourceGateway().getGatewayId() : null,
signerInformationEntity.getUuid(),
signerInformationEntity.getDomain(),
- signerInformationEntity.getVersion()
+ signerInformationEntity.getVersion(),
+ signerInformationService.getX509CertificateFromEntity(signerInformationEntity)
);
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedIssuerService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedIssuerService.java
index 737be48c..a2d9e180 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedIssuerService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedIssuerService.java
@@ -162,7 +162,8 @@ private boolean validateTrustedIssuerIntegrity(TrustedIssuerEntity trustedIssuer
// verify signature
SignedStringMessageParser parser = new SignedStringMessageParser(
trustedIssuerEntity.getSignature(),
- Base64.getEncoder().encodeToString(getHashData(trustedIssuerEntity).getBytes(StandardCharsets.UTF_8)));
+ Base64.getEncoder().encodeToString(
+ getHashData(trustedIssuerEntity).getBytes(StandardCharsets.UTF_8)));
if (parser.getParserState() != SignedMessageParser.ParserState.SUCCESS) {
DgcMdc.put(MDC_PROP_PARSER_STATE, parser.getParserState().name());
@@ -227,12 +228,11 @@ public TrustedIssuerEntity addFederatedTrustedIssuer(String country,
return trustedIssuerEntity;
}
-
private String getHashData(TrustedIssuerEntity entity) {
- return entity.getUuid() + HASH_SEPARATOR
- + entity.getCountry() + HASH_SEPARATOR
- + entity.getName() + HASH_SEPARATOR
- + entity.getUrl() + HASH_SEPARATOR
- + entity.getUrlType().name() + HASH_SEPARATOR;
+ return entity.getCountry() + HASH_SEPARATOR
+ + entity.getName() + HASH_SEPARATOR
+ + entity.getUrl() + HASH_SEPARATOR
+ + entity.getUrlType().name();
}
}
+
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java
index 609b9562..9821e176 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/TrustedPartyService.java
@@ -65,9 +65,11 @@ public class TrustedPartyService {
*
* @return List holding the found certificates.
*/
- public List getNonFederatedTrustedParties() {
+ public List getNonFederatedEuDscTrustedParties() {
- return trustedPartyRepository.getBySourceGatewayIsNullAndDomainIs("DCC")
+ return trustedPartyRepository.getBySourceGatewayIsNullAndDomainIsAndCertificateTypeIsIn("DCC",
+ List.of(TrustedPartyEntity.CertificateType.UPLOAD, TrustedPartyEntity.CertificateType.AUTHENTICATION,
+ TrustedPartyEntity.CertificateType.CSCA))
.stream()
.filter(this::validateCertificateIntegrity)
.collect(Collectors.toList());
@@ -79,7 +81,7 @@ public List getNonFederatedTrustedParties() {
* @param type type to filter for.
* @return List holding the found certificates.
*/
- public List getNonFederatedTrustedParties(TrustedPartyEntity.CertificateType type) {
+ public List getNonFederatedEuDscTrustedParties(TrustedPartyEntity.CertificateType type) {
return trustedPartyRepository.getByCertificateTypeAndSourceGatewayIsNullAndDomainIs(type, "DCC")
.stream()
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/did/AzureDidUploader.java b/src/main/java/eu/europa/ec/dgc/gateway/service/did/AzureDidUploader.java
index 47507e7c..393a89ca 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/did/AzureDidUploader.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/did/AzureDidUploader.java
@@ -32,6 +32,7 @@
import com.azure.storage.blob.models.BlobHttpHeaders;
import eu.europa.ec.dgc.gateway.config.DgcConfigProperties;
import java.net.InetSocketAddress;
+import java.text.MessageFormat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.MediaType;
@@ -42,7 +43,7 @@
@Slf4j
public class AzureDidUploader implements DidUploader {
- private final BlobClient blobClient;
+ private final BlobServiceClient blobServiceClient;
private final DgcConfigProperties dgcConfigProperties;
@@ -64,7 +65,7 @@ public AzureDidUploader(DgcConfigProperties dgcConfigProperties) {
HttpClient httpClient = HttpClient.createDefault(httpClientOptions);
- BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
+ blobServiceClient = new BlobServiceClientBuilder()
.httpClient(httpClient)
.endpoint(dgcConfigProperties.getDid().getAzure().getBlobEndpoint())
.credential(new ClientSecretCredentialBuilder()
@@ -75,10 +76,24 @@ public AzureDidUploader(DgcConfigProperties dgcConfigProperties) {
.build())
.buildClient();
- BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(
- dgcConfigProperties.getDid().getAzure().getBlobContainer());
+ }
- blobClient = blobContainerClient.getBlobClient(dgcConfigProperties.getDid().getAzure().getBlobName());
+ /**
+ * Setup container specific BlobClient.
+ * @param subContainer the name of the container (will be used in lowercase)
+ */
+ public BlobClient getBlobContainerClientForBlobContainer(String subContainer) {
+ BlobContainerClient blobContainerClient;
+ if (subContainer == null) {
+ blobContainerClient = blobServiceClient.getBlobContainerClient(
+ dgcConfigProperties.getDid().getAzure().getBlobContainer());
+ } else {
+ blobContainerClient = blobServiceClient.getBlobContainerClient(
+ MessageFormat.format("{0}/{1}",
+ dgcConfigProperties.getDid().getAzure().getBlobContainer(),
+ subContainer.toLowerCase()));
+ }
+ return blobContainerClient.getBlobClient(dgcConfigProperties.getDid().getAzure().getBlobName());
}
@Override
@@ -88,6 +103,20 @@ public void uploadDid(byte[] content) {
dgcConfigProperties.getDid().getAzure().getBlobContainer(),
dgcConfigProperties.getDid().getAzure().getBlobName());
+ BlobClient blobClient = getBlobContainerClientForBlobContainer(null);
+ blobClient.upload(BinaryData.fromBytes(content), true);
+ blobClient.setHttpHeaders(new BlobHttpHeaders().setContentType(MediaType.APPLICATION_JSON_VALUE));
+ log.info("Upload successful");
+ }
+
+ @Override
+ public void uploadDid(String subContainer, byte[] content) {
+ log.info("Uploading {} bytes as {}{}/{} to Azure BLOB Storage", content.length,
+ dgcConfigProperties.getDid().getAzure().getBlobEndpoint(),
+ dgcConfigProperties.getDid().getAzure().getBlobContainer(),
+ dgcConfigProperties.getDid().getAzure().getBlobName());
+
+ BlobClient blobClient = getBlobContainerClientForBlobContainer(subContainer);
blobClient.upload(BinaryData.fromBytes(content), true);
blobClient.setHttpHeaders(new BlobHttpHeaders().setContentType(MediaType.APPLICATION_JSON_VALUE));
log.info("Upload successful");
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidTrustListService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidTrustListService.java
index 8dc7b3e2..ef793993 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidTrustListService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidTrustListService.java
@@ -27,9 +27,10 @@
import eu.europa.ec.dgc.gateway.entity.SignerInformationEntity;
import eu.europa.ec.dgc.gateway.entity.TrustedIssuerEntity;
import eu.europa.ec.dgc.gateway.entity.TrustedPartyEntity;
+import eu.europa.ec.dgc.gateway.model.TrustedCertificateTrustList;
import eu.europa.ec.dgc.gateway.restapi.dto.did.DidTrustListDto;
import eu.europa.ec.dgc.gateway.restapi.dto.did.DidTrustListEntryDto;
-import eu.europa.ec.dgc.gateway.service.SignerInformationService;
+import eu.europa.ec.dgc.gateway.service.TrustListService;
import eu.europa.ec.dgc.gateway.service.TrustedIssuerService;
import eu.europa.ec.dgc.gateway.service.TrustedPartyService;
import foundation.identity.jsonld.ConfigurableDocumentLoader;
@@ -37,41 +38,50 @@
import info.weboftrust.ldsignatures.jsonld.LDSecurityKeywords;
import info.weboftrust.ldsignatures.signer.JsonWebSignature2020LdSigner;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.net.URI;
+import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import java.security.cert.X509Certificate;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.MissingResourceException;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
-import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
+
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnProperty("dgc.did.enableDidGeneration")
public class DidTrustListService {
+ private static final String SEPARATOR_COLON = ":";
+
+ private static final String SEPARATOR_FRAGMENT = "#";
+
private static final List DID_CONTEXTS = List.of(
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1");
- private final TrustedPartyService trustedPartyService;
-
- private final SignerInformationService signerInformationService;
-
private final TrustedIssuerService trustedIssuerService;
+ private final TrustListService trustListService;
+
private final DgcConfigProperties configProperties;
private final ByteSigner byteSigner;
@@ -80,15 +90,17 @@ public class DidTrustListService {
private final ObjectMapper objectMapper;
+ private final TrustedPartyService trustedPartyService;
+
/**
* Create and upload DID Document holding Uploaded DSC and Trusted Issuer.
*/
- @Scheduled(cron = "0 0 * * * *")
+ @Scheduled(cron = "${dgc.trustlist.cron}")
@SchedulerLock(name = "didTrustListGenerator")
public void job() {
String trustList;
try {
- trustList = generateTrustList();
+ trustList = generateTrustList(null);
} catch (Exception e) {
log.error("Failed to generate DID-TrustList: {}", e.getMessage());
return;
@@ -100,76 +112,101 @@ public void job() {
log.error("Failed to Upload DID-TrustList: {}", e.getMessage());
return;
}
+
+ List countries = trustedPartyService.getCountryList();
+
+ for (String country : countries) {
+ String countryTrustList = null;
+ List countryAsList = List.of(country);
+ String countryAsSubcontainer = getCountryAsLowerCaseAlpha3(country);
+ if (countryAsSubcontainer != null) {
+ try {
+ countryTrustList = generateTrustList(countryAsList);
+ } catch (Exception e) {
+ log.error("Failed to generate DID-TrustList for country {} : {}", country, e.getMessage());
+ continue;
+ }
+
+ try {
+ didUploader.uploadDid(countryAsSubcontainer, countryTrustList.getBytes(StandardCharsets.UTF_8));
+ } catch (Exception e) {
+ log.error("Failed to Upload DID-TrustList for country {} : {}", country, e.getMessage());
+ }
+ }
+ }
+
log.info("Finished DID Export Process");
}
- private String generateTrustList() throws Exception {
+ private String getCountryAsLowerCaseAlpha3(String country) {
+ if (country == null || country.length() != 2 && country.length() != 3) {
+ return null;
+ } else if (country.length() == 3) {
+ return country.toLowerCase();
+ }
+
+ return configProperties.getCountryCodeMap().getVirtualCountries()
+ .compute(country, (alpha2, alpha3) -> {
+ if (alpha3 != null) {
+ return alpha3.toLowerCase();
+ }
+
+ try {
+ return new Locale("en", alpha2).getISO3Country().toLowerCase();
+ } catch (MissingResourceException e) {
+ log.error("Country Code to alpha 3 conversion issue for country {} : {}",
+ country, e.getMessage());
+ return null;
+ }
+ });
+ }
+
+ private String generateTrustList(List countries) throws Exception {
DidTrustListDto trustList = new DidTrustListDto();
trustList.setContext(DID_CONTEXTS);
trustList.setId(configProperties.getDid().getDidId());
trustList.setController(configProperties.getDid().getDidController());
trustList.setVerificationMethod(new ArrayList<>());
+ if (countries != null && !countries.isEmpty()) {
+ trustList.setId(configProperties.getDid().getDidId()
+ + SEPARATOR_COLON
+ + getCountryAsLowerCaseAlpha3(countries.get(0)));
+ trustList.setController(configProperties.getDid().getDidController()
+ + SEPARATOR_COLON
+ + getCountryAsLowerCaseAlpha3(countries.get(0)));
+ }
// Add DSC
- List certs = signerInformationService.getSignerInformation(
- null, null, null, configProperties.getDid().getIncludeFederated());
-
- for (SignerInformationEntity cert : certs) {
- DidTrustListEntryDto.EcPublicKeyJwk.EcPublicKeyJwkBuilder, ?> jwkBuilder =
- DidTrustListEntryDto.EcPublicKeyJwk.builder();
-
- X509Certificate x509 = signerInformationService.getX509CertificateFromEntity(cert);
-
+ List certs = trustListService.getTrustedCertificateTrustList(
+ SignerInformationEntity.CertificateType.stringValues(),
+ countries,
+ null,
+ configProperties.getDid().getIncludeFederated()
+ );
- if (x509.getPublicKey() instanceof ECPublicKey publicKey) {
+ for (TrustedCertificateTrustList cert : certs) {
- jwkBuilder.valueX(Base64.getEncoder().encodeToString(publicKey.getW().getAffineX().toByteArray()));
- jwkBuilder.valueY(Base64.getEncoder().encodeToString(publicKey.getW().getAffineY().toByteArray()));
+ PublicKey publicKey = cert.getParsedCertificate().getPublicKey();
- ECNamedCurveSpec curveSpec = (ECNamedCurveSpec) publicKey.getParams();
- if (curveSpec.getName().equals("prime256v1")) {
- jwkBuilder.curve("P-256");
- } else if (curveSpec.getName().equals("prime384v1")) {
- jwkBuilder.curve("P-384");
- } else if (curveSpec.getName().equals("prime521v1")) {
- jwkBuilder.curve("P-521");
- }
-
- jwkBuilder.keyType("EC");
- jwkBuilder.encodedX509Certificates(new ArrayList<>(List.of(cert.getRawData())));
- DidTrustListEntryDto.EcPublicKeyJwk jwk = jwkBuilder.build();
-
- Optional csca =
- trustedPartyService.getCertificate(
- cert.getCountry(), TrustedPartyEntity.CertificateType.CSCA).stream()
- .map(trustedPartyService::getX509CertificateFromEntity)
- .filter(tp -> tp.getSubjectX500Principal().equals(x509.getIssuerX500Principal()))
- .findFirst();
-
- if (csca.isPresent()) {
- jwk.getEncodedX509Certificates().add(Base64.getEncoder().encodeToString(csca.get().getEncoded()));
- }
+ if (publicKey instanceof RSAPublicKey rsaPublicKey) {
+ addTrustListEntry(trustList, cert,
+ new DidTrustListEntryDto.RsaPublicKeyJwk(rsaPublicKey, List.of(cert.getCertificate())));
- DidTrustListEntryDto trustListEntry = new DidTrustListEntryDto();
- trustListEntry.setType("JsonWebKey2020");
- trustListEntry.setId(configProperties.getDid().getTrustListIdPrefix() + cert.getKid());
- trustListEntry.setController(configProperties.getDid().getTrustListControllerPrefix());
- trustListEntry.setPublicKeyJwk(jwk);
-
- trustList.getVerificationMethod().add(trustListEntry);
+ } else if (publicKey instanceof ECPublicKey ecPublicKey) {
+ addTrustListEntry(trustList, cert,
+ new DidTrustListEntryDto.EcPublicKeyJwk(ecPublicKey, List.of(cert.getCertificate())));
} else {
- log.error("Public Key is not EC Public Key for cert {} of country {}",
- cert.getThumbprint(),
- cert.getCountry());
+ log.error("Public Key is not RSA or EC Public Key for cert {} of country {}",
+ cert.getThumbprint(),
+ cert.getCountry());
}
-
}
// Add TrustedIssuer
trustedIssuerService.search(
- null, null, configProperties.getDid().getIncludeFederated()).stream()
+ null, countries, configProperties.getDid().getIncludeFederated()).stream()
.filter(trustedIssuer -> trustedIssuer.getUrlType() == TrustedIssuerEntity.UrlType.DID)
.forEach(trustedIssuer -> trustList.getVerificationMethod().add(trustedIssuer.getUrl()));
@@ -188,7 +225,6 @@ private String generateTrustList() throws Exception {
if (didContextFile == null) {
log.error("Failed to load DID-Context Document for {}: No Mapping to local JSON-File.", didContext);
-
}
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
@@ -208,4 +244,42 @@ private String generateTrustList() throws Exception {
return jsonLdObject.toJson();
}
+
+ private void addTrustListEntry(DidTrustListDto trustList,
+ TrustedCertificateTrustList cert,
+ DidTrustListEntryDto.PublicKeyJwk publicKeyJwk)
+ throws CertificateEncodingException, UnsupportedEncodingException {
+ Optional csca = searchForIssuer(cert);
+
+ if (csca.isPresent()) {
+ publicKeyJwk.getEncodedX509Certificates()
+ .add(Base64.getEncoder().encodeToString(csca.get().getParsedCertificate().getEncoded()));
+ }
+
+ DidTrustListEntryDto trustListEntry = new DidTrustListEntryDto();
+ trustListEntry.setType("JsonWebKey2020");
+ trustListEntry.setId(configProperties.getDid().getTrustListIdPrefix()
+ + SEPARATOR_COLON
+ + getCountryAsLowerCaseAlpha3(cert.getCountry())
+ + SEPARATOR_FRAGMENT
+ + URLEncoder.encode(cert.getKid(), StandardCharsets.UTF_8));
+ trustListEntry.setController(configProperties.getDid().getTrustListControllerPrefix()
+ + SEPARATOR_COLON + getCountryAsLowerCaseAlpha3(cert.getCountry()));
+ trustListEntry.setPublicKeyJwk(publicKeyJwk);
+
+ trustList.getVerificationMethod().add(trustListEntry);
+ }
+
+ @NotNull
+ private Optional searchForIssuer(TrustedCertificateTrustList cert) {
+ // Search for Issuer of DSC
+ return trustListService.getTrustedCertificateTrustList(
+ List.of(TrustedPartyEntity.CertificateType.CSCA.name()),
+ List.of(cert.getCountry()),
+ List.of(cert.getDomain()),
+ configProperties.getDid().getIncludeFederated()).stream()
+ .filter(tp -> tp.getParsedCertificate().getSubjectX500Principal()
+ .equals(cert.getParsedCertificate().getIssuerX500Principal()))
+ .findFirst();
+ }
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidUploader.java b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidUploader.java
index e79271b7..4d74bb60 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidUploader.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DidUploader.java
@@ -24,4 +24,6 @@ public interface DidUploader {
void uploadDid(byte[] content);
+ void uploadDid(String subContainer, byte[] content);
+
}
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DummyDidUploader.java b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DummyDidUploader.java
index 4949b519..2b3478a8 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/did/DummyDidUploader.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/did/DummyDidUploader.java
@@ -34,4 +34,9 @@ public void uploadDid(byte[] content) {
log.info("Uploaded {} bytes", content.length);
}
+ @Override
+ public void uploadDid(String subContainer, byte[] content) {
+ log.info("Uploaded {} bytes to subContainer {}", content.length, subContainer);
+ }
+
}
diff --git a/src/main/resources/.DS_Store b/src/main/resources/.DS_Store
deleted file mode 100644
index 4530b279..00000000
Binary files a/src/main/resources/.DS_Store and /dev/null differ
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 435e433f..ba197cfa 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -86,11 +86,20 @@ dgc:
- DCC
- SHC
- CRED
+ - RACSEL-DDVC
+ trustlist:
+ cron: "* */5 * * * *"
did:
enableDidGeneration: false
contextMapping:
"[https://www.w3.org/ns/did/v1]": did_v1.json
"[https://w3id.org/security/suites/jws-2020/v1]": jws-2020_v1.json
+ countryCodeMap:
+ virtualCountries:
+ XA: XXA
+ XB: XXB
+ XO: XXO
+ XL: XCL
cloudmersive:
apiKey: 0a0a0a0a-0a0a-0a0a-0a0a-0a0a0a0a0a0a
url: https://api.cloudmersive.com
diff --git a/src/main/resources/db/changelog.xml b/src/main/resources/db/changelog.xml
index 33cdf04c..b6921e11 100644
--- a/src/main/resources/db/changelog.xml
+++ b/src/main/resources/db/changelog.xml
@@ -19,4 +19,7 @@
+
+
+
diff --git a/src/main/resources/db/changelog/change-data-type-for-valuesets.xml b/src/main/resources/db/changelog/change-data-type-for-valuesets.xml
new file mode 100644
index 00000000..93e52c0d
--- /dev/null
+++ b/src/main/resources/db/changelog/change-data-type-for-valuesets.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/change-revocation-batch-value-type.xml b/src/main/resources/db/changelog/change-revocation-batch-value-type.xml
new file mode 100644
index 00000000..44e697ae
--- /dev/null
+++ b/src/main/resources/db/changelog/change-revocation-batch-value-type.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/src/main/resources/db/changelog/change-supported-length-of-domain.xml b/src/main/resources/db/changelog/change-supported-length-of-domain.xml
new file mode 100644
index 00000000..a65bbcbc
--- /dev/null
+++ b/src/main/resources/db/changelog/change-supported-length-of-domain.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index 268f5e8c..45a45700 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -11,9 +11,7 @@
- timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}",
- pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+', ',
- '}){'"', '\''}", trace="%X{traceId}", span="%X{spanId}", %X%n
+ timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}", pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+', ','}){'"', '\''}", trace="%X{traceId}", span="%X{spanId}", %X%n
utf8
@@ -31,10 +29,7 @@
- timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}",
- pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+',
- ','}){'"', '\''}", exception="%replace(%ex){'[\r\n]+', ', '}", trace="%X{traceId}", span="%X{spanId}",
- %X%n%nopex
+ timestamp="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC}", level="%level", hostname="${HOSTNAME}", pid="${PID:-}", thread="%thread", class="%logger{40}", message="%replace(%replace(%m){'[\r\n]+',','}){'"', '\''}", exception="%replace(%ex){'[\r\n]+', ', '}", trace="%X{traceId}", span="%X{spanId}", %X%n%nopex
utf8
diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java
index b92a6f6a..cc71884c 100644
--- a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java
+++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/TrustListIntegrationTest.java
@@ -98,7 +98,7 @@ class TrustListIntegrationTest {
private static final String countryCode = "EU";
private static final String authCertSubject = "C=" + countryCode;
- X509Certificate certUploadDe, certUploadEu, certCscaDe, certCscaEu, certAuthDe, certAuthEu, certDscDe, certDscEu;
+ X509Certificate certUploadDe, certUploadEu, certCscaDe, certCscaEu, certAuthDe, certAuthEu, certDscDe, certDscEu, certCustomEu, certTrustAnchorEu;
@AfterEach
public void cleanUp() {
@@ -118,9 +118,14 @@ void testData() throws Exception {
certAuthDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE");
certAuthEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU");
+ // Add New TrustedPartyType to check that it will not be returned.
+ certTrustAnchorEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.TRUSTANCHOR, "EU");
+
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ec");
certDscDe = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", "Test");
certDscEu = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", "Test");
+ certCustomEu = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", "Test");
+
signerInformationRepository.save(new SignerInformationEntity(
null,
@@ -146,6 +151,18 @@ void testData() throws Exception {
null
));
+ signerInformationRepository.save(new SignerInformationEntity(
+ null,
+ ZonedDateTime.now(),
+ "DE",
+ certificateUtils.getCertThumbprint(certCustomEu),
+ Base64.getEncoder().encodeToString(certCustomEu.getEncoded()),
+ "sig3",
+ null,
+ SignerInformationEntity.CertificateType.CUSTOM,
+ null
+ ));
+
trustedIssuerRepository.saveAll(List.of(
trustedIssuerTestHelper.createTrustedIssuer("EU", "ICAO"),
trustedIssuerTestHelper.createTrustedIssuer("DE", "NONE"),
@@ -173,6 +190,8 @@ void testTrustListDownloadNoFilter() throws Exception {
.andExpect(c -> assertTrustListItem(c, certUploadEu, "EU", CertificateTypeDto.UPLOAD, null))
.andExpect(c -> assertTrustListItem(c, certAuthDe, "DE", CertificateTypeDto.AUTHENTICATION, null))
.andExpect(c -> assertTrustListItem(c, certAuthEu, "EU", CertificateTypeDto.AUTHENTICATION, null))
+ .andExpect(c -> assertTrustListDoesNotContainItem(c, certCustomEu))
+ .andExpect(c -> assertTrustListDoesNotContainItem(c, certTrustAnchorEu))
.andExpect(c -> assertTrustListLength(c, 8));
}
@@ -556,6 +575,22 @@ void testTrustedIssuerEmpty() throws Exception {
.andExpect(jsonPath("$", hasSize(0)));
}
+ private void assertTrustListDoesNotContainItem(MvcResult result, X509Certificate certificate)
+ throws UnsupportedEncodingException, JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper()
+ .registerModule(new JavaTimeModule());
+ List trustList =
+ objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() {
+ });
+
+ Optional trustListOptional = trustList
+ .stream()
+ .filter(tl -> tl.getKid().equals(certificateUtils.getCertKid(certificate)))
+ .findFirst();
+
+ Assertions.assertFalse(trustListOptional.isPresent());
+ }
+
private void assertTrustListItem(MvcResult result, X509Certificate certificate, String country,
CertificateTypeDto certificateTypeDto, String signature)
throws CertificateEncodingException, UnsupportedEncodingException, JsonProcessingException {
diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java
index a59df119..9678717e 100644
--- a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java
+++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/ValidationRuleIntegrationTest.java
@@ -603,7 +603,7 @@ void testValidationTimestamps3() throws Exception {
validationRule.setValidFrom(ZonedDateTime.now().plus(3, ChronoUnit.DAYS));
validationRule.setValidTo(ZonedDateTime.now()
.plus(6, ChronoUnit.DAYS)
- .minus(1, ChronoUnit.SECONDS));
+ .minus(2, ChronoUnit.HOURS));
String payload = new SignedStringMessageBuilder()
.withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey)
diff --git a/src/test/java/eu/europa/ec/dgc/gateway/service/DidTrustListServiceTest.java b/src/test/java/eu/europa/ec/dgc/gateway/service/DidTrustListServiceTest.java
index b2ef582c..d52fb579 100644
--- a/src/test/java/eu/europa/ec/dgc/gateway/service/DidTrustListServiceTest.java
+++ b/src/test/java/eu/europa/ec/dgc/gateway/service/DidTrustListServiceTest.java
@@ -37,24 +37,30 @@
import eu.europa.ec.dgc.gateway.testdata.TrustedIssuerTestHelper;
import eu.europa.ec.dgc.gateway.testdata.TrustedPartyTestHelper;
import eu.europa.ec.dgc.utils.CertificateUtils;
-import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.security.KeyPairGenerator;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedHashMap;
+import java.util.List;
+
+import foundation.identity.jsonld.JsonLDObject;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -63,7 +69,6 @@
@SpringBootTest
public class DidTrustListServiceTest {
-
@Autowired
ObjectMapper objectMapper;
@@ -100,6 +105,8 @@ public class DidTrustListServiceTest {
X509Certificate certUploadDe, certUploadEu, certCscaDe, certCscaEu, certAuthDe, certAuthEu, certDscDe, certDscEu,
federatedCertDscEx;
+ String certDscDeKid;
+
FederationGatewayEntity federationGateway;
@AfterEach
@@ -107,32 +114,46 @@ public void cleanUp() {
trustedPartyRepository.deleteAll();
signerInformationRepository.deleteAll();
federationGatewayRepository.deleteAll();
+ trustedIssuerRepository.deleteAll();
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.UPLOAD, "DE");
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.UPLOAD, "EU");
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.CSCA, "DE");
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.CSCA, "EU");
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE");
+ trustedPartyTestHelper.clear(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU");
}
- @BeforeEach
- void testData() throws Exception {
+ void testData(CertificateTestUtils.SignerType signerType) throws Exception {
cleanUp();
federationGateway =
- new FederationGatewayEntity(null, ZonedDateTime.now(), "gw-id", "endpoint", "kid", "pk", "impl",
- FederationGatewayEntity.DownloadTarget.FEDERATION, FederationGatewayEntity.Mode.APPEND, "sig", -1L,
- null, null, 0L, null, null);
+ new FederationGatewayEntity(null, ZonedDateTime.now(), "gw-id", "endpoint",
+ "kid", "pk", "impl",
+ FederationGatewayEntity.DownloadTarget.FEDERATION, FederationGatewayEntity.Mode.APPEND, "sig",
+ -1L, null, null, 0L, null,
+ null);
federationGateway = federationGatewayRepository.save(federationGateway);
- certUploadDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "DE");
- certUploadEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "EU");
- certCscaDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "DE");
- certCscaEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "EU");
- certAuthDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE");
- certAuthEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU");
+ certUploadDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "DE", signerType);
+ certUploadEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, "EU", signerType);
+ certCscaDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "DE", signerType);
+ certCscaEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.CSCA, "EU", signerType);
+ certAuthDe = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE", signerType);
+ certAuthEu = trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU", signerType);
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ec");
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(signerType.getSigningAlgorithm());
certDscDe =
- CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", "Test", certCscaDe,
- trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.AUTHENTICATION, "DE"));
+ CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE",
+ "Test", certCscaDe,
+ trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.AUTHENTICATION,
+ "DE", signerType), signerType);
+
+ certDscDeKid = certificateUtils.getCertKid(certDscDe);
certDscEu =
- CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", "Test", certCscaEu,
- trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU"));
+ CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU",
+ "Test", certCscaEu,
+ trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.AUTHENTICATION, "EU", signerType),
+ signerType);
signerInformationRepository.save(new SignerInformationEntity(
null,
@@ -141,7 +162,7 @@ void testData() throws Exception {
certificateUtils.getCertThumbprint(certDscDe),
Base64.getEncoder().encodeToString(certDscDe.getEncoded()),
"sig1",
- "kid1",
+ null, // Don't provide a KID to test that calculated KID will be used
SignerInformationEntity.CertificateType.DSC,
null
));
@@ -158,7 +179,8 @@ void testData() throws Exception {
null
));
- federatedCertDscEx = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EX", "Test");
+ federatedCertDscEx = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EX",
+ "Test", signerType);
SignerInformationEntity federatedDscEntity = new SignerInformationEntity(
null,
ZonedDateTime.now(),
@@ -178,29 +200,40 @@ void testData() throws Exception {
trustedIssuerRepository.save(trustedIssuerTestHelper.createTrustedIssuer("XY", "DCC"));
}
- @Test
- void testTrustList() throws IOException, CertificateEncodingException {
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testTrustList(boolean isEcAlgorithm) throws Exception {
+ if (isEcAlgorithm) {
+ testData(CertificateTestUtils.SignerType.EC);
+ } else {
+ testData(CertificateTestUtils.SignerType.RSA);
+ }
ArgumentCaptor uploadArgumentCaptor = ArgumentCaptor.forClass(byte[].class);
doNothing().when(didUploaderMock).uploadDid(uploadArgumentCaptor.capture());
didTrustListService.job();
- SignedDidTrustListDto parsed = objectMapper.readValue(uploadArgumentCaptor.getValue(), SignedDidTrustListDto.class);
+ SignedDidTrustListDto parsed =
+ objectMapper.readValue(uploadArgumentCaptor.getValue(), SignedDidTrustListDto.class);
Assertions.assertEquals("a", parsed.getId());
Assertions.assertEquals("b", parsed.getController());
Assertions.assertEquals(6, parsed.getVerificationMethod().size());
- assertVerificationMethod(parsed.getVerificationMethod().get(0), "kid1", certDscDe, certCscaDe);
- assertVerificationMethod(parsed.getVerificationMethod().get(1), "kid2", certDscEu, certCscaEu);
- assertVerificationMethod(parsed.getVerificationMethod().get(2), "kid3", federatedCertDscEx, null);
+ assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(), "c" + ":deu" + "#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)),
+ certDscDeKid, certDscDe, certCscaDe, "deu");
+ assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(), "c:xeu#kid2"),
+ "kid2", certDscEu, certCscaEu, "xeu");
+ assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(), "c:xex#kid3"),
+ "kid3", federatedCertDscEx, null, "xex");
Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:DE:issuer"));
Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:EU:issuer"));
Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:XY:issuer"));
Assertions.assertEquals(2, parsed.getContext().size());
Assertions.assertEquals("JsonWebSignature2020", parsed.getProof().getType());
- Assertions.assertTrue(Instant.now().toEpochMilli() - parsed.getProof().getCreated().toInstant().toEpochMilli() < 10000);
+ Assertions.assertTrue(
+ Instant.now().toEpochMilli() - parsed.getProof().getCreated().toInstant().toEpochMilli() < 10000);
Assertions.assertEquals("f", parsed.getProof().getDomain());
Assertions.assertEquals("g", parsed.getProof().getNonce());
Assertions.assertEquals("assertionMethod", parsed.getProof().getProofPurpose());
@@ -208,21 +241,46 @@ void testTrustList() throws IOException, CertificateEncodingException {
Assertions.assertNotNull(parsed.getProof().getJws());
Assertions.assertNotEquals("", parsed.getProof().getJws());
+ //JSON should start with "@context" due to https://www.w3.org/TR/json-ld11-streaming/#key-ordering-required
+ String json = JsonLDObject.fromJson(objectMapper.writeValueAsString(parsed)).toJson();
+ String first10Characters = json.substring(0, Math.min(10, json.length()));
+ Assertions.assertTrue(first10Characters.contains("@context"));
}
- private void assertVerificationMethod(Object in, String kid, X509Certificate dsc, X509Certificate csca)
- throws CertificateEncodingException {
+
+ private Object getVerificationMethodByKid(List