From e1c588a09aa6a9dadbade849568935dd0a653231 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Tue, 3 Sep 2024 17:10:31 +0200 Subject: [PATCH] OZG-6239 KOP-2608 Add TrustStore option --- .../xta/client/config/XtaClientConfig.java | 4 ++ .../core/XtaTLSClientParametersFactory.java | 4 ++ .../ozgcloud/xta/client/XtaClientITCase.java | 3 -- .../xta/client/XtaTestServerContainer.java | 12 +---- .../client/XtaTestServerSetupExtension.java | 34 +++++++------ .../XtaTLSClientParametersFactoryTest.java | 48 ++++++++++++++++++ .../store/john-smith-client-cert-keystore.p12 | Bin 0 -> 2851 bytes .../store/xta-test-server-truststore.jks | Bin 0 -> 1136 bytes 8 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 src/test/resources/store/john-smith-client-cert-keystore.p12 create mode 100644 src/test/resources/store/xta-test-server-truststore.jks diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java index 4f7241e..1693d82 100644 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java @@ -49,6 +49,10 @@ public class XtaClientConfig { @Builder.Default private final KeyStore clientCertKeystore = null; + @Valid + @Builder.Default + private final KeyStore trustStore = null; + @Builder.Default private final boolean schemaValidation = true; @Builder.Default diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java index 911d35e..fee35b4 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java @@ -45,6 +45,10 @@ public class XtaTLSClientParametersFactory { if (clientCertKeystore != null) { sslContextBuilder.loadKeyMaterial(loadStore(clientCertKeystore), clientCertKeystore.getPassword()); } + var trustStore = config.getTrustStore(); + if (trustStore != null) { + sslContextBuilder.loadTrustMaterial(loadStore(trustStore), null); + } return sslContextBuilder.build(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | IOException | CertificateException e) { diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java index 0ebe3dd..2169e0b 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java @@ -3,9 +3,7 @@ package de.ozgcloud.xta.client; import static de.ozgcloud.xta.client.XtaDevServerSetupExtension.*; import static org.assertj.core.api.Assertions.*; -import org.junit.Ignore; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -13,7 +11,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import lombok.SneakyThrows; -@Ignore class XtaClientITCase { @RegisterExtension diff --git a/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java b/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java index 6629771..da3b366 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java @@ -2,19 +2,15 @@ package de.ozgcloud.xta.client; import jakarta.validation.constraints.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; public class XtaTestServerContainer extends GenericContainer<XtaTestServerContainer> { - private static final Logger log = LoggerFactory.getLogger(XtaTestServerContainer.class); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker.ozg-sh.de/xta-test-server"); private static final String DEFAULT_TAG = "latest"; public static final int PORT = 8443; - public XtaTestServerContainer() { this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } @@ -25,7 +21,7 @@ public class XtaTestServerContainer extends GenericContainer<XtaTestServerContai } public String getBaseUrl() { - return "https://localhost:%d/services/XTAService/".formatted(getMappedPort(PORT)); + return "https://%s:%d/services/XTAService/".formatted(getHost(), getMappedPort(PORT)); } public String getMsgBoxPortUrl() { @@ -40,10 +36,4 @@ public class XtaTestServerContainer extends GenericContainer<XtaTestServerContai return getBaseUrl() + "SendXtaPort"; } - public static void main(String[] args) { - try (XtaTestServerContainer container = new XtaTestServerContainer()) { - container.start(); - System.out.println(container.getBaseUrl()); - } - } } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java b/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java index 2ce8704..d7f3dd2 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java @@ -2,7 +2,6 @@ package de.ozgcloud.xta.client; import static de.ozgcloud.xta.client.XtaDevServerSetupExtension.*; -import java.io.File; import java.util.List; import java.util.Objects; @@ -12,8 +11,6 @@ import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.testcontainers.utility.DockerImageName; -import com.google.common.io.Files; - import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.core.WrappedXtaService; import de.ozgcloud.xta.client.model.Identifier; @@ -36,13 +33,18 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC private static final DockerImageName XTA_TEST_SERVER_IMAGE = DockerImageName.parse("docker.ozg-sh.de/xta-test-server") .withTag("1.4.1-SNAPSHOT"); + private static final String JOHN_SMITH_KEYSTORE_PATH = "store/john-smith-client-cert-keystore.p12"; + private static final String JOHN_SMITH_KEYSTORE_PASSWORD = "password"; + + private static final String XTA_TEST_SERVER_TRUSTSTORE_PATH = "store/xta-test-server-truststore.jks"; + private static final String XTA_TEST_SERVER_TRUSTSTORE_PASSWORD = "password"; + private XtaClient client; private WrappedXtaService service; private XtaClientConfig config; private XtaClientFactory clientFactory; private XtaTestServerContainer xtaServerContainer; - @Override @SneakyThrows public void beforeAll(ExtensionContext context) { @@ -73,21 +75,25 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC @SneakyThrows XtaClient setupClient() { - var clientCertKeystorePath = getEnvVar("KOP_SH_KIEL_DEV_PATH"); - var clientCertKeystorePassword = getEnvVar("KOP_SH_KIEL_DEV_PASSWORD"); - // TODO Trust store (xta-test-server certificate not trusted...) var clientCertKeyStore = XtaClientConfig.KeyStore.builder() - .content(readBytesFromFile(clientCertKeystorePath)) + .content(readBytesFromResource(JOHN_SMITH_KEYSTORE_PATH)) .type("PKCS12") - .password(clientCertKeystorePassword.toCharArray()) + .password(JOHN_SMITH_KEYSTORE_PASSWORD.toCharArray()) + .build(); + var trustStore = XtaClientConfig.KeyStore.builder() + .content(readBytesFromResource(XTA_TEST_SERVER_TRUSTSTORE_PATH)) + .type("JKS") + .password(XTA_TEST_SERVER_TRUSTSTORE_PASSWORD.toCharArray()) .build(); + config = XtaClientConfig.builder() .clientIdentifiers(List.of(CLIENT_IDENTIFIER3, CLIENT_IDENTIFIER2, CLIENT_IDENTIFIER1)) .managementServiceUrl(xtaServerContainer.getManagementPortUrl()) .sendServiceUrl(xtaServerContainer.getSendPortUrl()) .msgBoxServiceUrl(xtaServerContainer.getMsgBoxPortUrl()) .clientCertKeystore(clientCertKeyStore) + .trustStore(trustStore) .logSoapRequests(true) .logSoapResponses(true) .build(); @@ -95,10 +101,6 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC return clientFactory.create(); } - private String getEnvVar(String name) { - return Objects.requireNonNull(System.getenv(name), "Environment variable " + name + " is required!"); - } - @Override @SneakyThrows public void beforeEach(ExtensionContext context) { @@ -157,8 +159,10 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC } @SneakyThrows - private static byte[] readBytesFromFile(String path) { - return Files.toByteArray(new File(path)); + private static byte[] readBytesFromResource(String resourcePath) { + try (var inputStream = XtaTestServerSetupExtension.class.getClassLoader().getResourceAsStream(resourcePath)) { + return Objects.requireNonNull(inputStream, "Expect class-resource at: " + resourcePath).readAllBytes(); + } } } diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java index 3e27432..294fcb0 100644 --- a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java @@ -149,6 +149,54 @@ class XtaTLSClientParametersFactoryTest { assertThat(sslContextResult).isEqualTo(sslContext); } } + + @DisplayName("with trust store") + @Nested + class TestWithTrustStore { + @BeforeEach + @SneakyThrows + void mock() { + when(config.getTrustStore()).thenReturn(configKeystore); + doReturn(keystore).when(factory).loadStore(configKeystore); + } + + @DisplayName("should call loadTrustMaterial") + @Test + @SneakyThrows + void shouldCallLoadTrustMaterial() { + factory.createXtaSslContext(); + + verify(sslContextBuilder).loadTrustMaterial(keystore, null); + } + + @DisplayName("should return SSL context") + @Test + @SneakyThrows + void shouldSetProtocol() { + var sslContextResult = factory.createXtaSslContext(); + + assertThat(sslContextResult).isEqualTo(sslContext); + } + } + + @DisplayName("without trust store") + @Nested + class TestWithoutTrustStore { + @BeforeEach + @SneakyThrows + void mock() { + when(config.getTrustStore()).thenReturn(null); + } + + @DisplayName("should return SSL context") + @Test + @SneakyThrows + void shouldSetProtocol() { + var sslContextResult = factory.createXtaSslContext(); + + assertThat(sslContextResult).isEqualTo(sslContext); + } + } } @DisplayName("create SSL context builder") diff --git a/src/test/resources/store/john-smith-client-cert-keystore.p12 b/src/test/resources/store/john-smith-client-cert-keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..a727395694185315016bfccc2fd42e17749e4592 GIT binary patch literal 2851 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 N_V?swCT7OA1pu3bDyIMd literal 0 HcmV?d00001 diff --git a/src/test/resources/store/xta-test-server-truststore.jks b/src/test/resources/store/xta-test-server-truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..0935b1e776a77b5caa7e8cfc4a6a02a9881d03b7 GIT binary patch literal 1136 zcmezO_TO6u1_mYu1_nkjEmTpGs8EtxT#{O(P?VovqL7@(z`$5r$hb9<fi*(U)WDK~ zfkoAziAB+%iJ5Z&GZP~d69?1k0G55_t0o!nvT<s)d9;1!Wn|=LWiV)5ZpdxG$;KSY z!Y0h*;%dlmzzgDV2(vk*7Ug8-!35ah0zw7?AQfD~oPPO5C5br-j_!ss22vm~Zed}! z%)G?B<jlkzh2Z?0(vr;lykbLX14)o9v#?M^h@(OX#N|OCmpeNesvD@nUBt;KCXtd^ zl&zOknw*i5UzF#N2ev{lIX~AxPMp`s(9qD>z|g?dz`!6%oYxqcJA{1F#HfTE4UDV| z%uS5^3<gb%Tue=jj113=OlHY>Nw;t{?wzXmJS<@M))dz?ZkN{Ik;(ZN-%Zlqt+l*P z?1SFpd7oBCum9fjg@LK}et_t7)j}4|B5N*7Ue}Xyo}V_=Z`i$nC#U7pY!!xxWlnEY z9$Gd}m2ypDY>3`|&wQ=;p7uRX%UO@M8!oT3N;EyjzroB@WNqTfJq|oc-wm0+Cb7Qy z_O<8ljCQZpE;B3VCgm!#g(h7nk6&<vDLl;XR`-JTj8Fw_pMDL-goU%jO1JTsN{G#V z<0R!%xLp6x9iNN~1)CN$pRfoN`+D}un$IFkUxOTKcDS=1)L6UeLi8TKlKs1PCkmaM zt#M>y<2A{w$3chw9bB(rG0&Xkh}J<SW=00a#r_7q20U!cp|Zj(tOm@CjQ<T}K|DSd zF%}V~*_r3pL>uH^sxnKBx1GGQ_PX$I18$HsKMM;p6Jwi!ARA{wn+Idt4<|-OkPrhq za*%U_gPf6Jr`4N0XPxTw+Nj0d=eJi)Gf!l=o_ej?PrhH&epz(B<l94%65YpBiZ6Y= z^JC2w%U-9?H^dlPijSXd$$Mk&zH7pjs@&KoZ5O+I&5!eJnl1Kmuf<sflX$InCfoML z+C0|&c2YE=?2dW<!=o?Xh&N6XpI0X{={QrCPwo}&hmxybXI<DM?y6zC+d^}}ro4X* zWs=j6_b%JJ@BMnI((Yx9M^DHzwq0Gm$IR>BoBsC`oA0i=ofg#KdwaL}<?qb1&ztFa zt$dl$=6*}F>yE6|$u~CkI!w{~IUmjGwBKd7%kR(DwEL@mEaCmsp{#SN;*|e&sWT$V syI&o@+tuv8{PG9mP!)OE%;|C3uc{s?JI~#pxb)6h&!-FrRJS+-0FB&{UjP6A literal 0 HcmV?d00001 -- GitLab