diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java index 12c830977b9c8..81c315c22837d 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java @@ -95,6 +95,11 @@ public RestClientBuilderImpl trustStore(KeyStore trustStore) { return this; } + public RestClientBuilderImpl trustStore(KeyStore trustStore, String trustStorePassword) { + clientBuilder.trustStore(trustStore, trustStorePassword.toCharArray()); + return this; + } + @Override public RestClientBuilderImpl keyStore(KeyStore keyStore, String keystorePassword) { clientBuilder.keyStore(keyStore, keystorePassword); diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java index 83e647ae666df..d5f998178a40c 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java @@ -182,7 +182,7 @@ private void configureShared(RestClientBuilder builder) { } } - private void configureSsl(RestClientBuilder builder) { + private void configureSsl(RestClientBuilderImpl builder) { Optional maybeTrustStore = oneOf(clientConfigByClassName().trustStore, clientConfigByConfigKey().trustStore, configRoot.trustStore); @@ -249,7 +249,7 @@ private void registerKeyStore(String keyStorePath, RestClientBuilder builder) { } } - private void registerTrustStore(String trustStorePath, RestClientBuilder builder) { + private void registerTrustStore(String trustStorePath, RestClientBuilderImpl builder) { Optional maybeTrustStorePassword = oneOf(clientConfigByClassName().trustStorePassword, clientConfigByConfigKey().trustStorePassword, configRoot.trustStorePassword); Optional maybeTrustStoreType = oneOf(clientConfigByClassName().trustStoreType, @@ -269,7 +269,7 @@ private void registerTrustStore(String trustStorePath, RestClientBuilder builder e); } - builder.trustStore(trustStore); + builder.trustStore(trustStore, password); } catch (KeyStoreException e) { throw new IllegalArgumentException("Failed to initialize trust store from " + trustStorePath, e); } diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java index 60f80120fb6a7..9a0ec4493673d 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java @@ -109,7 +109,7 @@ public void testClientSpecificConfigs() { Mockito.verify(restClientBuilderMock).register(MyResponseFilter1.class); Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.COMMA_SEPARATED); - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); + Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } @@ -151,7 +151,7 @@ public void testGlobalConfigs() { Mockito.verify(restClientBuilderMock).register(MyResponseFilter2.class); Mockito.verify(restClientBuilderMock).queryParamStyle(QueryParamStyle.MULTI_PAIRS); - Mockito.verify(restClientBuilderMock).trustStore(Mockito.any()); + Mockito.verify(restClientBuilderMock).trustStore(Mockito.any(), Mockito.anyString()); Mockito.verify(restClientBuilderMock).keyStore(Mockito.any(), Mockito.anyString()); } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java index e8020ea6c1547..6afe4fb4d6e52 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java @@ -46,6 +46,7 @@ public class ClientBuilderImpl extends ClientBuilder { private char[] keystorePassword; private SSLContext sslContext; private KeyStore trustStore; + private char[] trustStorePassword; private String proxyHost; private int proxyPort; @@ -88,7 +89,12 @@ public ClientBuilder keyStore(KeyStore keyStore, char[] password) { @Override public ClientBuilder trustStore(KeyStore trustStore) { + return trustStore(trustStore, null); + } + + public ClientBuilder trustStore(KeyStore trustStore, char[] password) { this.trustStore = trustStore; + this.trustStorePassword = password; return this; } @@ -164,7 +170,7 @@ public ClientBuilder clientLogger(ClientLogger clientLogger) { @Override public ClientImpl build() { Buffer keyStore = asBuffer(this.keyStore, keystorePassword); - Buffer trustStore = asBuffer(this.trustStore, EMPTY_CHAR_ARARAY); + Buffer trustStore = asBuffer(this.trustStore, this.trustStorePassword); HttpClientOptions options = Optional.ofNullable(configuration.getFromContext(HttpClientOptions.class)) .orElseGet(HttpClientOptions::new); @@ -185,7 +191,7 @@ public ClientImpl build() { if (trustStore != null) { JksOptions jks = new JksOptions(); jks.setValue(trustStore); - jks.setPassword(""); + jks.setPassword(trustStorePassword == null ? "" : new String(trustStorePassword)); options.setTrustStoreOptions(jks); } } diff --git a/integration-tests/rest-client-reactive/pom.xml b/integration-tests/rest-client-reactive/pom.xml index 8889bbc39f7ad..d81583d01c89e 100644 --- a/integration-tests/rest-client-reactive/pom.xml +++ b/integration-tests/rest-client-reactive/pom.xml @@ -11,6 +11,11 @@ quarkus-integration-test-rest-client-reactive Quarkus - Integration Tests - REST Client Reactive + + ${project.build.directory}/self-signed.p12 + changeit + + @@ -165,6 +170,31 @@ + + + uk.co.automatictester + truststore-maven-plugin + ${truststore-maven-plugin.version} + + + self-signed-truststore + generate-test-resources + + generate-truststore + + + PKCS12 + ${self-signed.trust-store} + ${self-signed.trust-store-password} + + self-signed.badssl.com:443 + + true + LEAF + + + + diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java index 5b6252ce83a00..1e311fef17815 100644 --- a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java @@ -21,6 +21,7 @@ import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.quarkus.it.rest.client.main.MyResponseExceptionMapper.MyException; +import io.quarkus.it.rest.client.main.selfsigned.ExternalSelfSignedClient; import io.smallrye.mutiny.Uni; import io.vertx.core.Future; import io.vertx.core.json.Json; @@ -44,6 +45,9 @@ public class ClientCallingResource { @RestClient FaultToleranceOnInterfaceClient faultToleranceOnInterfaceClient; + @RestClient + ExternalSelfSignedClient externalSelfSignedClient; + @Inject InMemorySpanExporter inMemorySpanExporter; @@ -165,6 +169,9 @@ void init(@Observes Router router) { }); router.get("/with%20space").handler(rc -> rc.response().setStatusCode(200).end()); + + router.get("/self-signed").blockingHandler( + rc -> rc.response().setStatusCode(200).end(String.valueOf(externalSelfSignedClient.invoke().getStatus()))); } private Future success(RoutingContext rc, String body) { diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java new file mode 100644 index 0000000000000..7d2610f75b4be --- /dev/null +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/selfsigned/ExternalSelfSignedClient.java @@ -0,0 +1,15 @@ +package io.quarkus.it.rest.client.main.selfsigned; + +import javax.ws.rs.GET; +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.faulttolerance.Retry; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient(baseUri = "https://self-signed.badssl.com/", configKey = "self-signed") +public interface ExternalSelfSignedClient { + + @GET + @Retry(delay = 1000) + Response invoke(); +} diff --git a/integration-tests/rest-client-reactive/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/main/resources/application.properties index 392722ce0c912..03c013f85ce30 100644 --- a/integration-tests/rest-client-reactive/src/main/resources/application.properties +++ b/integration-tests/rest-client-reactive/src/main/resources/application.properties @@ -1,4 +1,7 @@ w-exception-mapper/mp-rest/url=${test.url} w-fault-tolerance/mp-rest/url=${test.url} io.quarkus.it.rest.client.main.ParamClient/mp-rest/url=${test.url} -io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url} \ No newline at end of file +io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url} +# HTTPS +quarkus.rest-client.self-signed.trust-store=${self-signed.trust-store} +quarkus.rest-client.self-signed.trust-store-password=${self-signed.trust-store-password} diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java new file mode 100644 index 0000000000000..1348f532290fc --- /dev/null +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/selfsigned/ExternalSelfSignedTestCase.java @@ -0,0 +1,21 @@ +package io.quarkus.it.rest.client.selfsigned; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ExternalSelfSignedTestCase { + + @Test + public void should_accept_self_signed_certs() { + when() + .get("/self-signed") + .then() + .statusCode(200) + .body(is("200")); + } +}