diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b433b65cc..5960282ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,7 +79,7 @@ jobs: strategy: fail-fast: false matrix: - testModule: ['client', 'client-server', 'fastinfoset', 'hc5', 'metrics', 'mtom', 'mtom-awt', 'opentelemetry', 'santuario-xmlsec', 'server', 'ws-rm-client', 'ws-security', 'ws-security -Djks', 'ws-security-policy', 'ws-security-policy -Djks', 'ws-trust', 'wsdl2java', 'wsdl2java-no-config'] + testModule: ['client', 'client-server', 'fastinfoset', 'hc5', 'metrics', 'mtls', 'mtls -Djks', 'mtom', 'mtom-awt', 'opentelemetry', 'santuario-xmlsec', 'server', 'ws-rm-client', 'ws-security', 'ws-security -Djks', 'ws-security-policy', 'ws-security-policy -Djks', 'ws-trust', 'wsdl2java', 'wsdl2java-no-config'] name: ${{matrix.testModule}} native tests needs: build-and-run-jvm-tests runs-on: ubuntu-latest diff --git a/docs/modules/ROOT/examples/ws-security-policy/application.properties b/docs/modules/ROOT/examples/ws-security-policy/application.properties index 25fcf932b..5d98402e3 100644 --- a/docs/modules/ROOT/examples/ws-security-policy/application.properties +++ b/docs/modules/ROOT/examples/ws-security-policy/application.properties @@ -3,10 +3,10 @@ # Server side SSL # tag::server-key-store[] # <1> -quarkus.http.ssl.certificate.key-store-file = localhost.${keystore.type} -quarkus.http.ssl.certificate.key-store-password = password +quarkus.http.ssl.certificate.key-store-file = localhost-keystore.${keystore.type} +quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password quarkus.http.ssl.certificate.key-store-key-alias = localhost -quarkus.http.ssl.certificate.key-store-key-password = password +quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password # end::server-key-store[] # tag::quarkus-cxf-rt-ws-security.adoc-service[] @@ -16,19 +16,19 @@ quarkus.cxf.endpoint."/helloEncryptSign".implementor = io.quarkiverse.cxf.it.sec keystore.type = ${keystore.type} # Signature settings quarkus.cxf.endpoint."/helloEncryptSign".security.signature.username = bob -quarkus.cxf.endpoint."/helloEncryptSign".security.signature.password = password +quarkus.cxf.endpoint."/helloEncryptSign".security.signature.password = bob-keystore-password quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.type" = ${keystore.type} -quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = bob-keystore-password quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = bob -quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob.${keystore.type} +quarkus.cxf.endpoint."/helloEncryptSign".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob-keystore.${keystore.type} # Encryption settings quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.username = alice quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.type" = ${keystore.type} -quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.password" = bob-keystore-password quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = bob -quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.file" = bob.${keystore.type} +quarkus.cxf.endpoint."/helloEncryptSign".security.encryption.properties."org.apache.ws.security.crypto.merlin.file" = bob-keystore.${keystore.type} # end::quarkus-cxf-rt-ws-security.adoc-service[] # This is only to be able to assert some specific error messages in tests @@ -42,22 +42,22 @@ quarkus.cxf.client.helloEncryptSign.service-interface = io.quarkiverse.cxf.it.se quarkus.cxf.client.helloEncryptSign.features = #messageCollector # The client-endpoint-url above is HTTPS, so we have to setup the server's SSL certificates quarkus.cxf.client.helloEncryptSign.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloEncryptSign.trust-store-password = password +quarkus.cxf.client.helloEncryptSign.trust-store-password = client-truststore-password # Signature settings quarkus.cxf.client.helloEncryptSign.security.signature.username = alice -quarkus.cxf.client.helloEncryptSign.security.signature.password = password +quarkus.cxf.client.helloEncryptSign.security.signature.password = alice-keystore-password quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.type" = pkcs12 -quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = alice-keystore-password quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = alice -quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.file" = alice.${keystore.type} +quarkus.cxf.client.helloEncryptSign.security.signature.properties."org.apache.ws.security.crypto.merlin.file" = alice-keystore.${keystore.type} # Encryption settings quarkus.cxf.client.helloEncryptSign.security.encryption.username = bob quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.type" = pkcs12 -quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.password" = alice-keystore-password quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = alice -quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.file" = alice.${keystore.type} +quarkus.cxf.client.helloEncryptSign.security.encryption.properties."org.apache.ws.security.crypto.merlin.file" = alice-keystore.${keystore.type} # end::quarkus-cxf-rt-ws-security.adoc-client[] quarkus.native.resources.includes = *.xml,*.pkcs12 @@ -89,7 +89,7 @@ quarkus.cxf.endpoint."/helloUsernameTokenUncachedNonce".security.enable.nonce.ca quarkus.cxf.endpoint."/helloEncryptSignCrypto".implementor = io.quarkiverse.cxf.it.security.policy.EncryptSignPolicyHelloServiceImpl quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.return.security.error = true quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.signature.username = bob -quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.signature.password = password +quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.signature.password = bob-keystore-password quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.signature.crypto = #bobCrypto quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.encryption.username = alice quarkus.cxf.endpoint."/helloEncryptSignCrypto".security.encryption.crypto = #bobCrypto @@ -98,9 +98,9 @@ quarkus.cxf.client.helloEncryptSignCrypto.client-endpoint-url = https://localhos quarkus.cxf.client.helloEncryptSignCrypto.service-interface = io.quarkiverse.cxf.it.security.policy.EncryptSignPolicyHelloService quarkus.cxf.client.helloEncryptSignCrypto.features = #messageCollector quarkus.cxf.client.helloEncryptSignCrypto.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloEncryptSignCrypto.trust-store-password = password +quarkus.cxf.client.helloEncryptSignCrypto.trust-store-password = client-truststore-password quarkus.cxf.client.helloEncryptSignCrypto.security.signature.username = alice -quarkus.cxf.client.helloEncryptSignCrypto.security.signature.password = password +quarkus.cxf.client.helloEncryptSignCrypto.security.signature.password = alice-keystore-password quarkus.cxf.client.helloEncryptSignCrypto.security.signature.crypto = #aliceCrypto quarkus.cxf.client.helloEncryptSignCrypto.security.encryption.username = bob quarkus.cxf.client.helloEncryptSignCrypto.security.encryption.crypto = #aliceCrypto @@ -109,24 +109,24 @@ quarkus.cxf.client.helloEncryptSignCrypto.security.encryption.crypto = #aliceCry quarkus.cxf.endpoint."/helloSaml1".implementor = io.quarkiverse.cxf.it.security.policy.Saml1PolicyHelloServiceImpl quarkus.cxf.endpoint."/helloSaml1".security.return.security.error = true quarkus.cxf.endpoint."/helloSaml1".security.signature.username = bob -quarkus.cxf.endpoint."/helloSaml1".security.signature.password = password +quarkus.cxf.endpoint."/helloSaml1".security.signature.password = bob-keystore-password quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.type" = pkcs12 -quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = bob-keystore-password quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = bob -quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob.${keystore.type} +quarkus.cxf.endpoint."/helloSaml1".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob-keystore.${keystore.type} quarkus.cxf.endpoint."/helloSaml1".security.saml-callback-handler = #saml1CallbackHandler quarkus.cxf.endpoint."/helloSaml2".implementor = io.quarkiverse.cxf.it.security.policy.Saml2PolicyHelloServiceImpl quarkus.cxf.endpoint."/helloSaml2".security.return.security.error = true quarkus.cxf.endpoint."/helloSaml2".security.signature.username = bob -quarkus.cxf.endpoint."/helloSaml2".security.signature.password = password +quarkus.cxf.endpoint."/helloSaml2".security.signature.password = bob-keystore-password quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.provider" = org.apache.ws.security.components.crypto.Merlin quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.type" = pkcs12 -quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = password +quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.password" = bob-keystore-password quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.keystore.alias" = bob -quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob.${keystore.type} +quarkus.cxf.endpoint."/helloSaml2".security.signature.properties."org.apache.ws.security.crypto.merlin.file" = bob-keystore.${keystore.type} quarkus.cxf.endpoint."/helloSaml2".security.saml-callback-handler = #saml2CallbackHandler # Clients # tag::client-trust-store[] @@ -135,47 +135,47 @@ quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.poli quarkus.cxf.client.hello.trust-store-type = ${keystore.type} # <2> quarkus.cxf.client.hello.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.hello.trust-store-password = password +quarkus.cxf.client.hello.trust-store-password = client-truststore-password # end::client-trust-store[] quarkus.cxf.client.helloAllowAll.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.helloAllowAll.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloAllowAll.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloAllowAll.trust-store-password = password +quarkus.cxf.client.helloAllowAll.trust-store-password = client-truststore-password quarkus.cxf.client.helloAllowAll.hostname-verifier = AllowAllHostnameVerifier quarkus.cxf.client.helloCustomHostnameVerifier.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.helloCustomHostnameVerifier.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloCustomHostnameVerifier.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloCustomHostnameVerifier.trust-store-password = password +quarkus.cxf.client.helloCustomHostnameVerifier.trust-store-password = client-truststore-password quarkus.cxf.client.helloCustomHostnameVerifier.hostname-verifier = io.quarkiverse.cxf.it.security.policy.NoopHostnameVerifier quarkus.cxf.client.helloIp.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.helloIp.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloIp.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloIp.trust-store-password = password +quarkus.cxf.client.helloIp.trust-store-password = client-truststore-password quarkus.cxf.client.helloHttps.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloHttps quarkus.cxf.client.helloHttps.service-interface = io.quarkiverse.cxf.it.security.policy.HttpsPolicyHelloService quarkus.cxf.client.helloHttps.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloHttps.trust-store-password = password +quarkus.cxf.client.helloHttps.trust-store-password = client-truststore-password quarkus.cxf.client.helloHttps.features = #messageCollector quarkus.cxf.client.helloHttpsPkcs12.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloHttps quarkus.cxf.client.helloHttpsPkcs12.service-interface = io.quarkiverse.cxf.it.security.policy.HttpsPolicyHelloService quarkus.cxf.client.helloHttpsPkcs12.trust-store = client-truststore.${keystore.type} quarkus.cxf.client.helloHttpsPkcs12.trust-store-type = PKCS12 -quarkus.cxf.client.helloHttpsPkcs12.trust-store-password = password +quarkus.cxf.client.helloHttpsPkcs12.trust-store-password = client-truststore-password quarkus.cxf.client.helloHttp.client-endpoint-url = http://localhost:${quarkus.http.test-port}/services/helloHttps quarkus.cxf.client.helloHttp.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloHttp.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloHttp.trust-store-password = password +quarkus.cxf.client.helloHttp.trust-store-password = client-truststore-password quarkus.cxf.client.helloUsernameToken.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameToken quarkus.cxf.client.helloUsernameToken.service-interface = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloService quarkus.cxf.client.helloUsernameToken.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloUsernameToken.trust-store-password = password +quarkus.cxf.client.helloUsernameToken.trust-store-password = client-truststore-password quarkus.cxf.client.helloUsernameToken.security.username = ${wss.user} quarkus.cxf.client.helloUsernameToken.security.callback-handler = #usernameTokenPasswordCallback quarkus.cxf.client.helloUsernameToken.features = #messageCollector @@ -183,7 +183,7 @@ quarkus.cxf.client.helloUsernameToken.features = #messageCollector quarkus.cxf.client.helloUsernameTokenAlt.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameTokenAlt quarkus.cxf.client.helloUsernameTokenAlt.service-interface = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloService quarkus.cxf.client.helloUsernameTokenAlt.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloUsernameTokenAlt.trust-store-password = password +quarkus.cxf.client.helloUsernameTokenAlt.trust-store-password = client-truststore-password quarkus.cxf.client.helloUsernameTokenAlt.security.username = ${wss.user} quarkus.cxf.client.helloUsernameTokenAlt.security.password = ${wss.password} quarkus.cxf.client.helloUsernameTokenAlt.features = #messageCollector @@ -191,7 +191,7 @@ quarkus.cxf.client.helloUsernameTokenAlt.features = #messageCollector quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameToken quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.service-interface = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloService quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.trust-store-password = password +quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.trust-store-password = client-truststore-password quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.security.username = ${wss.user} quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.security.callback-handler = #usernameTokenPasswordCallback quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.security.must-understand = false @@ -201,29 +201,27 @@ quarkus.cxf.client.helloUsernameTokenNoMustUnderstand.features = #messageCollect quarkus.cxf.client.helloNoUsernameToken.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameToken quarkus.cxf.client.helloNoUsernameToken.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloNoUsernameToken.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloNoUsernameToken.trust-store-password = password +quarkus.cxf.client.helloNoUsernameToken.trust-store-password = client-truststore-password quarkus.cxf.client.helloNoUsernameToken.security.username = ${wss.user} quarkus.cxf.client.helloNoUsernameToken.security.password = ${wss.password} quarkus.cxf.client.helloSaml1.service-interface = io.quarkiverse.cxf.it.security.policy.Saml1PolicyHelloService quarkus.cxf.client.helloSaml1.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloSaml1 quarkus.cxf.client.helloSaml1.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloSaml1.trust-store-password = password +quarkus.cxf.client.helloSaml1.trust-store-password = client-truststore-password quarkus.cxf.client.helloSaml1.features = #messageCollector quarkus.cxf.client.helloSaml1.security.signature.username = alice -quarkus.cxf.client.helloSaml1.security.signature.password = password +quarkus.cxf.client.helloSaml1.security.signature.password = alice-keystore-password quarkus.cxf.client.helloSaml1.security.signature.crypto = #aliceCrypto quarkus.cxf.client.helloSaml1.security.saml-callback-handler = #saml1CallbackHandler quarkus.cxf.client.helloSaml2.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloSaml2 quarkus.cxf.client.helloSaml2.service-interface = io.quarkiverse.cxf.it.security.policy.Saml2PolicyHelloService quarkus.cxf.client.helloSaml2.trust-store = client-truststore.${keystore.type} -quarkus.cxf.client.helloSaml2.trust-store-password = password +quarkus.cxf.client.helloSaml2.trust-store-password = client-truststore-password quarkus.cxf.client.helloSaml2.features = #messageCollector quarkus.cxf.client.helloSaml2.security.signature.username = alice -quarkus.cxf.client.helloSaml2.security.signature.password = password +quarkus.cxf.client.helloSaml2.security.signature.password = alice-keystore-password quarkus.cxf.client.helloSaml2.security.signature.crypto = #aliceCrypto quarkus.cxf.client.helloSaml2.security.saml-callback-handler = #saml2CallbackHandler - - diff --git a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc index cd4fea5ce..91164023b 100644 --- a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc @@ -118,6 +118,28 @@ Larger values may give slight performance increases for large responses, at the *Environment variable*: `+++QUARKUS_CXF_OUTPUT_BUFFER_SIZE+++` +.<|icon:lock[title=Fixed at build time] [[quarkus-cxf_quarkus-cxf-http-conduit-factory]]`link:#quarkus-cxf_quarkus-cxf-http-conduit-factory[quarkus.cxf.http-conduit-factory]` +.<| `QuarkusCXFDefault`, `CXFDefault`, `HttpClientHTTPConduitFactory`, `URLConnectionHTTPConduitFactory` +.<| + +3+a|Select the `HTTPConduitFactory` implementation for all clients except the ones that override this setting via +`quarkus.cxf.client.myClient.http-conduit-factory`. + +- `QuarkusCXFDefault` (default): if `io.quarkiverse.cxf:quarkus-cxf-rt-transports-http-hc5` is present in class path, +then its `HTTPConduitFactory` implementation will be used; otherwise this value is equivalent with +`URLConnectionHTTPConduitFactory` (this may change, once issue +link:https://github.com/quarkiverse/quarkus-cxf/issues/992[++#++992] gets resolved in CXF) +- `CXFDefault`: the selection of `HTTPConduitFactory` implementation is left to CXF +- `HttpClientHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning +`org.apache.cxf.transport.http.HttpClientHTTPConduit`. This will use `java.net.http.HttpClient` as the underlying HTTP +client. +- `URLConnectionHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning +`org.apache.cxf.transport.http.URLConnectionHTTPConduit`. This will use `java.net.HttpURLConnection` as the underlying +HTTP client. + +*Environment variable*: `+++QUARKUS_CXF_HTTP_CONDUIT_FACTORY+++` + +*Since Quarkus CXF*: 2.3.0 + .<| [[quarkus-cxf_quarkus-cxf-decoupled-endpoint-base]]`link:#quarkus-cxf_quarkus-cxf-decoupled-endpoint-base[quarkus.cxf.decoupled-endpoint-base]` .<| `string` .<| @@ -1141,7 +1163,44 @@ underlying HTTP client. returning `org.apache.cxf.transport.http.URLConnectionHTTPConduit`. This will use `java.net.HttpURLConnection` as the underlying HTTP client. -*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__HTTP_CONDUIT_FACTORY+++` +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__HTTP_CONDUIT_FACTORY+++` + +*Since Quarkus CXF*: 2.3.0 + +.<| [[quarkus-cxf_quarkus-cxf-client-clients-key-store]]`link:#quarkus-cxf_quarkus-cxf-client-clients-key-store[quarkus.cxf.client."clients".key-store]` +.<| `string` +.<| + +3+a|The key store location for this client. The resource is first looked up in the classpath, then in the file system. + +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__KEY_STORE+++` + +*Since Quarkus CXF*: 3.9.0 + +.<| [[quarkus-cxf_quarkus-cxf-client-clients-key-store-password]]`link:#quarkus-cxf_quarkus-cxf-client-clients-key-store-password[quarkus.cxf.client."clients".key-store-password]` +.<| `string` +.<| + +3+a|The key store password + +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__KEY_STORE_PASSWORD+++` + +*Since Quarkus CXF*: 3.9.0 + +.<| [[quarkus-cxf_quarkus-cxf-client-clients-key-store-type]]`link:#quarkus-cxf_quarkus-cxf-client-clients-key-store-type[quarkus.cxf.client."clients".key-store-type]` +.<| `string` +.<| `JKS` + +3+a|The type of the key store. + +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__KEY_STORE_TYPE+++` + +*Since Quarkus CXF*: 3.9.0 + +.<| [[quarkus-cxf_quarkus-cxf-client-clients-key-password]]`link:#quarkus-cxf_quarkus-cxf-client-clients-key-password[quarkus.cxf.client."clients".key-password]` +.<| `string` +.<| + +3+a|The key password. + +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__KEY_PASSWORD+++` + +*Since Quarkus CXF*: 3.9.0 .<| [[quarkus-cxf_quarkus-cxf-client-clients-trust-store]]`link:#quarkus-cxf_quarkus-cxf-client-clients-trust-store[quarkus.cxf.client."clients".trust-store]` .<| `string` @@ -1149,15 +1208,17 @@ underlying HTTP client. 3+a|The trust store location for this client. The resource is first looked up in the classpath, then in the file system. -*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE+++` +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE+++` + +*Since Quarkus CXF*: 2.5.0 .<| [[quarkus-cxf_quarkus-cxf-client-clients-trust-store-password]]`link:#quarkus-cxf_quarkus-cxf-client-clients-trust-store-password[quarkus.cxf.client."clients".trust-store-password]` .<| `string` .<| -3+a|The trust store password +3+a|The trust store password. -*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE_PASSWORD+++` +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE_PASSWORD+++` + +*Since Quarkus CXF*: 2.5.0 .<| [[quarkus-cxf_quarkus-cxf-client-clients-trust-store-type]]`link:#quarkus-cxf_quarkus-cxf-client-clients-trust-store-type[quarkus.cxf.client."clients".trust-store-type]` .<| `string` @@ -1165,7 +1226,8 @@ underlying HTTP client. 3+a|The type of the trust store. -*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE_TYPE+++` +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__TRUST_STORE_TYPE+++` + +*Since Quarkus CXF*: 2.5.0 .<| [[quarkus-cxf_quarkus-cxf-client-clients-hostname-verifier]]`link:#quarkus-cxf_quarkus-cxf-client-clients-hostname-verifier[quarkus.cxf.client."clients".hostname-verifier]` .<| `string` @@ -1181,7 +1243,8 @@ not specified, then the creation of the `HostnameVerifier` is delegated to CXF, `org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader` as returned from `PublicSuffixMatcherLoader.getDefault()`. -*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__HOSTNAME_VERIFIER+++` +*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENTS__HOSTNAME_VERIFIER+++` + +*Since Quarkus CXF*: 2.5.0 .<| [[quarkus-cxf_quarkus-cxf-client-clients-schema-validation-enabled-for]]`link:#quarkus-cxf_quarkus-cxf-client-clients-schema-validation-enabled-for[quarkus.cxf.client."clients".schema-validation.enabled-for]` .<| `in`, `request`, `out`, `response`, `both`, `none` diff --git a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfBuildTimeConfig.java b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfBuildTimeConfig.java index 7946ecd0a..a154c63cb 100644 --- a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfBuildTimeConfig.java +++ b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfBuildTimeConfig.java @@ -4,7 +4,6 @@ import java.util.Map; import java.util.Optional; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkiverse.cxf.deployment.codegen.Wsdl2JavaParam; import io.quarkiverse.cxf.deployment.codegen.Wsdl2JavaParam.Wsdl2JavaParamCollection; import io.quarkiverse.cxf.deployment.codegen.Wsdl2JavaParam.Wsdl2JavaParamTransformer; @@ -45,26 +44,6 @@ public interface CxfBuildTimeConfig { @WithName("java2ws") public Java2WsConfig java2ws(); - /** - * Select the `HTTPConduitFactory` implementation for all clients except the ones that override this setting via - * `quarkus.cxf.client.myClient.http-conduit-factory`. - * - * - `QuarkusCXFDefault` (default): if `io.quarkiverse.cxf:quarkus-cxf-rt-transports-http-hc5` is present in class path, - * then its `HTTPConduitFactory` implementation will be used; otherwise this value is equivalent with - * `URLConnectionHTTPConduitFactory` (this may change, once issue - * link:https://github.com/quarkiverse/quarkus-cxf/issues/992[++#++992] gets resolved in CXF) - * - `CXFDefault`: the selection of `HTTPConduitFactory` implementation is left to CXF - * - `HttpClientHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning - * `org.apache.cxf.transport.http.HttpClientHTTPConduit`. This will use `java.net.http.HttpClient` as the underlying HTTP - * client. - * - `URLConnectionHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning - * `org.apache.cxf.transport.http.URLConnectionHTTPConduit`. This will use `java.net.HttpURLConnection` as the underlying - * HTTP client. - * - * @asciidoclet - */ - public Optional httpConduitFactory(); - @ConfigGroup public interface CodeGenConfig { diff --git a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java index 502e8267e..e687a06e0 100644 --- a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java +++ b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java @@ -482,7 +482,7 @@ private void produceUnremovableBean( @Record(ExecutionTime.STATIC_INIT) void customizers( CXFRecorder recorder, - CxfBuildTimeConfig config, + CxfFixedConfig config, BuildProducer customizers) { final HTTPConduitImpl factory = HTTPConduitImpl.fromOptional( config.httpConduitFactory(), diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java index 106c12b3e..e3055e8e9 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java @@ -172,6 +172,26 @@ public class CXFClientInfo { */ private final String proxyPassword; + /** + * The key store location. Can point to either a classpath resource or a file. + */ + private final String keyStore; + + /** + * The key store password. + */ + private final String keyStorePassword; + + /** + * The type of the trust store. Defaults to "JKS". + */ + private final String keyStoreType; + + /** + * The key password. + */ + private final String keyPassword; + /** * The trust store location. Can point to either a classpath resource or a file. */ @@ -239,6 +259,10 @@ public CXFClientInfo(CXFClientData other, CxfConfig cxfConfig, CxfClientConfig c this.proxyUsername = config.proxyUsername().orElse(null); this.proxyPassword = config.proxyPassword().orElse(null); + this.keyStore = config.keyStore().orElse(null); + this.keyStorePassword = config.keyStorePassword().orElse(null); + this.keyStoreType = Objects.requireNonNull(config.keyStoreType(), "keyStoreType cannot be null"); + this.keyPassword = config.keyPassword().orElse(null); this.trustStore = config.trustStore().orElse(null); this.trustStorePassword = config.trustStorePassword().orElse(null); this.trustStoreType = Objects.requireNonNull(config.trustStoreType(), "trustStoreType cannot be null"); @@ -461,6 +485,22 @@ public HTTPConduitImpl getHttpConduitImpl() { return httpConduitImpl; } + public String getKeyStore() { + return keyStore; + } + + public String getKeyStorePassword() { + return keyStorePassword; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public String getKeyPassword() { + return keyPassword; + } + public String getTrustStore() { return trustStore; } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java index d85a8e4f7..e149fcec8 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java @@ -389,20 +389,56 @@ public interface CxfClientConfig { * underlying HTTP client. * * @asciidoclet + * @since 2.3.0 */ public Optional httpConduitFactory(); + /** + * The key store location for this client. The resource is first looked up in the classpath, then in the file system. + * + * @asciidoclet + * @since 3.9.0 + */ + public Optional keyStore(); + + /** + * The key store password + * + * @asciidoclet + * @since 3.9.0 + */ + public Optional keyStorePassword(); + + /** + * The type of the key store. + * + * @asciidoclet + * @since 3.9.0 + */ + @WithDefault("JKS") + public String keyStoreType(); + + /** + * The key password. + * + * @asciidoclet + * @since 3.9.0 + */ + public Optional keyPassword(); + /** * The trust store location for this client. The resource is first looked up in the classpath, then in the file system. * * @asciidoclet + * @since 2.5.0 */ public Optional trustStore(); /** - * The trust store password + * The trust store password. * * @asciidoclet + * @since 2.5.0 */ public Optional trustStorePassword(); @@ -410,6 +446,7 @@ public interface CxfClientConfig { * The type of the trust store. * * @asciidoclet + * @since 2.5.0 */ @WithDefault("JKS") public String trustStoreType(); @@ -426,6 +463,7 @@ public interface CxfClientConfig { * `PublicSuffixMatcherLoader.getDefault()`. * * @asciidoclet + * @since 2.5.0 */ public Optional hostnameVerifier(); diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java index 61c590ccd..985459fce 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java @@ -4,20 +4,12 @@ import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.TrustManagerFactory; import javax.xml.namespace.QName; import jakarta.annotation.PostConstruct; @@ -27,22 +19,18 @@ import jakarta.inject.Inject; import jakarta.xml.ws.BindingProvider; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType; -import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.configuration.security.AuthorizationPolicy; -import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.message.Message; -import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transport.http.HTTPConduitFactory; -import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import org.apache.cxf.ws.addressing.WSAContextUtils; import org.jboss.logging.Logger; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; -import io.quarkiverse.cxf.CxfClientConfig.WellKnownHostnameVerifier; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.logging.LoggingFactoryCustomizer; @@ -194,27 +182,6 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { CXFRuntimeUtils.addBeans(cxfClientInfo.getInFaultInterceptors(), "inFaultInterceptor", clientString, sei, factory.getInFaultInterceptors()); - final HTTPConduitImpl httpConduitImpl = cxfClientInfo.getHttpConduitImpl(); - if (httpConduitImpl != null) { - switch (httpConduitImpl) { - case CXFDefault: - // nothing to do - break; - case QuarkusCXFDefault: - case URLConnectionHTTPConduitFactory: { - props.put(HTTPConduitFactory.class.getName(), new URLConnectionHTTPConduitFactory()); - break; - } - case HttpClientHTTPConduitFactory: { - props.put(HTTPConduitFactory.class.getName(), new HttpClientHTTPConduitFactory()); - break; - } - default: - throw new IllegalStateException("Unexpected " + HTTPConduitImpl.class.getSimpleName() + " value: " - + httpConduitImpl); - } - } - { final String value = cxfClientInfo.getDecoupledEndpointBase(); if (value != null) { @@ -225,132 +192,28 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { loggingFactoryCustomizer.customize(cxfClientInfo, factory); customizers.forEach(customizer -> customizer.customize(cxfClientInfo, factory)); - LOGGER.debug("cxf client loaded for " + sei); - Object result = factory.create(); - final Client client = ClientProxy.getClient(result); - final HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); - final HTTPClientPolicy policy = httpConduit.getClient(); - policy.setConnectionTimeout(cxfClientInfo.getConnectionTimeout()); - policy.setReceiveTimeout(cxfClientInfo.getReceiveTimeout()); - policy.setConnectionRequestTimeout(cxfClientInfo.getConnectionRequestTimeout()); - policy.setAutoRedirect(cxfClientInfo.isAutoRedirect()); - policy.setMaxRetransmits(cxfClientInfo.getMaxRetransmits()); - policy.setAllowChunking(cxfClientInfo.isAllowChunking()); - policy.setChunkingThreshold(cxfClientInfo.getChunkingThreshold()); - policy.setChunkLength(cxfClientInfo.getChunkLength()); - { - final String value = cxfClientInfo.getAccept(); - if (value != null) { - policy.setAccept(value); - } - } - { - final String value = cxfClientInfo.getAcceptLanguage(); - if (value != null) { - policy.setAcceptLanguage(value); - } - } - { - final String value = cxfClientInfo.getAcceptEncoding(); - if (value != null) { - policy.setAcceptEncoding(value); - } - } - { - final String value = cxfClientInfo.getContentType(); - if (value != null) { - policy.setContentType(value); - } - } - { - final String value = cxfClientInfo.getHost(); - if (value != null) { - policy.setHost(value); - } - } - policy.setConnection(cxfClientInfo.getConnection()); - { - final String value = cxfClientInfo.getCacheControl(); - if (value != null) { - policy.setCacheControl(value); - } - } - policy.setVersion(cxfClientInfo.getVersion()); - { - final String value = cxfClientInfo.getBrowserType(); - if (value != null) { - policy.setBrowserType(value); - } - } - { - final String value = cxfClientInfo.getDecoupledEndpoint(); - if (value != null) { - policy.setDecoupledEndpoint(value); - } - } - { - final String value = cxfClientInfo.getProxyServer(); - if (value != null) { - policy.setProxyServer(value); - } - } - { - final Integer value = cxfClientInfo.getProxyServerPort(); - if (value != null) { - policy.setProxyServerPort(value); - } - } - { - final String value = cxfClientInfo.getNonProxyHosts(); - if (value != null) { - policy.setNonProxyHosts(value); - } - } - policy.setProxyServerType(cxfClientInfo.getProxyServerType()); - - final String proxyUsername = cxfClientInfo.getProxyUsername(); - if (proxyUsername != null) { - final String proxyPassword = cxfClientInfo.getProxyPassword(); - final ProxyAuthorizationPolicy proxyAuth = new ProxyAuthorizationPolicy(); - proxyAuth.setUserName(proxyUsername); - proxyAuth.setPassword(proxyPassword); - httpConduit.setProxyAuthorization(proxyAuth); + final QuarkusHTTPConduitFactory conduitFactory = new QuarkusHTTPConduitFactory(fixedConfig, cxfClientInfo, + CXFRecorder.isHc5Present()); + props.put(HTTPConduitFactory.class.getName(), conduitFactory); + final Bus bus = BusFactory.getDefaultBus(); + final HTTPConduitFactory origConduitFactory = bus.getExtension(HTTPConduitFactory.class); + Object result; + try { + /* + * Workaround for https://github.com/quarkiverse/quarkus-cxf/issues/1264 + * We set the client specific HTTPConduitFactory on the bus temporarily, + * so that it is honored for getting the WSDL. + * We assume that no other tread is accessing the HTTPConduitFactory.class extension in parallel + */ + bus.setExtension(conduitFactory, HTTPConduitFactory.class); + + LOGGER.debug("cxf client loaded for " + sei); + result = factory.create(); + } finally { + bus.setExtension(origConduitFactory, HTTPConduitFactory.class); } - final String trustStorePath = cxfClientInfo.getTrustStore(); - if (trustStorePath != null) { - TLSClientParameters tlsCP = new TLSClientParameters(); - final KeyStore trustStore; - final TrustManagerFactory tmf; - try (InputStream is = Thread.currentThread().getContextClassLoader() - .getResourceAsStream(trustStorePath)) { - trustStore = KeyStore.getInstance(cxfClientInfo.getTrustStoreType()); - final String pwd = cxfClientInfo.getTrustStorePassword(); - trustStore.load(is, pwd == null ? null : pwd.toCharArray()); - tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(trustStore); - } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) { - throw new RuntimeException("Could not load client-truststore.jks from class path", e); - } - tlsCP.setTrustManagers(tmf.getTrustManagers()); - - final String hostnameVerifierName = cxfClientInfo.getHostnameVerifier(); - if (hostnameVerifierName != null) { - final Optional wellKnownHostNameVerifierName = WellKnownHostnameVerifier - .of(hostnameVerifierName); - if (wellKnownHostNameVerifierName.isPresent()) { - wellKnownHostNameVerifierName.get().configure(tlsCP); - } else { - final HostnameVerifier hostnameVerifier = CXFRuntimeUtils.getInstance(hostnameVerifierName, true); - if (hostnameVerifier == null) { - throw new RuntimeException("Could not find or instantiate " + hostnameVerifierName); - } - tlsCP.setHostnameVerifier(hostnameVerifier); - } - } - - httpConduit.setTlsClientParameters(tlsCP); - } + final Client client = ClientProxy.getClient(result); { final SchemaValidationType value = cxfClientInfo.getSchemaValidationEnabledFor(); if (value != null) { diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java index 1ccfefaf1..7b3b719e8 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.Optional; +import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -53,6 +54,27 @@ public interface CxfFixedConfig { @WithDefault("8191") int outputBufferSize(); + /** + * Select the `HTTPConduitFactory` implementation for all clients except the ones that override this setting via + * `quarkus.cxf.client.myClient.http-conduit-factory`. + * + * - `QuarkusCXFDefault` (default): if `io.quarkiverse.cxf:quarkus-cxf-rt-transports-http-hc5` is present in class path, + * then its `HTTPConduitFactory` implementation will be used; otherwise this value is equivalent with + * `URLConnectionHTTPConduitFactory` (this may change, once issue + * link:https://github.com/quarkiverse/quarkus-cxf/issues/992[++#++992] gets resolved in CXF) + * - `CXFDefault`: the selection of `HTTPConduitFactory` implementation is left to CXF + * - `HttpClientHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning + * `org.apache.cxf.transport.http.HttpClientHTTPConduit`. This will use `java.net.http.HttpClient` as the underlying HTTP + * client. + * - `URLConnectionHTTPConduitFactory`: the `HTTPConduitFactory` will be set to an implementation always returning + * `org.apache.cxf.transport.http.URLConnectionHTTPConduit`. This will use `java.net.HttpURLConnection` as the underlying + * HTTP client. + * + * @asciidoclet + * @since 2.3.0 + */ + public Optional httpConduitFactory(); + /** * The build time part of the client configuration. * diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java new file mode 100644 index 000000000..825e4b174 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java @@ -0,0 +1,246 @@ +package io.quarkiverse.cxf; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.Optional; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.cxf.Bus; +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transport.http.HTTPConduitFactory; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.cxf.transport.http.HttpClientHTTPConduit; +import org.apache.cxf.transport.http.URLConnectionHTTPConduit; +import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; +import org.apache.cxf.ws.addressing.EndpointReferenceType; + +import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.CxfClientConfig.WellKnownHostnameVerifier; + +/** + * A HTTPConduitFactory with some client specific configuration, such as timeouts and SSL. + * + * @since 3.9.0 + */ +public class QuarkusHTTPConduitFactory implements HTTPConduitFactory { + + private final CxfFixedConfig cxFixedConfig; + private final CXFClientInfo cxfClientInfo; + private final boolean hc5Present; + + public QuarkusHTTPConduitFactory(CxfFixedConfig cxFixedConfig, CXFClientInfo cxfClientInfo, boolean hc5Present) { + super(); + this.cxFixedConfig = cxFixedConfig; + this.cxfClientInfo = cxfClientInfo; + this.hc5Present = hc5Present; + } + + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + throws IOException { + HTTPConduitImpl httpConduitImpl = cxfClientInfo.getHttpConduitImpl(); + if (httpConduitImpl == null) { + httpConduitImpl = cxFixedConfig.httpConduitFactory().orElse(null); + } + if (httpConduitImpl == null && hc5Present) { + return configure( + b.getExtension(HTTPConduitFactory.class).createConduit(f, b, localInfo, target), + cxfClientInfo); + } + + if (httpConduitImpl == null) { + httpConduitImpl = HTTPConduitImpl.QuarkusCXFDefault; + } + + final HTTPConduit result; + switch (httpConduitImpl) { + case CXFDefault: { + /* + * Mimic what is done in org.apache.cxf.transport.http.HTTPTransportFactory.getConduit(EndpointInfo, + * EndpointReferenceType, Bus) + */ + if (Boolean.getBoolean("org.apache.cxf.transport.http.forceURLConnection")) { + result = new URLConnectionHTTPConduit(b, localInfo, target); + } else { + result = new HttpClientHTTPConduit(b, localInfo, target); + } + break; + } + case QuarkusCXFDefault: + case URLConnectionHTTPConduitFactory: { + result = new URLConnectionHTTPConduit(b, localInfo, target); + break; + } + case HttpClientHTTPConduitFactory: { + result = new HttpClientHTTPConduit(b, localInfo, target); + break; + } + default: + throw new IllegalStateException("Unexpected " + HTTPConduitImpl.class.getSimpleName() + " value: " + + httpConduitImpl); + } + return configure(result, cxfClientInfo); + } + + private HTTPConduit configure(HTTPConduit httpConduit, CXFClientInfo cxfClientInfo) { + final String hostnameVerifierName = cxfClientInfo.getHostnameVerifier(); + final String keyStorePath = cxfClientInfo.getKeyStore(); + final String trustStorePath = cxfClientInfo.getTrustStore(); + if (hostnameVerifierName != null || keyStorePath != null || trustStorePath != null) { + TLSClientParameters tlsCP = new TLSClientParameters(); + + if (hostnameVerifierName != null) { + final Optional wellKnownHostNameVerifierName = WellKnownHostnameVerifier + .of(hostnameVerifierName); + if (wellKnownHostNameVerifierName.isPresent()) { + wellKnownHostNameVerifierName.get().configure(tlsCP); + } else { + final HostnameVerifier hostnameVerifier = CXFRuntimeUtils.getInstance(hostnameVerifierName, true); + if (hostnameVerifier == null) { + throw new RuntimeException("Could not find or instantiate " + hostnameVerifierName); + } + tlsCP.setHostnameVerifier(hostnameVerifier); + } + } + + if (keyStorePath != null) { + final KeyStore keyStore; + final KeyManagerFactory kmf; + try (InputStream is = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(keyStorePath)) { + keyStore = KeyStore.getInstance(cxfClientInfo.getKeyStoreType()); + final String pwd = cxfClientInfo.getKeyStorePassword(); + keyStore.load(is, pwd == null ? null : pwd.toCharArray()); + kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + final String keyPassword = cxfClientInfo.getKeyPassword(); + kmf.init(keyStore, (keyPassword != null) ? keyPassword.toCharArray() : null); + } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException + | UnrecoverableKeyException e) { + throw new RuntimeException("Could not load " + keyStorePath + " from class path", e); + } + tlsCP.setKeyManagers(kmf.getKeyManagers()); + } + + if (trustStorePath != null) { + final KeyStore trustStore; + final TrustManagerFactory tmf; + try (InputStream is = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(trustStorePath)) { + trustStore = KeyStore.getInstance(cxfClientInfo.getTrustStoreType()); + final String pwd = cxfClientInfo.getTrustStorePassword(); + trustStore.load(is, pwd == null ? null : pwd.toCharArray()); + tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) { + throw new RuntimeException("Could not load " + trustStorePath + " from class path", e); + } + tlsCP.setTrustManagers(tmf.getTrustManagers()); + } + + httpConduit.setTlsClientParameters(tlsCP); + } + + final HTTPClientPolicy policy = new HTTPClientPolicy(); + httpConduit.setClient(policy); + policy.setConnectionTimeout(cxfClientInfo.getConnectionTimeout()); + policy.setReceiveTimeout(cxfClientInfo.getReceiveTimeout()); + policy.setConnectionRequestTimeout(cxfClientInfo.getConnectionRequestTimeout()); + policy.setAutoRedirect(cxfClientInfo.isAutoRedirect()); + policy.setMaxRetransmits(cxfClientInfo.getMaxRetransmits()); + policy.setAllowChunking(cxfClientInfo.isAllowChunking()); + policy.setChunkingThreshold(cxfClientInfo.getChunkingThreshold()); + policy.setChunkLength(cxfClientInfo.getChunkLength()); + { + final String value = cxfClientInfo.getAccept(); + if (value != null) { + policy.setAccept(value); + } + } + { + final String value = cxfClientInfo.getAcceptLanguage(); + if (value != null) { + policy.setAcceptLanguage(value); + } + } + { + final String value = cxfClientInfo.getAcceptEncoding(); + if (value != null) { + policy.setAcceptEncoding(value); + } + } + { + final String value = cxfClientInfo.getContentType(); + if (value != null) { + policy.setContentType(value); + } + } + { + final String value = cxfClientInfo.getHost(); + if (value != null) { + policy.setHost(value); + } + } + policy.setConnection(cxfClientInfo.getConnection()); + { + final String value = cxfClientInfo.getCacheControl(); + if (value != null) { + policy.setCacheControl(value); + } + } + policy.setVersion(cxfClientInfo.getVersion()); + { + final String value = cxfClientInfo.getBrowserType(); + if (value != null) { + policy.setBrowserType(value); + } + } + { + final String value = cxfClientInfo.getDecoupledEndpoint(); + if (value != null) { + policy.setDecoupledEndpoint(value); + } + } + { + final String value = cxfClientInfo.getProxyServer(); + if (value != null) { + policy.setProxyServer(value); + } + } + { + final Integer value = cxfClientInfo.getProxyServerPort(); + if (value != null) { + policy.setProxyServerPort(value); + } + } + { + final String value = cxfClientInfo.getNonProxyHosts(); + if (value != null) { + policy.setNonProxyHosts(value); + } + } + policy.setProxyServerType(cxfClientInfo.getProxyServerType()); + + final String proxyUsername = cxfClientInfo.getProxyUsername(); + if (proxyUsername != null) { + final String proxyPassword = cxfClientInfo.getProxyPassword(); + final ProxyAuthorizationPolicy proxyAuth = new ProxyAuthorizationPolicy(); + proxyAuth.setUserName(proxyUsername); + proxyAuth.setPassword(proxyPassword); + httpConduit.setProxyAuthorization(proxyAuth); + } + + return httpConduit; + } + +} diff --git a/integration-tests/mtls/generate-certs.sh b/integration-tests/mtls/generate-certs.sh new file mode 100755 index 000000000..9bd77e47d --- /dev/null +++ b/integration-tests/mtls/generate-certs.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +set -e +set -x + +invocationDir="$(pwd)" +workDir="target/openssl-work" +destinationDir="target/classes" +keySize=2048 +days=10000 +extFile="$(pwd)/v3.ext" +encryptionAlgo="aes-256-cbc" + +if [[ -n "${JAVA_HOME}" ]] ; then + keytool="$JAVA_HOME/bin/keytool" +elif ! [[ -x "$(command -v keytool)" ]] ; then + echo 'Error: Either add keytool to PATH or set JAVA_HOME' >&2 + exit 1 +else + keytool="keytool" +fi + +if ! [[ -x "$(command -v openssl)" ]] ; then + echo 'Error: openssl is not installed.' >&2 + exit 1 +fi + +mkdir -p "$workDir" +mkdir -p "$destinationDir" + +# Certificate authority +openssl genrsa -out "$workDir/cxfca.key" $keySize +openssl req -x509 -new -subj '/O=apache.org/OU=eng (NOT FOR PRODUCTION)/CN=cxfca' -key "$workDir/cxfca.key" -nodes -out "$workDir/cxfca.pem" -days $days -extensions v3_req +openssl req -new -subj '/O=apache.org/OU=eng (NOT FOR PRODUCTION)/CN=cxfca' -x509 -key "$workDir/cxfca.key" -days $days -out "$workDir/cxfca.crt" + +for actor in localhost client; do + # Generate keys + openssl genrsa -out "$workDir/$actor.key" $keySize + + # Generate certificates + openssl req -new -subj "/O=apache.org/OU=eng (NOT FOR PRODUCTION)/CN=$actor" -key "$workDir/$actor.key" -out "$workDir/$actor.csr" + openssl x509 -req -in "$workDir/$actor.csr" -extfile "$extFile" -CA "$workDir/cxfca.pem" -CAkey "$workDir/cxfca.key" -CAcreateserial -days $days -out "$workDir/$actor.crt" + + # Export keystores + openssl pkcs12 -export -in "$workDir/$actor.crt" -inkey "$workDir/$actor.key" -certfile "$workDir/cxfca.crt" -name "$actor" -out "$destinationDir/$actor-keystore.pkcs12" -passout pass:"${actor}-keystore-password" -keypbe "$encryptionAlgo" -certpbe "$encryptionAlgo" +done + + +# Truststores +"$keytool" -import -file "$workDir/localhost.crt" -alias localhost -noprompt -keystore "$destinationDir/client-truststore.pkcs12" -storepass "client-truststore-password" +"$keytool" -import -file "$workDir/cxfca.crt" -alias cxfca -noprompt -keystore "$destinationDir/client-truststore.pkcs12" -storepass "client-truststore-password" + +"$keytool" -import -file "$workDir/client.crt" -alias client -noprompt -keystore "$destinationDir/localhost-truststore.pkcs12" -storepass "localhost-truststore-password" +"$keytool" -import -file "$workDir/cxfca.crt" -alias cxfca -noprompt -keystore "$destinationDir/localhost-truststore.pkcs12" -storepass "localhost-truststore-password" diff --git a/integration-tests/mtls/pom.xml b/integration-tests/mtls/pom.xml new file mode 100644 index 000000000..d6cbe7fbc --- /dev/null +++ b/integration-tests/mtls/pom.xml @@ -0,0 +1,450 @@ + + + 4.0.0 + + io.quarkiverse.cxf + quarkus-cxf-integration-tests + 3.8.1-SNAPSHOT + ../pom.xml + + + quarkus-cxf-integration-test-mtls + + Quarkus CXF - Integration Test - mTLS + + + + io.quarkiverse.cxf + quarkus-cxf + + + io.quarkus + quarkus-resteasy + + + + io.rest-assured + rest-assured + test + + + + + + + src/main/resources + + application.properties + + true + + + src/main/resources + + application.properties + + false + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + + pkcs12 + + + !jks + + + + pkcs12 + + + + + org.codehaus.mojo + exec-maven-plugin + + + generate-certs.sh + generate-sources + + exec + + + ${basedir}/generate-certs.sh + + + + + + + + + jks + + + jks + + + + jks + + + + + org.codehaus.mojo + keytool-maven-plugin + + 3650 + RSA + ${keytool.skip} + + + + + generate-cxfca-keypair + generate-sources + + clean + generateKeyPair + + + cxfca + CN=cxfca, OU=eng, O=apache.org + + bc:c=ca:true,pathlen:2147483647 + IssuerAlternativeName=DNS:NOT-FOR-PRODUCTION-USE + + cxfca-password + cxfca-password + ${project.build.outputDirectory}/cxfca.jks + + + + export-cxfca-certificate + generate-sources + + exportCertificate + + + cxfca + ${project.build.outputDirectory}/cxfca.jks + true + cxfca-password + ${project.build.outputDirectory}/cxfca.pem + + + + + generate-server-keypair + generate-sources + + clean + generateKeyPair + + + localhost + CN=localhost, OU=eng, O=apache.org + + bc:c=ca:true,pathlen:2147483647 + IssuerAlternativeName=DNS:NOT-FOR-PRODUCTION-USE + + localhost-keystore-password + localhost-keystore-password + ${project.build.outputDirectory}/localhost-keystore.jks + + + + generate-server-certificate-request + generate-sources + + generateCertificateRequest + + + localhost + localhost-keystore-password + ${project.build.outputDirectory}/localhost-keystore.jks + ${project.build.outputDirectory}/server.csr + + + + sign-server-certificate + generate-sources + + generateCertificate + + + cxfca + cxfca-password + ${project.build.outputDirectory}/cxfca.jks + true + ${project.build.outputDirectory}/server.csr + ${project.build.outputDirectory}/server.pem + + + + import-cxfca-certificate-to-server-keystore + generate-sources + + importCertificate + + + cxfca + true + true + localhost-keystore-password + ${project.build.outputDirectory}/localhost-keystore.jks + ${project.build.outputDirectory}/cxfca.pem + + + + import-signed-server-certificate-to-server-keystore + generate-sources + + importCertificate + + + localhost + true + true + localhost-keystore-password + ${project.build.outputDirectory}/localhost-keystore.jks + ${project.build.outputDirectory}/server.pem + + + + + + generate-client-keypair + generate-sources + + clean + generateKeyPair + + + client + CN=client, OU=eng, O=apache.org + + IssuerAlternativeName=DNS:NOT-FOR-PRODUCTION-USE + SubjectAlternativeName=DNS:localhost,IP:127.0.0.1 + + client-keystore-password + client-keystore-password + ${project.build.outputDirectory}/client-keystore.jks + + + + generate-client-certificate-request + generate-sources + + generateCertificateRequest + + + client + client-keystore-password + ${project.build.outputDirectory}/client-keystore.jks + ${project.build.outputDirectory}/client.csr + + + + generate-client-certificate + generate-sources + + generateCertificate + + + cxfca + cxfca-password + ${project.build.outputDirectory}/cxfca.jks + true + ${project.build.outputDirectory}/client.csr + ${project.build.outputDirectory}/client.pem + + + + import-cxfca-certificate-to-client-keystore + generate-sources + + importCertificate + + + cxfca + true + true + client-keystore-password + ${project.build.outputDirectory}/client-keystore.jks + ${project.build.outputDirectory}/cxfca.pem + + + + import-client-certificate + generate-sources + + importCertificate + + + client + true + true + client-keystore-password + ${project.build.outputDirectory}/client-keystore.jks + ${project.build.outputDirectory}/client.pem + + + + + + prepare-localhost-truststore-jks + generate-sources + + clean + importCertificate + + + cxfca + true + true + localhost-truststore-password + ${project.build.outputDirectory}/localhost-truststore.jks + ${project.build.outputDirectory}/cxfca.pem + + + + import-client-certificate-to-localhost-truststore + generate-sources + + importCertificate + + + client + true + true + localhost-truststore-password + ${project.build.outputDirectory}/localhost-truststore.jks + ${project.build.outputDirectory}/client.pem + + + + + + + prepare-client-truststore-jks + generate-sources + + clean + importCertificate + + + cxfca + true + true + client-truststore-password + ${project.build.outputDirectory}/client-truststore.jks + ${project.build.outputDirectory}/cxfca.pem + + + + import-localhost-certificate-to-client-truststore + generate-sources + + importCertificate + + + localhost + true + true + client-truststore-password + ${project.build.outputDirectory}/client-truststore.jks + ${project.build.outputDirectory}/server.pem + + + + + + + + + native + + false + + + native + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + virtualDependencies + + + !noVirtualDependencies + + + + + + io.quarkiverse.cxf + quarkus-cxf-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkiverse.cxf + quarkus-cxf-rt-ws-security-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + \ No newline at end of file diff --git a/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/HelloService.java b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/HelloService.java new file mode 100644 index 000000000..8c326bc4a --- /dev/null +++ b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/HelloService.java @@ -0,0 +1,15 @@ +package io.quarkiverse.cxf.it.auth.mtls; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +/** + * The simplest Hello service. + */ +@WebService(serviceName = "HelloService") +public interface HelloService { + + @WebMethod + String hello(String text); + +} diff --git a/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/MTlsHelloServiceImpl.java b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/MTlsHelloServiceImpl.java new file mode 100644 index 000000000..7de625a75 --- /dev/null +++ b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/MTlsHelloServiceImpl.java @@ -0,0 +1,11 @@ +package io.quarkiverse.cxf.it.auth.mtls; + +import jakarta.jws.WebService; + +@WebService +public class MTlsHelloServiceImpl implements HelloService { + @Override + public String hello(String person) { + return "Hello " + person + " authenticated by mTLS!"; + } +} diff --git a/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/SecurityPolicyResource.java b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/SecurityPolicyResource.java new file mode 100644 index 000000000..f8d7e2256 --- /dev/null +++ b/integration-tests/mtls/src/main/java/io/quarkiverse/cxf/it/auth/mtls/SecurityPolicyResource.java @@ -0,0 +1,48 @@ +package io.quarkiverse.cxf.it.auth.mtls; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import io.quarkiverse.cxf.annotation.CXFClient; + +@Path("/cxf/mtls-rest") +public class SecurityPolicyResource { + + @CXFClient("mTls") + HelloService mTls; + + @CXFClient("noKeystore") + HelloService noKeystore; + + @POST + @Path("/{client}") + @Produces(MediaType.TEXT_PLAIN) + public Response hello(@PathParam("client") String client, String body) { + final HelloService service; + switch (client) { + case "mTls": + service = mTls; + break; + case "noKeystore": + service = noKeystore; + break; + default: + throw new IllegalStateException("Unexpected client " + client); + } + try { + return Response.ok(service.hello(body)).build(); + } catch (Exception e) { + final StringWriter w = new StringWriter(); + final PrintWriter pw = new PrintWriter(w); + e.printStackTrace(pw); + return Response.status(500).entity(w.toString()).build(); + } + } +} diff --git a/integration-tests/mtls/src/main/resources/application.properties b/integration-tests/mtls/src/main/resources/application.properties new file mode 100644 index 000000000..b0c5a53b9 --- /dev/null +++ b/integration-tests/mtls/src/main/resources/application.properties @@ -0,0 +1,34 @@ +quarkus.http.ssl.certificate.key-store-file = localhost-keystore.${keystore.type} +quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password +quarkus.http.ssl.certificate.key-store-key-alias = localhost +quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password +quarkus.http.ssl.certificate.trust-store-file = localhost-truststore.${keystore.type} +quarkus.http.ssl.certificate.trust-store-password = localhost-truststore-password +quarkus.http.ssl.client-auth = required +quarkus.http.auth.permission.default.paths = /services/* +quarkus.http.auth.permission.default.policy = authenticated + +quarkus.native.resources.includes = *.pkcs12 + +keystore.type = ${keystore.type} + +# Mutual TLS +quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl + +# Client with a properly set keystore +quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/mTls +quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService +quarkus.cxf.client.mTls.key-store = client-keystore.${keystore.type} +quarkus.cxf.client.mTls.key-store-type = ${keystore.type} +quarkus.cxf.client.mTls.key-store-password = client-keystore-password +quarkus.cxf.client.mTls.key-password = client-keystore-password +quarkus.cxf.client.mTls.trust-store = client-truststore.${keystore.type} +quarkus.cxf.client.mTls.trust-store-type = ${keystore.type} +quarkus.cxf.client.mTls.trust-store-password = client-truststore-password + +# Client without keystore +quarkus.cxf.client.noKeystore.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/mTls +quarkus.cxf.client.noKeystore.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService +quarkus.cxf.client.noKeystore.trust-store = client-truststore.${keystore.type} +quarkus.cxf.client.noKeystore.trust-store-type = ${keystore.type} +quarkus.cxf.client.noKeystore.trust-store-password = client-truststore-password diff --git a/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsIT.java b/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsIT.java new file mode 100644 index 000000000..fe769e52d --- /dev/null +++ b/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsIT.java @@ -0,0 +1,12 @@ +package io.quarkiverse.cxf.it.auth.mtls; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MutualTlsIT extends MutualTlsTest { + + @Override + protected String noKeystoreMessage() { + return "IOException: Error writing to server"; + } +} diff --git a/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsTest.java b/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsTest.java new file mode 100644 index 000000000..aff8df437 --- /dev/null +++ b/integration-tests/mtls/src/test/java/io/quarkiverse/cxf/it/auth/mtls/MutualTlsTest.java @@ -0,0 +1,54 @@ +package io.quarkiverse.cxf.it.auth.mtls; + +import static org.hamcrest.Matchers.is; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.config.RestAssuredConfig; +import io.restassured.config.SSLConfig; + +@QuarkusTest +public class MutualTlsTest { + @Test + void mTls() { + RestAssured.given() + .config(restAssuredConfig()) + .body("Sam") + .post("https://localhost:8444/cxf/mtls-rest/mTls") + .then() + .statusCode(200) + .body(is("Hello Sam authenticated by mTLS!")); + + } + + @Test + void noKeystore() { + RestAssured.given() + .config(restAssuredConfig()) + .body("Sam") + .post("https://localhost:8444/cxf/mtls-rest/noKeystore") + .then() + .statusCode(500) + .body(Matchers.containsString(noKeystoreMessage())); + + } + + protected String noKeystoreMessage() { + return "SSLHandshakeException: Received fatal alert: bad_certificate"; + } + + public static RestAssuredConfig restAssuredConfig() { + return RestAssured.config().sslConfig(new SSLConfig().with() + .trustStore( + "client-truststore." + ConfigProvider.getConfig().getValue("keystore.type", String.class), + "client-truststore-password") + .keyStore( + "client-keystore." + ConfigProvider.getConfig().getValue("keystore.type", String.class), + "client-keystore-password")); + } + +} diff --git a/integration-tests/mtls/v3.ext b/integration-tests/mtls/v3.ext new file mode 100644 index 000000000..632d6d3a6 --- /dev/null +++ b/integration-tests/mtls/v3.ext @@ -0,0 +1,3 @@ +authorityKeyIdentifier = keyid, issuer +basicConstraints = CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment \ No newline at end of file