diff --git a/web-modules/jersey-2/pom.xml b/web-modules/jersey-2/pom.xml
index 1cac59a9312d..8d24f67c06ff 100644
--- a/web-modules/jersey-2/pom.xml
+++ b/web-modules/jersey-2/pom.xml
@@ -115,6 +115,12 @@
jakarta.xml.bind-api
${jaxb-runtime.version}
+
+ org.wiremock
+ wiremock
+ ${wiremock.version}
+ test
+
@@ -144,6 +150,7 @@
3.0.1
5.8.2
4.5.1
+ 3.13.0
diff --git a/web-modules/jersey-2/src/test/java/com/baeldung/jersey/https/JerseyHttpsClientManualTest.java b/web-modules/jersey-2/src/test/java/com/baeldung/jersey/https/JerseyHttpsClientManualTest.java
new file mode 100644
index 000000000000..cfc1ebe1c00b
--- /dev/null
+++ b/web-modules/jersey-2/src/test/java/com/baeldung/jersey/https/JerseyHttpsClientManualTest.java
@@ -0,0 +1,130 @@
+package com.baeldung.jersey.https;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Response;
+
+/**
+ * 1. run {@code gen-keys.sh api.service} to generate server certificate/stores
+ * 2. specify system properties with the directory of the certificates and password
+ * 3. create api.service entries for 127.0.0.1 in /etc/hosts
+ * 4. run this class
+ */
+class JerseyHttpsClientManualTest {
+
+ static final String MOCK_HOST = "api.service";
+ static final String CERTS_DIR = System.getProperty("certs.dir");
+ static final String PASSWORD = System.getProperty("certs.password");
+
+ WireMockServer api;
+
+ void setup(boolean mTls) {
+ api = mockHttpsServer(mTls);
+ api.stubFor(get(urlEqualTo("/test")).willReturn(aResponse().withHeader("Content-Type", "text/plain")
+ .withStatus(200)
+ .withBody("ok")));
+ api.start();
+
+ System.out.println(">> Mock server started on HTTPS port: " + api.httpsPort());
+ }
+
+ @AfterEach
+ void teardown() {
+ if (api != null) {
+ api.stop();
+ }
+ }
+
+ @Test
+ void whenUsingJVMParameters_thenCorrectCertificateUsed() {
+ setup(false);
+
+ System.setProperty("javax.net.ssl.trustStore", CERTS_DIR + "/trust." + MOCK_HOST + ".p12");
+ System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
+
+ Response response = ClientBuilder.newClient().target(String.format("https://%s:%d/test", api.getOptions().bindAddress(), api.httpsPort()))
+ .request()
+ .get();
+
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ void whenUsingCustomSSLContext_thenCorrectCertificateUsed() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
+ setup(false);
+
+ SSLContext sslContext = SSLContextBuilder.create()
+ .loadTrustMaterial(Paths.get(CERTS_DIR + "/trust." + MOCK_HOST + ".p12"), PASSWORD.toCharArray())
+ .build();
+
+ Client client = ClientBuilder.newBuilder()
+ .sslContext(sslContext)
+ .build();
+
+ Response response = client.target(String.format("https://%s:%d/test", api.getOptions().bindAddress(), api.httpsPort()))
+ .request()
+ .get();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ void whenUsingMutualTLS_thenCorrectCertificateUsed() throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
+ setup(true);
+
+ char[] password = PASSWORD.toCharArray();
+
+ SSLContext sslContext = SSLContextBuilder.create()
+ .loadTrustMaterial(Paths.get(CERTS_DIR + "/trust." + MOCK_HOST + ".p12"), password)
+ .loadKeyMaterial(Paths.get(CERTS_DIR + "/client." + MOCK_HOST + ".p12"), password, password)
+ .build();
+
+ Client client = ClientBuilder.newBuilder()
+ .sslContext(sslContext)
+ .build();
+
+ Response response = client.target(String.format("https://%s:%d/test", api.getOptions().bindAddress(), api.httpsPort()))
+ .request()
+ .get();
+ assertEquals(200, response.getStatus());
+ }
+
+ private static WireMockServer mockHttpsServer(boolean mTls) {
+ WireMockConfiguration config = WireMockConfiguration.options()
+ .bindAddress(MOCK_HOST)
+ .dynamicPort()
+ .dynamicHttpsPort()
+ .keystorePath(CERTS_DIR + "/server." + MOCK_HOST + ".p12")
+ .keystorePassword(PASSWORD)
+ .keyManagerPassword(PASSWORD);
+
+ if (mTls) {
+ config.trustStorePath(CERTS_DIR + "/trust." + MOCK_HOST + ".p12")
+ .trustStorePassword(PASSWORD)
+ .needClientAuth(true);
+ }
+
+ return new WireMockServer(config);
+ }
+}
diff --git a/web-modules/jersey-2/src/test/resources/https/gen-keys.sh b/web-modules/jersey-2/src/test/resources/https/gen-keys.sh
new file mode 100755
index 000000000000..2bddb1922876
--- /dev/null
+++ b/web-modules/jersey-2/src/test/resources/https/gen-keys.sh
@@ -0,0 +1,49 @@
+#!/bin/bash -e
+# @see JerseyHttpsClientManualTest
+# generates a CA, signed client/server keys and trust stores for the provided host.
+# e.g.: gen-keys.sh api.service
+MYSELF="$(readlink -f $0)"
+MYDIR="${MYSELF%/*}"
+
+HOST=${1}
+if [[ -z "$HOST" ]]; then
+ echo "arg 1 should be the host/alias"
+ exit 1
+fi
+
+PASSWORD="${2}"
+if [[ -z "$PASSWORD" ]]; then
+ echo "arg 2 should be the desired password"
+ exit 1
+fi
+
+VALIDITY_DAYS=1
+CERTS_DIR=$MYDIR/keystore
+
+mkdir -p "$CERTS_DIR"
+
+keytool=$JAVA_HOME/bin/keytool
+$JAVA_HOME/bin/java -version
+
+echo '1. creating certificate authority (CA)'
+openssl genrsa -out $CERTS_DIR/ca.${HOST}.key 2048
+openssl req -x509 -new -nodes -key $CERTS_DIR/ca.${HOST}.key -sha256 -days $VALIDITY_DAYS -out $CERTS_DIR/ca.${HOST}.crt -subj "/CN=${HOST}"
+
+echo '2. generating server key and CSR'
+openssl genrsa -out $CERTS_DIR/server.${HOST}.key 2048
+openssl req -new -key $CERTS_DIR/server.${HOST}.key -out $CERTS_DIR/server.${HOST}.csr -subj "/CN=${HOST}"
+openssl x509 -req -in $CERTS_DIR/server.${HOST}.csr -CA $CERTS_DIR/ca.${HOST}.crt -CAkey $CERTS_DIR/ca.${HOST}.key -CAcreateserial -out $CERTS_DIR/server.${HOST}.crt -days $VALIDITY_DAYS -sha256
+
+echo '3. generating client key and CSR'
+openssl genrsa -out $CERTS_DIR/client.${HOST}.key 2048
+openssl req -new -key $CERTS_DIR/client.${HOST}.key -out $CERTS_DIR/client.${HOST}.csr -subj "/CN=${HOST}"
+openssl x509 -req -in $CERTS_DIR/client.${HOST}.csr -CA $CERTS_DIR/ca.${HOST}.crt -CAkey $CERTS_DIR/ca.${HOST}.key -CAcreateserial -out $CERTS_DIR/client.${HOST}.crt -days $VALIDITY_DAYS -sha256
+
+echo '4. creating PKCS12 keystores'
+openssl pkcs12 -export -out $CERTS_DIR/server.${HOST}.p12 -inkey $CERTS_DIR/server.${HOST}.key -in $CERTS_DIR/server.${HOST}.crt -certfile $CERTS_DIR/ca.${HOST}.crt -name ${HOST} -passout pass:$PASSWORD
+openssl pkcs12 -export -out $CERTS_DIR/client.${HOST}.p12 -inkey $CERTS_DIR/client.${HOST}.key -in $CERTS_DIR/client.${HOST}.crt -certfile $CERTS_DIR/ca.${HOST}.crt -name ${HOST} -passout pass:$PASSWORD
+
+echo '5. creating truststore'
+$keytool -importcert -keystore $CERTS_DIR/trust.${HOST}.p12 -storetype PKCS12 -storepass $PASSWORD -alias ca.${HOST} -file $CERTS_DIR/ca.${HOST}.crt -noprompt
+
+echo done