diff --git a/build-parent/pom.xml b/build-parent/pom.xml index d6b4d1f649c4e..1fe653830d0cc 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -374,7 +374,7 @@ me.escoffier.certs certificate-generator-junit5 - 0.4.0 + 0.4.3 test diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java index 65e0d23e78197..a8b5de8403c04 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java @@ -11,6 +11,7 @@ import jakarta.enterprise.event.Observes; import org.assertj.core.api.Assertions; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -19,20 +20,33 @@ import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class DisableHttpPortTest { + private static final String configuration = """ + # Enable SSL, configure the key store + quarkus.http.insecure-requests=REDIRECT + quarkus.http.ssl.certificate.files=server-cert.crt + quarkus.http.ssl.certificate.key-files=server-key.key + """; + @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/disable-http.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-key.pem"), "server-key.pem") - .addAsResource(new File("src/test/resources/conf/server-cert.pem"), "server-cert.pem")); + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-test.key"), "server-key.key") + .addAsResource(new File("target/certs/ssl-test.crt"), "server-cert.crt")); @BeforeAll public static void setupRestAssured() { - RestAssured.useRelaxedHTTPSValidation(); + RestAssured + .trustStore(new File("target/certs/ssl-test-truststore.jks"), "secret"); } @AfterAll diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java index 82473a256a1a4..7f8494bb679cd 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java @@ -54,9 +54,11 @@ public class MainHttpServerTlsPKCS12CertificateReloadTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar.addClasses(MyBean.class)) - .overrideConfigKey("quarkus.http.ssl.insecure-requests", "redirect") + .overrideConfigKey("quarkus.http.insecure-requests", "redirect") .overrideConfigKey("quarkus.http.ssl.certificate.reload-period", "30s") .overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", temp.getAbsolutePath() + "/tls.p12") + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "password") + .overrideConfigKey("loc", temp.getAbsolutePath()) .setBeforeAllCustomizer(() -> { try { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java index c3abccb5d5512..b141965526b81 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java @@ -11,6 +11,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -26,14 +27,28 @@ import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpVersion; import io.vertx.core.net.JdkSSLEngineOptions; +import io.vertx.core.net.JksOptions; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; /** * Configuration of the RST flood protection (CVE-2023-44487) */ +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) @DisabledOnOs(OS.WINDOWS) public class Http2RSTFloodProtectionConfigTest { + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.jks + quarkus.http.ssl.certificate.key-store-password=secret + + quarkus.http.limits.rst-flood-max-rst-frame-per-window=10 + quarkus.http.limits.rst-flood-window-duration=10s + """; + @TestHTTPResource(value = "/ping", ssl = true) URL sslUrl; @@ -44,9 +59,8 @@ public class Http2RSTFloodProtectionConfigTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-jks-rst-flood-protection.conf"), - "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks")); + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks")); @Test void testRstFloodProtectionWithTlsEnabled() throws Exception { @@ -55,7 +69,8 @@ void testRstFloodProtectionWithTlsEnabled() throws Exception { .setUseAlpn(true) .setProtocolVersion(HttpVersion.HTTP_2) .setSsl(true) - .setTrustAll(true); + .setTrustOptions(new JksOptions().setPath(new File("target/certs/ssl-test-truststore.jks").getAbsolutePath()) + .setPassword("secret")); var client = VertxCoreRecorder.getVertx().get().createHttpClient(options); int port = sslUrl.getPort(); diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java index 991cc1d57771c..3e676ca2abfa0 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java @@ -11,6 +11,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -26,14 +27,25 @@ import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpVersion; import io.vertx.core.net.JdkSSLEngineOptions; +import io.vertx.core.net.JksOptions; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; /** * Reproduce CVE-2023-44487. */ +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) @DisabledOnOs(OS.WINDOWS) public class Http2RSTFloodProtectionTest { + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.jks + quarkus.http.ssl.certificate.key-store-password=secret + """; + @TestHTTPResource(value = "/ping", ssl = true) URL sslUrl; @@ -44,8 +56,8 @@ public class Http2RSTFloodProtectionTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks")); + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks")); @Test void testRstFloodProtectionWithTlsEnabled() throws Exception { @@ -54,7 +66,8 @@ void testRstFloodProtectionWithTlsEnabled() throws Exception { .setUseAlpn(true) .setProtocolVersion(HttpVersion.HTTP_2) .setSsl(true) - .setTrustAll(true); + .setTrustOptions(new JksOptions().setPath(new File("target/certs/ssl-test-truststore.jks").getAbsolutePath()) + .setPassword("secret")); var client = VertxCoreRecorder.getVertx().get().createHttpClient(options); int port = sslUrl.getPort(); diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java index 8d79b21ab3578..4cf2f94e496ea 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java @@ -21,11 +21,17 @@ import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpVersion; import io.vertx.core.net.JdkSSLEngineOptions; +import io.vertx.core.net.JksOptions; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class Http2Test { protected static final String PING_DATA = "12345678"; @@ -33,15 +39,16 @@ public class Http2Test { @TestHTTPResource(value = "/ping", ssl = true) URL sslUrl; - @TestHTTPResource(value = "/ping", ssl = false) - URL url; + @TestHTTPResource(value = "/ping") + URL plainUrl; @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks")); + .addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks")) + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", "server-keystore.jks") + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "secret"); @Test public void testHttp2EnabledSsl() throws ExecutionException, InterruptedException { @@ -50,7 +57,7 @@ public void testHttp2EnabledSsl() throws ExecutionException, InterruptedExceptio .setUseAlpn(true) .setProtocolVersion(HttpVersion.HTTP_2) .setSsl(true) - .setTrustAll(true); + .setTrustOptions(new JksOptions().setPath("target/certs/ssl-test-truststore.jks").setPassword("secret")); WebClient client = WebClient.create(VertxCoreRecorder.getVertx().get(), options); int port = sslUrl.getPort(); @@ -63,7 +70,7 @@ public void testHttp2EnabledPlain() throws ExecutionException, InterruptedExcept .setProtocolVersion(HttpVersion.HTTP_2) .setHttp2ClearTextUpgrade(true); WebClient client = WebClient.create(VertxCoreRecorder.getVertx().get(), options); - runTest(client, url.getPort()); + runTest(client, plainUrl.getPort()); } private void runTest(WebClient client, int port) throws InterruptedException, ExecutionException { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java index 1a0827d198fb4..64d1c9d094aae 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java @@ -21,19 +21,25 @@ import io.restassured.RestAssured; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class ManagementWithJksTest { - private static final String APP_PROPS = "" + - "quarkus.management.enabled=true\n" + - "quarkus.management.root-path=/management\n" + - "quarkus.management.ssl.certificate.key-store-file=server-keystore.jks\n" + - "quarkus.management.ssl.certificate.key-store-password=secret\n"; + private static final String configuration = """ + quarkus.management.enabled=true + quarkus.management.root-path=/management + quarkus.management.ssl.certificate.key-store-file=server-keystore.jks + quarkus.management.ssl.certificate.key-store-password=secret + """; @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar - .addAsResource(new StringAsset(APP_PROPS), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks") + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-management-interface-test-keystore.jks"), "server-keystore.jks") .addClasses(MyObserver.class)) .addBuildChainCustomizer(buildCustomizer()); @@ -72,7 +78,7 @@ public void handle(RoutingContext rc) { @Test public void testSslWithJks() { RestAssured.given() - .relaxedHTTPSValidation() + .trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret") .get("https://0.0.0.0:9001/management/my-route") .then().statusCode(200).body(Matchers.equalTo("ssl")); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java index a8e55cedb5608..2dfe139a6948a 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java @@ -21,20 +21,26 @@ import io.restassured.RestAssured; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class ManagementWithP12Test { - private static final String APP_PROPS = "" + - "quarkus.management.enabled=true\n" + - "quarkus.management.root-path=/management\n" + - "quarkus.management.ssl.certificate.key-store-file=server-keystore.p12\n" + - "quarkus.management.ssl.certificate.key-store-password=secret\n"; + private static final String configuration = """ + quarkus.management.enabled=true + quarkus.management.root-path=/management + quarkus.management.ssl.certificate.key-store-file=server-keystore.p12 + quarkus.management.ssl.certificate.key-store-password=secret + """; @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar - .addAsResource(new StringAsset(APP_PROPS), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.p12"), "server-keystore.p12") - .addClasses(MyObserver.class)) + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-management-interface-test-keystore.p12"), "server-keystore.p12") + .addClasses(ManagementWithJksTest.MyObserver.class)) .addBuildChainCustomizer(buildCustomizer()); static Consumer buildCustomizer() { @@ -72,7 +78,7 @@ public void handle(RoutingContext rc) { @Test public void testSslWithP12() { RestAssured.given() - .relaxedHTTPSValidation() + .trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret") .get("https://0.0.0.0:9001/management/my-route") .then().statusCode(200).body(Matchers.equalTo("ssl")); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java index bc86379e35909..3c5bf89806922 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java @@ -21,20 +21,27 @@ import io.restassured.RestAssured; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class ManagementWithPemTest { - private static final String APP_PROPS = "" + - "quarkus.management.enabled=true\n" + - "quarkus.management.root-path=/management\n" + - "quarkus.management.ssl.certificate.files=server-cert.pem\n" + - "quarkus.management.ssl.certificate.key-files=server-key.pem\n"; + + private static final String configuration = """ + quarkus.management.enabled=true + quarkus.management.root-path=/management + quarkus.management.ssl.certificate.files=server.crt + quarkus.management.ssl.certificate.key-files=server.key + """; @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar - .addAsResource(new StringAsset(APP_PROPS), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-key.pem"), "server-key.pem") - .addAsResource(new File("src/test/resources/conf/server-cert.pem"), "server-cert.pem") + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/ssl-management-interface-test.key"), "server.key") + .addAsResource(new File("target/certs/ssl-management-interface-test.crt"), "server.crt") .addClasses(MyObserver.class)) .addBuildChainCustomizer(buildCustomizer()); @@ -73,7 +80,8 @@ public void handle(RoutingContext rc) { @Test public void testSslWithPem() { RestAssured.given() - .relaxedHTTPSValidation() + .given() + .trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret") .get("https://0.0.0.0:9001/management/my-route") .then().statusCode(200).body(Matchers.equalTo("ssl")); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestBasicAuthTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java similarity index 54% rename from extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestBasicAuthTest.java rename to extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java index 2a651111126f0..303c23ab05f29 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestBasicAuthTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java @@ -1,4 +1,4 @@ -package io.quarkus.vertx.http.security; +package io.quarkus.vertx.http.mtls; import static org.hamcrest.Matchers.is; @@ -8,6 +8,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -17,11 +18,27 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.quarkus.vertx.http.security.TestTrustedIdentityProvider; import io.restassured.RestAssured; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM }, client = true)) public class MtlsRequestBasicAuthTest { + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.jks + quarkus.http.ssl.certificate.key-store-password=secret + quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks + quarkus.http.ssl.certificate.trust-store-password=secret + quarkus.http.ssl.client-auth=REQUEST + quarkus.http.auth.basic=true + quarkus.http.auth.proactive=true + """; + @TestHTTPResource(value = "/mtls", ssl = true) URL url; @@ -30,9 +47,9 @@ public class MtlsRequestBasicAuthTest { .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) .addClasses(TestIdentityProvider.class, TestTrustedIdentityProvider.class, TestIdentityController.class) - .addAsResource("conf/mtls/mtls-basic-jks.conf", "application.properties") - .addAsResource("conf/mtls/server-keystore.jks", "server-keystore.jks") - .addAsResource("conf/mtls/server-truststore.jks", "server-truststore.jks")); + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/mtls-test-keystore.jks"), "server-keystore.jks") + .addAsResource(new File("target/certs/mtls-test-server-truststore.jks"), "server-truststore.jks")); @BeforeAll public static void setup() { @@ -43,15 +60,15 @@ public static void setup() { @Test public void testClientAuthentication() { RestAssured.given() - .keyStore(new File("src/test/resources/conf/mtls/client-keystore.jks"), "password") - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") - .get(url).then().statusCode(200).body(is("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); + .keyStore("target/certs/mtls-test-client-keystore.jks", "secret") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(200).body(is("CN=localhost")); } @Test public void testNoClientCert() { RestAssured.given() - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") .get(url).then().statusCode(200).body(is("")); } @@ -61,7 +78,7 @@ public void testNoClientCertBasicAuth() { .auth() .preemptive() .basic("admin", "admin") - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") .get(url).then().statusCode(200).body(is("admin")); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java new file mode 100644 index 0000000000000..e8ac562acc49e --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java @@ -0,0 +1,74 @@ +package io.quarkus.vertx.http.mtls; + +import static org.hamcrest.Matchers.is; + +import java.io.File; +import java.net.URL; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.restassured.RestAssured; +import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM }, client = true)) +public class MtlsRequestTest { + + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.jks + quarkus.http.ssl.certificate.key-store-password=secret + quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks + quarkus.http.ssl.certificate.trust-store-password=secret + quarkus.http.ssl.client-auth=REQUEST + quarkus.http.auth.permission.all.paths=/* + quarkus.http.auth.permission.all.policy=authenticated + """; + + @TestHTTPResource(value = "/mtls", ssl = true) + URL url; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(MyBean.class) + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/mtls-test-keystore.jks"), "server-keystore.jks") + .addAsResource(new File("target/certs/mtls-test-server-truststore.jks"), "server-truststore.jks")); + + @Test + public void testClientAuthentication() { + RestAssured.given() + .keyStore("target/certs/mtls-test-client-keystore.jks", "secret") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(200).body(is("CN=localhost")); + } + + @Test + public void testNoClientCert() { + RestAssured.given() + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(401); + } + + @ApplicationScoped + static class MyBean { + + public void register(@Observes Router router) { + router.get("/mtls").handler(rc -> { + rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); + }); + } + + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java new file mode 100644 index 0000000000000..fbbed0c116045 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java @@ -0,0 +1,78 @@ +package io.quarkus.vertx.http.mtls; + +import static org.hamcrest.Matchers.is; + +import java.io.File; +import java.net.URL; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.restassured.RestAssured; +import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM }, client = true)) +public class MtlsRequiredTest { + + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.jks + quarkus.http.ssl.certificate.key-store-password=secret + quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks + quarkus.http.ssl.certificate.trust-store-password=secret + quarkus.http.ssl.client-auth=REQUIRED + quarkus.http.insecure-requests=enabled + + quarkus.http.auth.permission.default.paths=/* + quarkus.http.auth.permission.default.policy=authenticated + """; + + @TestHTTPResource(value = "/mtls", ssl = true) + URL url; + + @TestHTTPResource(value = "/mtls", ssl = false) + URL urlNoTls; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(MyBean.class) + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/mtls-test-keystore.jks"), "server-keystore.jks") + .addAsResource(new File("target/certs/mtls-test-server-truststore.jks"), "server-truststore.jks")); + + @Test + public void testClientAuthentication() { + RestAssured.given() + .keyStore("target/certs/mtls-test-client-keystore.jks", "secret") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(200).body(is("CN=localhost")); + } + + @Test + public void testNoClientCert() { + RestAssured.given() + .get(urlNoTls).then().statusCode(401); + } + + @ApplicationScoped + static class MyBean { + + public void register(@Observes Router router) { + router.get("/mtls").handler(rc -> { + rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); + }); + } + + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java new file mode 100644 index 0000000000000..c0cf63e6b0fe1 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java @@ -0,0 +1,78 @@ +package io.quarkus.vertx.http.mtls; + +import static org.hamcrest.Matchers.is; + +import java.io.File; +import java.net.URL; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.restassured.RestAssured; +import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM }, client = true)) +public class MtlsWithP12Test { + + private static final String configuration = """ + quarkus.http.ssl.certificate.key-store-file=server-keystore.p12 + quarkus.http.ssl.certificate.key-store-password=secret + quarkus.http.ssl.certificate.trust-store-file=server-truststore.p12 + quarkus.http.ssl.certificate.trust-store-password=secret + quarkus.http.ssl.client-auth=REQUIRED + quarkus.http.insecure-requests=enabled + + quarkus.http.auth.permission.default.paths=/* + quarkus.http.auth.permission.default.policy=authenticated + """; + + @TestHTTPResource(value = "/mtls", ssl = true) + URL url; + + @TestHTTPResource(value = "/mtls", ssl = false) + URL urlNoTls; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(MyBean.class) + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/mtls-test-keystore.p12"), "server-keystore.p12") + .addAsResource(new File("target/certs/mtls-test-server-truststore.p12"), "server-truststore.p12")); + + @Test + public void testClientAuthentication() { + RestAssured.given() + .keyStore("target/certs/mtls-test-client-keystore.jks", "secret") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(200).body(is("CN=localhost")); + } + + @Test + public void testNoClientCert() { + RestAssured.given() + .get(urlNoTls).then().statusCode(401); + } + + @ApplicationScoped + static class MyBean { + + public void register(@Observes Router router) { + router.get("/mtls").handler(rc -> { + rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); + }); + } + + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java new file mode 100644 index 0000000000000..6230f62508515 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java @@ -0,0 +1,79 @@ +package io.quarkus.vertx.http.mtls; + +import static org.hamcrest.Matchers.is; + +import java.io.File; +import java.net.URL; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.restassured.RestAssured; +import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM }, client = true)) +public class MtlsWithPemTest { + + private static final String configuration = """ + quarkus.http.ssl.certificate.files=server.crt + quarkus.http.ssl.certificate.key-files=server.key + quarkus.http.ssl.certificate.trust-store-file=ca.crt + + quarkus.http.ssl.client-auth=REQUIRED + quarkus.http.insecure-requests=enabled + + quarkus.http.auth.permission.default.paths=/* + quarkus.http.auth.permission.default.policy=authenticated + """; + + @TestHTTPResource(value = "/mtls", ssl = true) + URL url; + + @TestHTTPResource(value = "/mtls", ssl = false) + URL urlNoTls; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(MyBean.class) + .addAsResource(new StringAsset(configuration), "application.properties") + .addAsResource(new File("target/certs/mtls-test.key"), "server.key") + .addAsResource(new File("target/certs/mtls-test.crt"), "server.crt") + .addAsResource(new File("target/certs/mtls-test-server-ca.crt"), "ca.crt")); + + @Test + public void testClientAuthentication() { + RestAssured.given() + .keyStore("target/certs/mtls-test-client-keystore.jks", "secret") + .trustStore("target/certs/mtls-test-client-truststore.jks", "secret") + .get(url).then().statusCode(200).body(is("CN=localhost")); + } + + @Test + public void testNoClientCert() { + RestAssured.given() + .get(urlNoTls).then().statusCode(401); + } + + @ApplicationScoped + static class MyBean { + + public void register(@Observes Router router) { + router.get("/mtls").handler(rc -> { + rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); + }); + } + + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestTest.java deleted file mode 100644 index 93a2a16e9ad3e..0000000000000 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequestTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.quarkus.vertx.http.security; - -import static org.hamcrest.Matchers.is; - -import java.io.File; -import java.net.URL; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusUnitTest; -import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; -import io.restassured.RestAssured; -import io.vertx.ext.web.Router; - -public class MtlsRequestTest { - - @TestHTTPResource(value = "/mtls", ssl = true) - URL url; - - @TestHTTPResource(value = "/mtls", ssl = false) - URL urlNoTls; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/mtls/mtls-no-auth-jks.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/mtls/server-keystore.jks"), "server-keystore.jks") - .addAsResource(new File("src/test/resources/conf/mtls/server-truststore.jks"), "server-truststore.jks")); - - @Test - public void testClientAuthentication() { - RestAssured.given() - .keyStore(new File("src/test/resources/conf/mtls/client-keystore.jks"), "password") - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") - .get(url).then().statusCode(200).body(is("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); - } - - @Test - public void testNoClientCert() { - RestAssured.given() - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") - .get(url).then().statusCode(401); - } - - @ApplicationScoped - static class MyBean { - - public void register(@Observes Router router) { - router.get("/mtls").handler(rc -> { - rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); - }); - } - - } -} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequiredTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequiredTest.java deleted file mode 100644 index 9457f81106d76..0000000000000 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/MtlsRequiredTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.quarkus.vertx.http.security; - -import static org.hamcrest.Matchers.is; - -import java.io.File; -import java.net.URL; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusUnitTest; -import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; -import io.restassured.RestAssured; -import io.vertx.ext.web.Router; - -public class MtlsRequiredTest { - - @TestHTTPResource(value = "/mtls", ssl = true) - URL url; - - @TestHTTPResource(value = "/mtls", ssl = false) - URL urlNoTls; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/mtls/mtls-jks.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/mtls/server-keystore.jks"), "server-keystore.jks") - .addAsResource(new File("src/test/resources/conf/mtls/server-truststore.jks"), "server-truststore.jks")); - - @Test - public void testClientAuthentication() { - RestAssured.given() - .keyStore(new File("src/test/resources/conf/mtls/client-keystore.jks"), "password") - .trustStore(new File("src/test/resources/conf/mtls/client-truststore.jks"), "password") - .get(url).then().statusCode(200).body(is("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); - } - - @Test - public void testNoClientCert() { - RestAssured.given() - .get(urlNoTls).then().statusCode(401); - } - - @ApplicationScoped - static class MyBean { - - public void register(@Observes Router router) { - router.get("/mtls").handler(rc -> { - rc.response().end(QuarkusHttpUser.class.cast(rc.user()).getSecurityIdentity().getPrincipal().getName()); - }); - } - - } -} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java index 0b74e031c0d6b..ffefd7fd6f979 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java @@ -9,8 +9,6 @@ import jakarta.enterprise.event.Observes; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -18,7 +16,12 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class SslServerWithJksTest { @TestHTTPResource(value = "/ssl", ssl = true) @@ -28,22 +31,16 @@ public class SslServerWithJksTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks")); - - @BeforeAll - public static void setupRestAssured() { - RestAssured.useRelaxedHTTPSValidation(); - } - - @AfterAll - public static void restoreRestAssured() { - RestAssured.reset(); - } + .addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks")) + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", "server-keystore.jks") + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "secret"); @Test public void testSslServerWithJKS() { - RestAssured.get(url).then().statusCode(200).body(is("ssl")); + RestAssured + .given() + .trustStore(new File("target/certs/ssl-test-truststore.jks"), "secret") + .get(url).then().statusCode(200).body(is("ssl")); } @ApplicationScoped diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java index 3a3591f20327e..981c9b70cf292 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java @@ -9,8 +9,6 @@ import jakarta.enterprise.event.Observes; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -18,7 +16,12 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class SslServerWithP12Test { @TestHTTPResource(value = "/ssl", ssl = true) @@ -28,22 +31,16 @@ public class SslServerWithP12Test { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-pkcs12.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-keystore.p12"), "server-keystore.pkcs12")); - - @BeforeAll - public static void setupRestAssured() { - RestAssured.useRelaxedHTTPSValidation(); - } - - @AfterAll - public static void restoreRestAssured() { - RestAssured.reset(); - } + .addAsResource(new File("target/certs/ssl-test-keystore.p12"), "server-keystore.pkcs12")) + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", "server-keystore.pkcs12") + .overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "secret"); @Test public void testSslServerWithPkcs12() { - RestAssured.get(url).then().statusCode(200).body(is("ssl")); + RestAssured + .given() + .trustStore(new File("target/certs/ssl-test-truststore.jks"), "secret") + .get(url).then().statusCode(200).body(is("ssl")); } @ApplicationScoped diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java index 448ba943f9e4e..a9580ab8491f5 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java @@ -9,8 +9,7 @@ import jakarta.enterprise.event.Observes; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -18,13 +17,27 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; import io.vertx.ext.web.Router; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; /** * We also set quarkus.http.insecure-requests=disabled in order to test that server starts correctly - see - * https://github.com/quarkusio/quarkus/issues/8336. + * #8336. */ +@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { + Format.JKS, Format.PKCS12, Format.PEM })) public class SslServerWithPemTest { + private static final String configuration = """ + # Enable SSL, configure the key store + quarkus.http.ssl.certificate.files=server-cert.pem + quarkus.http.ssl.certificate.key-files=server-key.pem + # Test that server starts with this option + # See https://github.com/quarkusio/quarkus/issues/8336 + quarkus.http.insecure-requests=disabled + """; + @TestHTTPResource(value = "/ssl", ssl = true) URL url; @@ -32,23 +45,16 @@ public class SslServerWithPemTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(MyBean.class) - .addAsResource(new File("src/test/resources/conf/ssl-pem.conf"), "application.properties") - .addAsResource(new File("src/test/resources/conf/server-key.pem"), "server-key.pem") - .addAsResource(new File("src/test/resources/conf/server-cert.pem"), "server-cert.pem")); - - @BeforeAll - public static void setupRestAssured() { - RestAssured.useRelaxedHTTPSValidation(); - } - - @AfterAll - public static void restoreRestAssured() { - RestAssured.reset(); - } + .addAsResource(new StringAsset((configuration)), "application.properties") + .addAsResource(new File("target/certs/ssl-test.key"), "server-key.pem") + .addAsResource(new File("target/certs/ssl-test.crt"), "server-cert.pem")); @Test public void testSslServerWithPem() { - RestAssured.get(url).then().statusCode(200).body(is("ssl")); + RestAssured + .given() + .trustStore(new File("target/certs/ssl-test-truststore.jks"), "secret") + .get(url).then().statusCode(200).body(is("ssl")); } @ApplicationScoped diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/README.md b/extensions/vertx-http/deployment/src/test/resources/conf/README.md deleted file mode 100644 index 5345acc890d29..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Generate the Self signed PEM, JKS and P12 certificates - -To renew the certificate, run the following script: - -```bash -export SECRET=secret - -# 1. Create RSA private keys and certificates as a key store and export -export JKS_FILE=server-keystore.jks -export CERT_FILE=localhost.crt -export PKCS_FILE=server-keystore.p12 -export PEM_FILE_CERT=server-cert.pem -export PEM_FILE_KEY=server-key.pem -keytool -genkey -alias test-store -keyalg RSA -keystore ${JKS_FILE} -keysize 2048 -validity 1095 -dname CN=localhost -keypass ${SECRET} -storepass ${SECRET} -keytool -export -alias test-store -file ${CERT_FILE} -keystore ${JKS_FILE} -keypass ${SECRET} -storepass ${SECRET} - - -#2. Transform JSK into PKCS12 -keytool -importkeystore -srckeystore ${JKS_FILE} -srcstorepass ${SECRET} -destkeystore ${PKCS_FILE} -deststoretype PKCS12 -deststorepass ${SECRET} - -# 3. Export the PKCS12 into PEM files -openssl pkcs12 -in ${PKCS_FILE} -nodes -passin pass:${SECRET} | openssl pkcs8 -topk8 -inform PEM -outform PEM -out ${PEM_FILE_KEY} -nocrypt -openssl pkcs12 -name test-store -in ${PKCS_FILE} -nokeys -passin pass:${SECRET} -out ${PEM_FILE_CERT} | echo test-store - -``` diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf b/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf deleted file mode 100644 index 580909ea73575..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Enable SSL, configure the key store -quarkus.http.insecure-requests=REDIRECT -quarkus.http.ssl.certificate.files=server-cert.pem -quarkus.http.ssl.certificate.key-files=server-key.pem diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/localhost.crt b/extensions/vertx-http/deployment/src/test/resources/conf/localhost.crt deleted file mode 100644 index f096c251e25c0..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/localhost.crt and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-keystore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-keystore.jks deleted file mode 100644 index cf6d6ba454864..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-keystore.jks and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-truststore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-truststore.jks deleted file mode 100644 index bf6371859c55f..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/client-truststore.jks and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-basic-jks.conf b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-basic-jks.conf deleted file mode 100644 index 9cdaf4679f8e7..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-basic-jks.conf +++ /dev/null @@ -1,7 +0,0 @@ -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks -quarkus.http.ssl.certificate.key-store-password=secret -quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks -quarkus.http.ssl.certificate.trust-store-password=password -quarkus.http.ssl.client-auth=REQUEST -quarkus.http.auth.basic=true -quarkus.http.auth.proactive=true \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-jks.conf b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-jks.conf deleted file mode 100644 index a78a34793096e..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-jks.conf +++ /dev/null @@ -1,9 +0,0 @@ -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks -quarkus.http.ssl.certificate.key-store-password=secret -quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks -quarkus.http.ssl.certificate.trust-store-password=password -quarkus.http.ssl.client-auth=REQUIRED -quarkus.http.insecure-requests=enabled - -quarkus.http.auth.permission.default.paths=/* -quarkus.http.auth.permission.default.policy=authenticated \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-no-auth-jks.conf b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-no-auth-jks.conf deleted file mode 100644 index e1f95f0d7b8f5..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/mtls-no-auth-jks.conf +++ /dev/null @@ -1,7 +0,0 @@ -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks -quarkus.http.ssl.certificate.key-store-password=secret -quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks -quarkus.http.ssl.certificate.trust-store-password=password -quarkus.http.ssl.client-auth=REQUEST -quarkus.http.auth.permission.all.paths=/* -quarkus.http.auth.permission.all.policy=authenticated \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-keystore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-keystore.jks deleted file mode 100644 index da33e8e7a1668..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-keystore.jks and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-truststore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-truststore.jks deleted file mode 100644 index 8ec8e126507b6..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/mtls/server-truststore.jks and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/server-cert.pem b/extensions/vertx-http/deployment/src/test/resources/conf/server-cert.pem deleted file mode 100644 index 837702dd2ab8d..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/server-cert.pem +++ /dev/null @@ -1,22 +0,0 @@ -Bag Attributes - friendlyName: test-store - localKeyID: 54 69 6D 65 20 31 35 36 38 30 34 30 37 34 37 35 34 31 -subject=/CN=localhost -issuer=/CN=localhost ------BEGIN CERTIFICATE----- -MIICxzCCAa+gAwIBAgIEUfdmtTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls -b2NhbGhvc3QwHhcNMTkwOTA5MTQ1MjI1WhcNMjIwOTA4MTQ1MjI1WjAUMRIwEAYD -VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM -Zh82steZt+2yANuCR1xMe5hb+zPSAb9vF/oVfy6DUorbqR+I5quDgvjUNe8RooPr -u5R1d/m0Rm4SEQETMJTUnvYUGTDMKZVCTfFuthM+/+jvFCXC8RdZ4VrQ9NpPWd8p -vVfo3tCC7+5Sabrqd25g4c0FI4oFdECjzeaxCvO3YrBr6XNjlH3RTE0NC6QgbCYR -vHsfTVzstRkwo1kpWgLrRcHQInKLpFv8w4QQ/g/pxzGtb4DvyqubGXqLilb0GQO5 -gm/iWdvmX8lq1qW//d2/tixS9spVS63Bz19uVb4fLz53t61XXaxtzfy/3/rseid1 -+ZWB4smPkyjSk2KuQADxAgMBAAGjITAfMB0GA1UdDgQWBBS8QnI8iaQo0oFYq8GS -76Zcw8ne7zANBgkqhkiG9w0BAQsFAAOCAQEAHVssxFZVcckOOjD/2BB9kiEfAa7g -bSPomLNOK2/y2lPxJSwze3uW9Pazue87PpswYip4o/bQWpfifncHLU6LDZ6EndFn -G2ZCfnjpyHzmNVGf1V32DJi3PdOe4yBPNEetSLSe/v3CW/6kpIZmLZvwGoDSIooe -3oWDg7TeLcwp5v2R2SOMPiUiM4gPY4Scda/gl9y5Eg/IamxzYROtepJjupO6En95 -0B5h1yu0PyyKRsRD+RBf5K7mo6nh5LeyQCHB5yttTkJX3NvbH5wkMOV9QLNUuoAj -ZkGWeMRZCRpZajHwI95U8RcFwhQg3r8rWVYMr5BiHuXiHxA0S+ckvT3Tjw== ------END CERTIFICATE----- diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/server-key.pem b/extensions/vertx-http/deployment/src/test/resources/conf/server-key.pem deleted file mode 100644 index 3bb409cf39a2a..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/server-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCMZh82steZt+2y -ANuCR1xMe5hb+zPSAb9vF/oVfy6DUorbqR+I5quDgvjUNe8RooPru5R1d/m0Rm4S -EQETMJTUnvYUGTDMKZVCTfFuthM+/+jvFCXC8RdZ4VrQ9NpPWd8pvVfo3tCC7+5S -abrqd25g4c0FI4oFdECjzeaxCvO3YrBr6XNjlH3RTE0NC6QgbCYRvHsfTVzstRkw -o1kpWgLrRcHQInKLpFv8w4QQ/g/pxzGtb4DvyqubGXqLilb0GQO5gm/iWdvmX8lq -1qW//d2/tixS9spVS63Bz19uVb4fLz53t61XXaxtzfy/3/rseid1+ZWB4smPkyjS -k2KuQADxAgMBAAECggEAFHNDlKoUMXXTD5yEXMOcNB/En5FFiQ9sHPh3Gu59je9V -/ozx5PUEgpFvQSnh+sWbRZj7FOk23AVuPv9vEqZ+50GIQoEw1GwyK4hC5Ids4j5Q -ZZKftXd8mUD0A3/G1MwYb3H+/hKGeKwOIL751PE0iLQbMuZsUkMOr01sjtkzisCD -RyweFnD/NmlGYJyxZo2/iQi/PD2szkWftL+fkwLQZD0hlYbvJZTZ6rWd68FN+Hg6 -afveOrCgkaH9rsXdodahOUP+fUUPDIJAmSGY0Z+P4t9piBHbdY+4b0dKu2MJzhHz -ZaBIewnWor7yX05UinhPhjFruo0Rf1obZtttvr0XiQKBgQDf5YwKCu+BNZFEkaEq -wwgD9qjYqGEzblVCX+HDvmcgJ7neWBSB4FYRzj9JKZ6jFQbHYHMvb+oO1WbGWF5J -R0YJ9m6qLsRgQKpW3/RFFWzCYHA1NdCxUicHeUVqsA7C/E8TvckAVEUGpZ+8cHsZ -fwyNfkOYW3nADBo+aTFpi8l+awKBgQCgh6sYSetdfTL+WR1K/Z8u2ZSsXEuM/PEH -nQQwkSBiYxHfpKo26s9LpEIHDjmsZQZHKgINNQiXIjQXB11HjiNK3Gn4aAY3HQMH -Ew7zHF7BvIKCJlY4kLU/68HzYlpda2WSTpBGeuiLsx0BHwzQ4utt63TugiiRCbs9 -8vzonEydEwKBgQCCpO3W+lSQn5I5YYSacB0gOvCBI5c8LHhBd7NDxbllEOkq7Vhi -BGYUk7Vn2oo/fMhs1EHB/1qi9zC2MeAqgBVFQO+IDCv4QiNi9+EP4pWIwj8XPWSd -42L0wHWZ8EtcyV7XFRnRQFraqxAPI88grHrFoVSN9WqQfj9eBYGYpcGYKwKBgQCD -3c47PoV8H5ti+jso3dwENTF+wLZDE4i/U0kcyCpNB8qn5vD3+bzdCwo+F35i0f3t -OWHEDZuH4s3dWOxUGjt9hD/8kR8B6PASwKIYLGbeSIcAeJN9Fcn9vyFsV4+Zi948 -6BJ2AwLBtxmNUT9K3Ay/F5rPTdYbBl5sTg1Y2wPIRwKBgQCNJpTTgWXo1o2Ol1nQ -Ju/Asw2HlYmMZIYqOPzXLUqPhTU0SA+M4jod/zQX1RGLb5KE+GQ1gKpU4iz64q3x -3qiNxXghuP3beV/+bSVvcdF9wW/1CJOwV1nVwNjFmdiBZUAJPv6yBeKf+pIZ9nrE -cSuSQnddeA0ojQOLbK6nyaARBw== ------END PRIVATE KEY----- diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.jks deleted file mode 100644 index da33e8e7a1668..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.jks and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.p12 b/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.p12 deleted file mode 100644 index 2a2ba3a17a8df..0000000000000 Binary files a/extensions/vertx-http/deployment/src/test/resources/conf/server-keystore.p12 and /dev/null differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf deleted file mode 100644 index 09b82c863ebe4..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf +++ /dev/null @@ -1,6 +0,0 @@ - -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks -quarkus.http.ssl.certificate.key-store-password=secret - - quarkus.http.limits.rst-flood-max-rst-frame-per-window=10 - quarkus.http.limits.rst-flood-window-duration=10s \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks.conf b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks.conf deleted file mode 100644 index 1aa9d96edb51c..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks.conf +++ /dev/null @@ -1,3 +0,0 @@ -# Enable SSL, configure the key store -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks -quarkus.http.ssl.certificate.key-store-password=secret \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pem.conf b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pem.conf deleted file mode 100644 index 4cbb70dee720e..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pem.conf +++ /dev/null @@ -1,6 +0,0 @@ -# Enable SSL, configure the key store -quarkus.http.ssl.certificate.files=server-cert.pem -quarkus.http.ssl.certificate.key-files=server-key.pem -# Test that server starts with this option -# See https://github.com/quarkusio/quarkus/issues/8336 -quarkus.http.insecure-requests=disabled \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pkcs12.conf b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pkcs12.conf deleted file mode 100644 index 32773cd6029f6..0000000000000 --- a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-pkcs12.conf +++ /dev/null @@ -1,3 +0,0 @@ -# Enable SSL, configure the key store -quarkus.http.ssl.certificate.key-store-file=server-keystore.pkcs12 -quarkus.http.ssl.certificate.key-store-password=secret \ No newline at end of file diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CertificateConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CertificateConfig.java index 74b8668ded172..c5048503200cd 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CertificateConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CertificateConfig.java @@ -104,35 +104,76 @@ public class CertificateConfig { * An optional parameter to select a specific key in the keystore. * When SNI is disabled, and the keystore contains multiple * keys and no alias is specified; the behavior is undefined. + * + * @deprecated Use {@link #keyStoreAlias} instead. */ @ConfigItem + @Deprecated public Optional keyStoreKeyAlias; + /** + * An optional parameter to select a specific key in the keystore. + * When SNI is disabled, and the keystore contains multiple + * keys and no alias is specified; the behavior is undefined. + */ + @ConfigItem + public Optional keyStoreAlias; + /** * An optional parameter to define the password for the key, * in case it is different from {@link #keyStorePassword} * If not given, it might be retrieved from {@linkplain CredentialsProvider}. * * @see {@link #credentialsProvider}. + * @deprecated Use {@link #keyStoreAliasPassword} instead. */ + @Deprecated @ConfigItem public Optional keyStoreKeyPassword; + /** + * An optional parameter to define the password for the key, + * in case it is different from {@link #keyStorePassword} + * If not given, it might be retrieved from {@linkplain CredentialsProvider}. + * + * @see {@link #credentialsProvider}. + */ + @ConfigItem + public Optional keyStoreAliasPassword; + /** * A parameter to specify a {@linkplain CredentialsProvider} property key, - * which can be used to get the password for the key from {@linkplain CredentialsProvider}. + * which can be used to get the password for the alias from {@linkplain CredentialsProvider}. * * @see {@link #credentialsProvider} + * @deprecated Use {@link #keyStoreAliasPasswordKey} instead. */ @ConfigItem + @Deprecated public Optional keyStoreKeyPasswordKey; + /** + * A parameter to specify a {@linkplain CredentialsProvider} property key, + * which can be used to get the password for the alias from {@linkplain CredentialsProvider}. + * + * @see {@link #credentialsProvider} + */ + @ConfigItem + public Optional keyStoreAliasPasswordKey; + /** * An optional trust store that holds the certificate information of the trusted certificates. */ @ConfigItem public Optional trustStoreFile; + /** + * An optional list of trusted certificates using the PEM format. + * If you pass multiple files, you must use the PEM format. + */ + @ConfigItem + public Optional> trustStoreFiles; + /** * An optional parameter to specify the type of the trust store file. * If not given, the type is automatically detected based on the file name. diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java index d53c444263d20..a16c972884260 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java @@ -1,15 +1,13 @@ package io.quarkus.vertx.http.runtime.options; +import static io.quarkus.vertx.http.runtime.options.TlsUtils.computeKeyStoreOptions; +import static io.quarkus.vertx.http.runtime.options.TlsUtils.computeTrustOptions; + import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; import org.jboss.logging.Logger; @@ -24,15 +22,11 @@ import io.quarkus.vertx.http.runtime.ServerSslConfig; import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig; import io.quarkus.vertx.http.runtime.management.ManagementInterfaceConfiguration; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.ClientAuth; import io.vertx.core.http.Http2Settings; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpVersion; -import io.vertx.core.net.JdkSSLEngineOptions; -import io.vertx.core.net.KeyStoreOptions; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.TrafficShapingOptions; +import io.vertx.core.net.*; @SuppressWarnings("OptionalIsPresent") public class HttpServerOptionsUtils { @@ -49,15 +43,6 @@ public static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeCo ServerSslConfig sslConfig = httpConfiguration.ssl; - final List keys = new ArrayList<>(); - final List certificates = new ArrayList<>(); - if (sslConfig.certificate.keyFiles.isPresent()) { - keys.addAll(sslConfig.certificate.keyFiles.get()); - } - if (sslConfig.certificate.files.isPresent()) { - certificates.addAll(sslConfig.certificate.files.get()); - } - // credentials provider Map credentials = Map.of(); if (sslConfig.certificate.credentialsProvider.isPresent()) { @@ -66,14 +51,33 @@ public static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeCo String name = sslConfig.certificate.credentialsProvider.get(); credentials = credentialsProvider.getCredentials(name); } - final Optional keyStoreFile = sslConfig.certificate.keyStoreFile; + final Optional keyStorePassword = getCredential(sslConfig.certificate.keyStorePassword, credentials, sslConfig.certificate.keyStorePasswordKey); - final Optional keyStoreKeyPassword = getCredential(sslConfig.certificate.keyStoreKeyPassword, credentials, - sslConfig.certificate.keyStoreKeyPasswordKey); - final Optional trustStoreFile = sslConfig.certificate.trustStoreFile; + + Optional keyStoreAliasPassword = Optional.empty(); + if (sslConfig.certificate.keyStoreAliasPassword.isPresent() || sslConfig.certificate.keyStoreKeyPassword.isPresent() + || sslConfig.certificate.keyStoreKeyPasswordKey.isPresent() + || sslConfig.certificate.keyStoreAliasPasswordKey.isPresent()) { + if (sslConfig.certificate.keyStoreKeyPasswordKey.isPresent() + && sslConfig.certificate.keyStoreAliasPasswordKey.isPresent()) { + throw new ConfigurationException( + "You cannot specify both `keyStoreKeyPasswordKey` and `keyStoreAliasPasswordKey` - Use `keyStoreAliasPasswordKey` instead"); + } + if (sslConfig.certificate.keyStoreAliasPassword.isPresent() + && sslConfig.certificate.keyStoreKeyPassword.isPresent()) { + throw new ConfigurationException( + "You cannot specify both `keyStoreKeyPassword` and `keyStoreAliasPassword` - Use `keyStoreAliasPassword` instead"); + } + keyStoreAliasPassword = getCredential( + or(sslConfig.certificate.keyStoreAliasPassword, sslConfig.certificate.keyStoreKeyPassword), + credentials, + or(sslConfig.certificate.keyStoreAliasPasswordKey, sslConfig.certificate.keyStoreKeyPasswordKey)); + } + final Optional trustStorePassword = getCredential(sslConfig.certificate.trustStorePassword, credentials, sslConfig.certificate.trustStorePasswordKey); + final HttpServerOptions serverOptions = new HttpServerOptions(); //ssl @@ -85,32 +89,14 @@ public static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeCo } setIdleTimeout(httpConfiguration, serverOptions); - if (!certificates.isEmpty() && !keys.isEmpty()) { - createPemKeyCertOptions(certificates, keys, serverOptions); - } else if (keyStoreFile.isPresent()) { - - KeyStoreOptions options = createKeyStoreOptions( - keyStoreFile.get(), - keyStorePassword.orElse("password"), - sslConfig.certificate.keyStoreFileType, - sslConfig.certificate.keyStoreProvider, - sslConfig.certificate.keyStoreKeyAlias, - keyStoreKeyPassword); - serverOptions.setKeyCertOptions(options); + var kso = computeKeyStoreOptions(sslConfig.certificate, keyStorePassword, keyStoreAliasPassword); + if (kso != null) { + serverOptions.setKeyCertOptions(kso); } - if (trustStoreFile.isPresent()) { - if (!trustStorePassword.isPresent()) { - throw new IllegalArgumentException("No trust store password provided"); - } - KeyStoreOptions options = createKeyStoreOptions( - trustStoreFile.get(), - trustStorePassword.get(), - sslConfig.certificate.trustStoreFileType, - sslConfig.certificate.trustStoreProvider, - sslConfig.certificate.trustStoreCertAlias, - Optional.empty()); - serverOptions.setTrustOptions(options); + var to = computeTrustOptions(sslConfig.certificate, trustStorePassword); + if (to != null) { + serverOptions.setTrustOptions(to); } for (String cipher : sslConfig.cipherSuites.orElse(Collections.emptyList())) { @@ -143,15 +129,6 @@ public static HttpServerOptions createSslOptionsForManagementInterface(Managemen ServerSslConfig sslConfig = httpConfiguration.ssl; - final List keys = new ArrayList<>(); - final List certificates = new ArrayList<>(); - if (sslConfig.certificate.keyFiles.isPresent()) { - keys.addAll(sslConfig.certificate.keyFiles.get()); - } - if (sslConfig.certificate.files.isPresent()) { - certificates.addAll(sslConfig.certificate.files.get()); - } - // credentials provider Map credentials = Map.of(); if (sslConfig.certificate.credentialsProvider.isPresent()) { @@ -160,14 +137,33 @@ public static HttpServerOptions createSslOptionsForManagementInterface(Managemen String name = sslConfig.certificate.credentialsProvider.get(); credentials = credentialsProvider.getCredentials(name); } - final Optional keyStoreFile = sslConfig.certificate.keyStoreFile; + final Optional keyStorePassword = getCredential(sslConfig.certificate.keyStorePassword, credentials, sslConfig.certificate.keyStorePasswordKey); - final Optional keyStoreKeyPassword = getCredential(sslConfig.certificate.keyStoreKeyPassword, credentials, - sslConfig.certificate.keyStoreKeyPasswordKey); - final Optional trustStoreFile = sslConfig.certificate.trustStoreFile; + + Optional keyStoreAliasPassword = Optional.empty(); + if (sslConfig.certificate.keyStoreAliasPassword.isPresent() || sslConfig.certificate.keyStoreKeyPassword.isPresent() + || sslConfig.certificate.keyStoreKeyPasswordKey.isPresent() + || sslConfig.certificate.keyStoreAliasPasswordKey.isPresent()) { + if (sslConfig.certificate.keyStoreKeyPasswordKey.isPresent() + && sslConfig.certificate.keyStoreAliasPasswordKey.isPresent()) { + throw new ConfigurationException( + "You cannot specify both `keyStoreKeyPasswordKey` and `keyStoreAliasPasswordKey` - Use `keyStoreAliasPasswordKey` instead"); + } + if (sslConfig.certificate.keyStoreAliasPassword.isPresent() + && sslConfig.certificate.keyStoreKeyPassword.isPresent()) { + throw new ConfigurationException( + "You cannot specify both `keyStoreKeyPassword` and `keyStoreAliasPassword` - Use `keyStoreAliasPassword` instead"); + } + keyStoreAliasPassword = getCredential( + or(sslConfig.certificate.keyStoreAliasPassword, sslConfig.certificate.keyStoreKeyPassword), + credentials, + or(sslConfig.certificate.keyStoreAliasPasswordKey, sslConfig.certificate.keyStoreKeyPasswordKey)); + } + final Optional trustStorePassword = getCredential(sslConfig.certificate.trustStorePassword, credentials, sslConfig.certificate.trustStorePasswordKey); + final HttpServerOptions serverOptions = new HttpServerOptions(); //ssl @@ -179,31 +175,14 @@ public static HttpServerOptions createSslOptionsForManagementInterface(Managemen serverOptions.setIdleTimeout(idleTimeout); serverOptions.setIdleTimeoutUnit(TimeUnit.MILLISECONDS); - if (!certificates.isEmpty() && !keys.isEmpty()) { - createPemKeyCertOptions(certificates, keys, serverOptions); - } else if (keyStoreFile.isPresent()) { - KeyStoreOptions options = createKeyStoreOptions( - keyStoreFile.get(), - keyStorePassword.orElse("password"), - sslConfig.certificate.keyStoreFileType, - sslConfig.certificate.keyStoreProvider, - sslConfig.certificate.keyStoreKeyAlias, - keyStoreKeyPassword); - serverOptions.setKeyCertOptions(options); + var kso = computeKeyStoreOptions(sslConfig.certificate, keyStorePassword, keyStoreAliasPassword); + if (kso != null) { + serverOptions.setKeyCertOptions(kso); } - if (trustStoreFile.isPresent()) { - if (!trustStorePassword.isPresent()) { - throw new IllegalArgumentException("No trust store password provided"); - } - KeyStoreOptions options = createKeyStoreOptions( - trustStoreFile.get(), - trustStorePassword.get(), - sslConfig.certificate.trustStoreFileType, - sslConfig.certificate.trustStoreProvider, - sslConfig.certificate.trustStoreCertAlias, - Optional.empty()); - serverOptions.setTrustOptions(options); + var to = computeTrustOptions(sslConfig.certificate, trustStorePassword); + if (to != null) { + serverOptions.setTrustOptions(to); } for (String cipher : sslConfig.cipherSuites.orElse(Collections.emptyList())) { @@ -350,26 +329,6 @@ public static void applyCommonOptionsForManagementInterface(HttpServerOptions op options.setUseProxyProtocol(httpConfiguration.proxy.useProxyProtocol); } - private static KeyStoreOptions createKeyStoreOptions(Path path, String password, Optional fileType, - Optional provider, Optional alias, Optional aliasPassword) throws IOException { - final String type; - if (fileType.isPresent()) { - type = fileType.get().toLowerCase(); - } else { - type = findKeystoreFileType(path); - } - - byte[] data = getFileContent(path); - KeyStoreOptions options = new KeyStoreOptions() - .setPassword(password) - .setValue(Buffer.buffer(data)) - .setType(type.toUpperCase()) - .setProvider(provider.orElse(null)) - .setAlias(alias.orElse(null)) - .setAliasPassword(aliasPassword.orElse(null)); - return options; - } - static byte[] getFileContent(Path path) throws IOException { byte[] data; final InputStream resource = Thread.currentThread().getContextClassLoader() @@ -386,43 +345,6 @@ static byte[] getFileContent(Path path) throws IOException { return data; } - private static void createPemKeyCertOptions(List certFile, List keyFile, - HttpServerOptions serverOptions) throws IOException { - - if (certFile.size() != keyFile.size()) { - throw new ConfigurationException("Invalid certificate configuration - `files` and `keyFiles` must have the " - + "same number of elements"); - } - - List certificates = new ArrayList<>(); - List keys = new ArrayList<>(); - - for (Path p : certFile) { - final byte[] cert = getFileContent(p); - certificates.add(Buffer.buffer(cert)); - } - - for (Path p : keyFile) { - final byte[] key = getFileContent(p); - keys.add(Buffer.buffer(key)); - } - - PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions() - .setCertValues(certificates) - .setKeyValues(keys); - serverOptions.setPemKeyCertOptions(pemKeyCertOptions); - } - - private static String findKeystoreFileType(Path storePath) { - final String pathName = storePath.toString(); - if (pathName.endsWith(".p12") || pathName.endsWith(".pkcs12") || pathName.endsWith(".pfx")) { - return "pkcs12"; - } else { - // assume jks - return "jks"; - } - } - private static byte[] doRead(InputStream is) throws IOException { return is.readAllBytes(); } @@ -453,4 +375,9 @@ public static HttpConfiguration.InsecureRequests getInsecureRequestStrategy(Http } return HttpConfiguration.InsecureRequests.ENABLED; } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + static Optional or(Optional a, Optional b) { + return a.isPresent() ? a : b; + } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloader.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloader.java index 07dc2d73259b3..fcf993863b739 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloader.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloader.java @@ -149,12 +149,8 @@ private static SSLOptions reloadFileContent(SSLOptions ssl, ServerSslConfig conf final List keys = new ArrayList<>(); final List certificates = new ArrayList<>(); - if (configuration.certificate.keyFiles.isPresent()) { - keys.addAll(configuration.certificate.keyFiles.get()); - } - if (configuration.certificate.files.isPresent()) { - certificates.addAll(configuration.certificate.files.get()); - } + configuration.certificate.keyFiles.ifPresent(keys::addAll); + configuration.certificate.files.ifPresent(certificates::addAll); if (!certificates.isEmpty() && !keys.isEmpty()) { List certBuffer = new ArrayList<>(); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsUtils.java new file mode 100644 index 0000000000000..7b2a08c729cb6 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsUtils.java @@ -0,0 +1,159 @@ +package io.quarkus.vertx.http.runtime.options; + +import static io.quarkus.vertx.http.runtime.options.HttpServerOptionsUtils.getFileContent; +import static io.quarkus.vertx.http.runtime.options.HttpServerOptionsUtils.or; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import io.quarkus.vertx.http.runtime.CertificateConfig; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.net.*; + +/** + * Utility class for TLS configuration. + */ +public class TlsUtils { + + private TlsUtils() { + // Avoid direct instantiation + } + + public static KeyCertOptions computeKeyStoreOptions(CertificateConfig certificates, Optional keyStorePassword, + Optional keyStoreAliasPassword) throws IOException { + if (certificates.keyFiles.isPresent() || certificates.files.isPresent()) { + if (certificates.keyFiles.isEmpty()) { + throw new IllegalArgumentException("You must specify the key files when specifying the certificate files"); + } + if (certificates.files.isEmpty()) { + throw new IllegalArgumentException("You must specify the certificate files when specifying the key files"); + } + if (certificates.files.get().size() != certificates.keyFiles.get().size()) { + throw new IllegalArgumentException( + "The number of certificate files and key files must be the same, and be given in the same order"); + } + + return createPemKeyCertOptions(certificates.files.get(), certificates.keyFiles.get()); + } else if (certificates.keyStoreFile.isPresent()) { + return createKeyStoreOptions( + certificates.keyStoreFile.get(), + keyStorePassword, + certificates.keyStoreFileType, + certificates.keyStoreProvider, + or(certificates.keyStoreAlias, certificates.keyStoreKeyAlias), + keyStoreAliasPassword); + } + return null; + } + + public static TrustOptions computeTrustOptions(CertificateConfig certificates, Optional trustStorePassword) + throws IOException { + // Decide if we have a single trust store file or multiple trust store files (PEM) + Path singleTrustStoreFile = null; + if (certificates.trustStoreFile.isPresent()) { + singleTrustStoreFile = certificates.trustStoreFile.get(); + } + if (certificates.trustStoreFiles.isPresent()) { + if (singleTrustStoreFile != null) { + throw new IllegalArgumentException("You cannot specify both `trustStoreFile` and `trustStoreFiles`"); + } + if (certificates.trustStoreFiles.get().size() == 1) { + singleTrustStoreFile = certificates.trustStoreFiles.get().get(0); + } + } + + if (singleTrustStoreFile != null) { // We have a single trust store file. + String type = certificates.trustStoreFileType.orElse(getTypeFromFileName(singleTrustStoreFile)); + if (type.equalsIgnoreCase("pem")) { + byte[] cert = getFileContent(singleTrustStoreFile); + return new PemTrustOptions() + .addCertValue(Buffer.buffer(cert)); + } + + if ((type.equalsIgnoreCase("pkcs12") || type.equalsIgnoreCase("jks"))) { + // We cannot assume that custom type configured by the user requires a password. + if (certificates.trustStorePassword.isEmpty() && trustStorePassword.isEmpty()) { + throw new IllegalArgumentException("No trust store password provided"); + } + } + + return createKeyStoreOptions( + singleTrustStoreFile, + trustStorePassword, + certificates.trustStoreFileType, + certificates.trustStoreProvider, + certificates.trustStoreCertAlias, + Optional.empty()); + } + + // We have multiple trust store files (PEM). + if (certificates.trustStoreFiles.isPresent() && !certificates.trustStoreFiles.get().isEmpty()) { + // Assuming PEM, as it's the only format with multiple files + PemTrustOptions pemKeyCertOptions = new PemTrustOptions(); + for (Path path : certificates.trustStoreFiles.get()) { + byte[] cert = getFileContent(path); + pemKeyCertOptions.addCertValue(Buffer.buffer(cert)); + } + return pemKeyCertOptions; + } + + return null; + } + + private static String getTypeFromFileName(Path path) { + String name = path.getFileName().toString().toLowerCase(); + if (name.endsWith(".p12") || name.endsWith(".pkcs12") || name.endsWith(".pfx")) { + return "pkcs12"; + } else if (name.endsWith(".jks")) { + return "jks"; + } else if (name.endsWith(".key") || name.endsWith(".crt") || name.endsWith(".pem")) { + return "pem"; + } else { + throw new IllegalArgumentException("Could not determine the trust store type from the file name: " + path + + ". Configure the file type property."); + } + + } + + private static KeyStoreOptions createKeyStoreOptions(Path path, Optional password, Optional fileType, + Optional provider, Optional alias, Optional aliasPassword) throws IOException { + final String type; + if (fileType.isPresent()) { + type = fileType.get().toLowerCase(); + } else { + type = getTypeFromFileName(path); + } + + byte[] data = getFileContent(path); + return new KeyStoreOptions() + .setPassword(password.orElse(null)) + .setValue(Buffer.buffer(data)) + .setType(type.toUpperCase()) + .setProvider(provider.orElse(null)) + .setAlias(alias.orElse(null)) + .setAliasPassword(aliasPassword.orElse(null)); + } + + private static PemKeyCertOptions createPemKeyCertOptions(List certFile, List keyFile) throws IOException { + List certificates = new ArrayList<>(); + List keys = new ArrayList<>(); + + for (Path p : certFile) { + final byte[] cert = getFileContent(p); + certificates.add(Buffer.buffer(cert)); + } + + for (Path p : keyFile) { + final byte[] key = getFileContent(p); + keys.add(Buffer.buffer(key)); + } + + return new PemKeyCertOptions() + .setCertValues(certificates) + .setKeyValues(keys); + } + +}