Skip to content

Commit 1ae0ef0

Browse files
authored
Merge pull request #87 from eu-digital-green-certificates/feat/multiple-trustanchors
Feat: Support for multiple TrustAnchors
2 parents 81ac06a + cdda568 commit 1ae0ef0

File tree

7 files changed

+60
-31
lines changed

7 files changed

+60
-31
lines changed

pom.xml

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@
2323
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2424
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
2525
<!-- dependencies -->
26-
<owasp.version>6.5.2</owasp.version>
27-
<spring.boot.version>2.6.2</spring.boot.version>
26+
<owasp.version>6.5.3</owasp.version>
27+
<spring.boot.version>2.6.3</spring.boot.version>
2828
<spring.cloud.version>3.1.0</spring.cloud.version>
2929
<feign.version>11.7</feign.version>
3030
<bcpkix.version>1.70</bcpkix.version>
3131
<lombok.version>1.18.22</lombok.version>
3232
<mapstruct.version>1.4.2.Final</mapstruct.version>
3333
<commonsio.version>2.11.0</commonsio.version>
3434
<cbor.version>4.5.1</cbor.version>
35-
<jackson.version>2.13.0</jackson.version>
35+
<jackson.version>2.13.1</jackson.version>
3636
<mockwebserver.version>4.9.3</mockwebserver.version>
3737
<plugin.checkstyle.version>3.1.2</plugin.checkstyle.version>
3838
<plugin.sonar.version>3.9.1.2184</plugin.sonar.version>
@@ -77,12 +77,6 @@
7777
<version>${spring.boot.version}</version>
7878
<optional>true</optional>
7979
</dependency>
80-
<dependency>
81-
<groupId>org.springframework.boot</groupId>
82-
<artifactId>spring-boot-starter-web</artifactId>
83-
<version>${spring.boot.version}</version>
84-
<optional>true</optional>
85-
</dependency>
8680
<dependency>
8781
<groupId>org.springframework.boot</groupId>
8882
<artifactId>spring-boot-configuration-processor</artifactId>
@@ -93,6 +87,12 @@
9387
<groupId>org.springframework.cloud</groupId>
9488
<artifactId>spring-cloud-starter-openfeign</artifactId>
9589
<version>${spring.cloud.version}</version>
90+
<exclusions>
91+
<exclusion>
92+
<groupId>org.springframework</groupId>
93+
<artifactId>spring-web</artifactId>
94+
</exclusion>
95+
</exclusions>
9696
</dependency>
9797
<dependency>
9898
<groupId>io.github.openfeign</groupId>
@@ -130,6 +130,11 @@
130130
<artifactId>jackson-databind</artifactId>
131131
<version>${jackson.version}</version>
132132
</dependency>
133+
<dependency>
134+
<groupId>com.fasterxml.jackson.datatype</groupId>
135+
<artifactId>jackson-datatype-jsr310</artifactId>
136+
<version>${jackson.version}</version>
137+
</dependency>
133138

134139
<dependency>
135140
<groupId>org.springframework.boot</groupId>
@@ -156,6 +161,11 @@
156161
<artifactId>spring-security-crypto</artifactId>
157162
<version>5.5.1</version>
158163
</dependency>
164+
<dependency>
165+
<groupId>org.springframework</groupId>
166+
<artifactId>spring-web</artifactId>
167+
<version>5.3.15</version>
168+
</dependency>
159169
</dependencies>
160170

161171
<build>

src/main/java/eu/europa/ec/dgc/gateway/connector/DgcGatewayConnectorUtils.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class DgcGatewayConnectorUtils {
7373
private final KeyStore trustAnchorKeyStore;
7474

7575
@Setter
76-
private X509CertificateHolder trustAnchor;
76+
private List<X509CertificateHolder> trustAnchors;
7777

7878

7979
@PostConstruct
@@ -87,7 +87,7 @@ void init() throws KeyStoreException, CertificateEncodingException, IOException
8787
log.error("Could not find TrustAnchor Certificate in Keystore");
8888
throw new KeyStoreException("Could not find TrustAnchor Certificate in Keystore");
8989
}
90-
trustAnchor = certificateUtils.convertCertificate(trustAnchorCert);
90+
trustAnchors = Collections.singletonList(certificateUtils.convertCertificate(trustAnchorCert));
9191
}
9292

9393
public boolean trustListItemSignedByCa(TrustListItemDto certificate, X509CertificateHolder ca) {
@@ -142,7 +142,7 @@ public boolean trustListItemSignedByCa(TrustListItemDto certificate, Map<String,
142142
.anyMatch(ca -> trustListItemSignedByCa(certificate, ca));
143143
}
144144

145-
boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, X509CertificateHolder trustAnchor) {
145+
boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, List<X509CertificateHolder> trustAnchors) {
146146
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
147147
trustListItem.getSignature(), trustListItem.getRawData());
148148

@@ -155,7 +155,7 @@ boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, X509Certificat
155155
return false;
156156
}
157157

158-
return parser.getSigningCertificate().equals(trustAnchor);
158+
return trustAnchors.stream().anyMatch(trustAnchor -> parser.getSigningCertificate().equals(trustAnchor));
159159
}
160160

161161
X509CertificateHolder getCertificateFromTrustListItem(TrustListItemDto trustListItem) {
@@ -187,7 +187,7 @@ public List<X509CertificateHolder> fetchCertificatesAndVerifyByTrustAnchor(Certi
187187

188188
return downloadedCertificates.getBody().stream()
189189
.filter(this::checkThumbprintIntegrity)
190-
.filter(c -> this.checkTrustAnchorSignature(c, trustAnchor))
190+
.filter(c -> this.checkTrustAnchorSignature(c, trustAnchors))
191191
.map(this::getCertificateFromTrustListItem)
192192
.filter(Objects::nonNull)
193193
.collect(Collectors.toList());

src/main/java/eu/europa/ec/dgc/gateway/connector/DgcGatewayDownloadConnectorBuilder.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import java.security.UnrecoverableKeyException;
1818
import java.security.cert.Certificate;
1919
import java.security.cert.CertificateException;
20+
import java.util.ArrayList;
2021
import java.util.Arrays;
22+
import java.util.List;
2123
import java.util.UUID;
2224
import javax.net.ssl.SSLContext;
2325
import lombok.AllArgsConstructor;
@@ -60,7 +62,7 @@ public class DgcGatewayDownloadConnectorBuilder {
6062
private String url;
6163
private KeyStore mtlsTrustStore;
6264
private KeyStore mtlsKeyStore;
63-
private X509CertificateHolder trustAnchor;
65+
private final List<X509CertificateHolder> trustAnchors = new ArrayList<>();
6466
private HttpHost proxy;
6567
private int cacheMagAge = -1;
6668
private boolean enableSslHostnameValidation = true;
@@ -79,13 +81,24 @@ public DgcGatewayDownloadConnectorBuilder withUrl(String url) {
7981
}
8082

8183
/**
82-
* Set the TrustAnchor to validate the received entities.
84+
* Add multiple TrustAnchors to validate the received entities.
85+
* Required.
86+
*
87+
* @param certs X509 Certificates which are allowed as TrustAnchor.
88+
*/
89+
public DgcGatewayDownloadConnectorBuilder withTrustAnchors(List<X509CertificateHolder> certs) {
90+
this.trustAnchors.addAll(certs);
91+
return this;
92+
}
93+
94+
/**
95+
* Add one TrustAnchor to validate the received entities.
8396
* Required.
8497
*
8598
* @param cert X509 Certificate which is the TrustAnchor.
8699
*/
87100
public DgcGatewayDownloadConnectorBuilder withTrustAnchor(X509CertificateHolder cert) {
88-
this.trustAnchor = cert;
101+
this.trustAnchors.add(cert);
89102
return this;
90103
}
91104

@@ -140,7 +153,7 @@ public DgcGatewayDownloadConnectorBuilder withMtlsAuthCert(X509CertificateHolder
140153
}
141154

142155
/**
143-
* Set the trusted Server Certificate of target DGCG.
156+
* Add a trusted Server Certificate to trustlist of target DGCG.
144157
* Default: Trust all incomming Certificates.
145158
*
146159
* @param cert X509 Certificate
@@ -233,10 +246,10 @@ public DgcGatewayDownloadConnector build() throws DgcGatewayDownloadConnectorBui
233246
"URL is not set");
234247
}
235248

236-
if (this.trustAnchor == null) {
249+
if (this.trustAnchors.isEmpty()) {
237250
throw new DgcGatewayDownloadConnectorBuilderException(
238251
DgcGatewayDownloadConnectorBuilderException.Reason.NOT_READY,
239-
"TrustAnchor is not set");
252+
"At least one TrustAnchor is required.");
240253
}
241254

242255
DgcGatewayConnectorConfigProperties properties = new DgcGatewayConnectorConfigProperties();
@@ -260,7 +273,7 @@ public DgcGatewayDownloadConnector build() throws DgcGatewayDownloadConnectorBui
260273

261274
DgcGatewayConnectorUtils connectorUtils =
262275
new DgcGatewayConnectorUtils(certificateUtils, restClient, null, null);
263-
connectorUtils.setTrustAnchor(trustAnchor);
276+
connectorUtils.setTrustAnchors(trustAnchors);
264277

265278
return new DgcGatewayDownloadConnector(connectorUtils, restClient, properties, trustListMapper);
266279
}

src/test/java/eu/europa/ec/dgc/gateway/connector/DownloadConnectorBuilderTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.security.cert.Certificate;
1010
import java.security.cert.X509Certificate;
1111
import java.time.temporal.ChronoUnit;
12+
import java.util.Collections;
1213
import java.util.Objects;
1314
import java.util.concurrent.TimeUnit;
1415
import javax.net.ssl.SSLContext;
@@ -86,7 +87,7 @@ void testConnectorUsesClientCertificate() throws Exception {
8687
.withTrustedServerCert(certificateUtils.convertCertificate(serverCertificate))
8788
.withUrl(server.url("/test").toString())
8889
.withSslHostnameValidation(false)
89-
.withTrustAnchor(certificateUtils.convertCertificate(trustAnchorCertificate))
90+
.withTrustAnchors(Collections.singletonList(certificateUtils.convertCertificate(trustAnchorCertificate)))
9091
.build();
9192

9293
connector.getTrustedCertificates();

src/test/java/eu/europa/ec/dgc/gateway/connector/DownloadConnectorUtilsTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.security.cert.X509Certificate;
3434
import java.time.ZonedDateTime;
3535
import java.util.Base64;
36+
import java.util.Collections;
3637
import lombok.extern.slf4j.Slf4j;
3738
import org.junit.jupiter.api.Assertions;
3839
import org.junit.jupiter.api.Test;
@@ -146,7 +147,9 @@ void testTrustListItemSignedByTrustAnchor() throws Exception {
146147
cscaTrustListItem.setThumbprint(certificateUtils.getCertThumbprint(csca));
147148
cscaTrustListItem.setRawData(Base64.getEncoder().encodeToString(csca.getEncoded()));
148149

149-
Assertions.assertTrue(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem, certificateUtils.convertCertificate(testKeyStore.getTrustAnchor())));
150+
Assertions.assertTrue(
151+
connectorUtils.checkTrustAnchorSignature(cscaTrustListItem,
152+
Collections.singletonList(certificateUtils.convertCertificate(testKeyStore.getTrustAnchor()))));
150153
}
151154

152155
@Test
@@ -169,7 +172,8 @@ void testTrustListItemSignedByTrustAnchorFailed() throws Exception {
169172
cscaTrustListItem.setThumbprint(certificateUtils.getCertThumbprint(csca));
170173
cscaTrustListItem.setRawData(Base64.getEncoder().encodeToString(csca.getEncoded()));
171174

172-
Assertions.assertFalse(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem, certificateUtils.convertCertificate(testKeyStore.getTrustAnchor())));
175+
Assertions.assertFalse(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem,
176+
Collections.singletonList(certificateUtils.convertCertificate(testKeyStore.getTrustAnchor()))));
173177
}
174178

175179
@Test

src/test/java/eu/europa/ec/dgc/signing/DeprecatedSignedCertificateMessageBuilderTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static org.mockito.Mockito.mock;
4040
import static org.mockito.Mockito.when;
4141

42+
@SuppressWarnings("deprecation")
4243
class DeprecatedSignedCertificateMessageBuilderTest {
4344

4445
KeyPair payloadKeyPair, signingKeyPair;

src/test/java/eu/europa/ec/dgc/signing/SignedCertificateMessageParserTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void setupTestData() throws Exception {
6262
signingCertificate = CertificateTestUtils.generateCertificate(signingKeyPair, "DE", "SigningCertificate");
6363

6464
builder = new SignedCertificateMessageBuilder()
65-
.withPayloadCertificate(new X509CertificateHolder(payloadCertificate.getEncoded()))
65+
.withPayload(new X509CertificateHolder(payloadCertificate.getEncoded()))
6666
.withSigningCertificate(new X509CertificateHolder(signingCertificate.getEncoded()), signingKeyPair.getPrivate());
6767
}
6868

@@ -72,7 +72,7 @@ void parserShouldParseByteArray() throws IOException, CertificateEncodingExcepti
7272
Base64.getEncoder().encode(builder.build()));
7373

7474
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
75-
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
75+
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
7676
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
7777
Assertions.assertTrue(parser.isSignatureVerified());
7878
checkSignatureFromParser(parser.getSignature());
@@ -87,7 +87,7 @@ void parserShouldParseByteArrayWithDetachedPayload() throws IOException, Certifi
8787
Base64.getEncoder().encode(payloadCertificate.getEncoded()));
8888

8989
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
90-
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
90+
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
9191
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
9292
Assertions.assertTrue(parser.isSignatureVerified());
9393
Assertions.assertEquals(new String(cms), parser.getSignature());
@@ -102,7 +102,7 @@ void parserShouldParseByteArrayWithDetachedPayloadAsString() throws IOException,
102102
Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()));
103103

104104
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
105-
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
105+
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
106106
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
107107
Assertions.assertTrue(parser.isSignatureVerified());
108108
checkSignatureFromParser(parser.getSignature());
@@ -114,7 +114,7 @@ void parserShouldParseString() throws IOException, CertificateEncodingException
114114
builder.buildAsString());
115115

116116
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
117-
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
117+
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
118118
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
119119
Assertions.assertTrue(parser.isSignatureVerified());
120120
checkSignatureFromParser(parser.getSignature());
@@ -127,7 +127,7 @@ void parserShouldParseStringWithDetachedPayload() throws IOException, Certificat
127127
Base64.getEncoder().encode(payloadCertificate.getEncoded()));
128128

129129
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
130-
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
130+
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
131131
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
132132
Assertions.assertTrue(parser.isSignatureVerified());
133133
checkSignatureFromParser(parser.getSignature());
@@ -280,7 +280,7 @@ private void checkSignatureFromParser(String signature) throws CertificateEncodi
280280
signature, Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()));
281281

282282
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
283-
Assertions.assertEquals(new X509CertificateHolder(payloadCertificate.getEncoded()), parser.getPayloadCertificate());
283+
Assertions.assertEquals(new X509CertificateHolder(payloadCertificate.getEncoded()), parser.getPayload());
284284
Assertions.assertEquals(new X509CertificateHolder(signingCertificate.getEncoded()), parser.getSigningCertificate());
285285
Assertions.assertTrue(parser.isSignatureVerified());
286286
Assertions.assertEquals(signature, parser.getSignature());

0 commit comments

Comments
 (0)