Skip to content

Commit

Permalink
Merge pull request #87 from eu-digital-green-certificates/feat/multip…
Browse files Browse the repository at this point in the history
…le-trustanchors

Feat: Support for multiple TrustAnchors
  • Loading branch information
f11h authored Feb 7, 2022
2 parents 81ac06a + cdda568 commit 1ae0ef0
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 31 deletions.
28 changes: 19 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- dependencies -->
<owasp.version>6.5.2</owasp.version>
<spring.boot.version>2.6.2</spring.boot.version>
<owasp.version>6.5.3</owasp.version>
<spring.boot.version>2.6.3</spring.boot.version>
<spring.cloud.version>3.1.0</spring.cloud.version>
<feign.version>11.7</feign.version>
<bcpkix.version>1.70</bcpkix.version>
<lombok.version>1.18.22</lombok.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
<commonsio.version>2.11.0</commonsio.version>
<cbor.version>4.5.1</cbor.version>
<jackson.version>2.13.0</jackson.version>
<jackson.version>2.13.1</jackson.version>
<mockwebserver.version>4.9.3</mockwebserver.version>
<plugin.checkstyle.version>3.1.2</plugin.checkstyle.version>
<plugin.sonar.version>3.9.1.2184</plugin.sonar.version>
Expand Down Expand Up @@ -77,12 +77,6 @@
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand All @@ -93,6 +87,12 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring.cloud.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
Expand Down Expand Up @@ -130,6 +130,11 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -156,6 +161,11 @@
<artifactId>spring-security-crypto</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.15</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class DgcGatewayConnectorUtils {
private final KeyStore trustAnchorKeyStore;

@Setter
private X509CertificateHolder trustAnchor;
private List<X509CertificateHolder> trustAnchors;


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

public boolean trustListItemSignedByCa(TrustListItemDto certificate, X509CertificateHolder ca) {
Expand Down Expand Up @@ -142,7 +142,7 @@ public boolean trustListItemSignedByCa(TrustListItemDto certificate, Map<String,
.anyMatch(ca -> trustListItemSignedByCa(certificate, ca));
}

boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, X509CertificateHolder trustAnchor) {
boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, List<X509CertificateHolder> trustAnchors) {
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
trustListItem.getSignature(), trustListItem.getRawData());

Expand All @@ -155,7 +155,7 @@ boolean checkTrustAnchorSignature(TrustListItemDto trustListItem, X509Certificat
return false;
}

return parser.getSigningCertificate().equals(trustAnchor);
return trustAnchors.stream().anyMatch(trustAnchor -> parser.getSigningCertificate().equals(trustAnchor));
}

X509CertificateHolder getCertificateFromTrustListItem(TrustListItemDto trustListItem) {
Expand Down Expand Up @@ -187,7 +187,7 @@ public List<X509CertificateHolder> fetchCertificatesAndVerifyByTrustAnchor(Certi

return downloadedCertificates.getBody().stream()
.filter(this::checkThumbprintIntegrity)
.filter(c -> this.checkTrustAnchorSignature(c, trustAnchor))
.filter(c -> this.checkTrustAnchorSignature(c, trustAnchors))
.map(this::getCertificateFromTrustListItem)
.filter(Objects::nonNull)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.net.ssl.SSLContext;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -60,7 +62,7 @@ public class DgcGatewayDownloadConnectorBuilder {
private String url;
private KeyStore mtlsTrustStore;
private KeyStore mtlsKeyStore;
private X509CertificateHolder trustAnchor;
private final List<X509CertificateHolder> trustAnchors = new ArrayList<>();
private HttpHost proxy;
private int cacheMagAge = -1;
private boolean enableSslHostnameValidation = true;
Expand All @@ -79,13 +81,24 @@ public DgcGatewayDownloadConnectorBuilder withUrl(String url) {
}

/**
* Set the TrustAnchor to validate the received entities.
* Add multiple TrustAnchors to validate the received entities.
* Required.
*
* @param certs X509 Certificates which are allowed as TrustAnchor.
*/
public DgcGatewayDownloadConnectorBuilder withTrustAnchors(List<X509CertificateHolder> certs) {
this.trustAnchors.addAll(certs);
return this;
}

/**
* Add one TrustAnchor to validate the received entities.
* Required.
*
* @param cert X509 Certificate which is the TrustAnchor.
*/
public DgcGatewayDownloadConnectorBuilder withTrustAnchor(X509CertificateHolder cert) {
this.trustAnchor = cert;
this.trustAnchors.add(cert);
return this;
}

Expand Down Expand Up @@ -140,7 +153,7 @@ public DgcGatewayDownloadConnectorBuilder withMtlsAuthCert(X509CertificateHolder
}

/**
* Set the trusted Server Certificate of target DGCG.
* Add a trusted Server Certificate to trustlist of target DGCG.
* Default: Trust all incomming Certificates.
*
* @param cert X509 Certificate
Expand Down Expand Up @@ -233,10 +246,10 @@ public DgcGatewayDownloadConnector build() throws DgcGatewayDownloadConnectorBui
"URL is not set");
}

if (this.trustAnchor == null) {
if (this.trustAnchors.isEmpty()) {
throw new DgcGatewayDownloadConnectorBuilderException(
DgcGatewayDownloadConnectorBuilderException.Reason.NOT_READY,
"TrustAnchor is not set");
"At least one TrustAnchor is required.");
}

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

DgcGatewayConnectorUtils connectorUtils =
new DgcGatewayConnectorUtils(certificateUtils, restClient, null, null);
connectorUtils.setTrustAnchor(trustAnchor);
connectorUtils.setTrustAnchors(trustAnchors);

return new DgcGatewayDownloadConnector(connectorUtils, restClient, properties, trustListMapper);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
Expand Down Expand Up @@ -86,7 +87,7 @@ void testConnectorUsesClientCertificate() throws Exception {
.withTrustedServerCert(certificateUtils.convertCertificate(serverCertificate))
.withUrl(server.url("/test").toString())
.withSslHostnameValidation(false)
.withTrustAnchor(certificateUtils.convertCertificate(trustAnchorCertificate))
.withTrustAnchors(Collections.singletonList(certificateUtils.convertCertificate(trustAnchorCertificate)))
.build();

connector.getTrustedCertificates();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.security.cert.X509Certificate;
import java.time.ZonedDateTime;
import java.util.Base64;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -146,7 +147,9 @@ void testTrustListItemSignedByTrustAnchor() throws Exception {
cscaTrustListItem.setThumbprint(certificateUtils.getCertThumbprint(csca));
cscaTrustListItem.setRawData(Base64.getEncoder().encodeToString(csca.getEncoded()));

Assertions.assertTrue(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem, certificateUtils.convertCertificate(testKeyStore.getTrustAnchor())));
Assertions.assertTrue(
connectorUtils.checkTrustAnchorSignature(cscaTrustListItem,
Collections.singletonList(certificateUtils.convertCertificate(testKeyStore.getTrustAnchor()))));
}

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

Assertions.assertFalse(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem, certificateUtils.convertCertificate(testKeyStore.getTrustAnchor())));
Assertions.assertFalse(connectorUtils.checkTrustAnchorSignature(cscaTrustListItem,
Collections.singletonList(certificateUtils.convertCertificate(testKeyStore.getTrustAnchor()))));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@SuppressWarnings("deprecation")
class DeprecatedSignedCertificateMessageBuilderTest {

KeyPair payloadKeyPair, signingKeyPair;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void setupTestData() throws Exception {
signingCertificate = CertificateTestUtils.generateCertificate(signingKeyPair, "DE", "SigningCertificate");

builder = new SignedCertificateMessageBuilder()
.withPayloadCertificate(new X509CertificateHolder(payloadCertificate.getEncoded()))
.withPayload(new X509CertificateHolder(payloadCertificate.getEncoded()))
.withSigningCertificate(new X509CertificateHolder(signingCertificate.getEncoded()), signingKeyPair.getPrivate());
}

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

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

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

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
checkSignatureFromParser(parser.getSignature());
Expand All @@ -114,7 +114,7 @@ void parserShouldParseString() throws IOException, CertificateEncodingException
builder.buildAsString());

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

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayload().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
checkSignatureFromParser(parser.getSignature());
Expand Down Expand Up @@ -280,7 +280,7 @@ private void checkSignatureFromParser(String signature) throws CertificateEncodi
signature, Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()));

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertEquals(new X509CertificateHolder(payloadCertificate.getEncoded()), parser.getPayloadCertificate());
Assertions.assertEquals(new X509CertificateHolder(payloadCertificate.getEncoded()), parser.getPayload());
Assertions.assertEquals(new X509CertificateHolder(signingCertificate.getEncoded()), parser.getSigningCertificate());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertEquals(signature, parser.getSignature());
Expand Down

0 comments on commit 1ae0ef0

Please sign in to comment.