From 55d77fedccab75655dfb0de2d4f55784adecc11f Mon Sep 17 00:00:00 2001 From: "Mathieu AMBLARD (u118971)" Date: Wed, 19 Oct 2022 14:50:26 +0200 Subject: [PATCH] Enable the use of relaxedHTTPSValidation with two-way ssl (rest-assured#1631) --- .../io/restassured/itest/java/SSLITest.java | 390 ++++++++++-------- .../itest/java/support/WithJetty.java | 71 +++- .../src/test/resources/keystore.p12 | Bin 0 -> 4286 bytes .../java/io/restassured/config/SSLConfig.java | 77 +++- 4 files changed, 358 insertions(+), 180 deletions(-) create mode 100644 examples/rest-assured-itest-java/src/test/resources/keystore.p12 diff --git a/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/SSLITest.java b/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/SSLITest.java index 9285aac29..bb3a17888 100644 --- a/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/SSLITest.java +++ b/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/SSLITest.java @@ -25,10 +25,13 @@ import io.restassured.itest.java.support.WithJetty; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; @@ -39,10 +42,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -public class SSLITest extends WithJetty { - - @Rule - public ExpectedException exception = ExpectedException.none(); +@RunWith(Enclosed.class) +public class SSLITest { public static ResponseSpecification helloWorldSpec() { return new ResponseSpecBuilder(). @@ -50,215 +51,260 @@ public static ResponseSpecification helloWorldSpec() { expectStatusCode(200).build(); } - @Test(expected = SSLException.class) - public void throwsSSLExceptionWhenHostnameInCertDoesntMatch() throws Exception { - RestAssured.get("https://localhost:8443/hello"); - } + public static class OneWaySSLTest extends WithJetty { - @Test - public void givenTrustStoreDefinedStaticallyWhenSpecifyingJksKeyStoreFileWithCorrectPasswordAllowsToUseSSL() throws Exception { - RestAssured.trustStore("jetty_localhost_client.jks", "test1234"); - try { - RestAssured.expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); - } finally { - RestAssured.reset(); - } - } + @Rule + public ExpectedException exception = ExpectedException.none(); - @Test(expected = SSLHandshakeException.class) - public void whenEnablingAllowAllHostNamesVerifierWithoutActivatingAKeyStore() throws Exception { - RestAssured.config = RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames()); - try { - RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } finally { - RestAssured.reset(); + @Test(expected = SSLException.class) + public void throwsSSLExceptionWhenHostnameInCertDoesntMatch() throws Exception { + RestAssured.get("https://localhost:8443/hello"); } - } - @Test - public void usingStaticallyConfiguredCertificateAuthenticationWorks() throws Exception { - RestAssured.authentication = RestAssured.certificate("jetty_localhost_client.jks", "test1234", CertificateAuthSettings.certAuthSettings().allowAllHostnames()); - try { - RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } finally { - RestAssured.reset(); + @Test + public void givenTrustStoreDefinedStaticallyWhenSpecifyingJksKeyStoreFileWithCorrectPasswordAllowsToUseSSL() throws Exception { + RestAssured.trustStore("jetty_localhost_client.jks", "test1234"); + try { + RestAssured.expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); + } finally { + RestAssured.reset(); + } } - } - @Test(expected = SSLException.class) - public void usingStaticallyConfiguredCertificateAuthenticationWithIllegalHostNameInCertDoesntWork() throws Exception { - RestAssured.authentication = RestAssured.certificate("truststore_mjvmobile.jks", "test4321"); - try { - RestAssured.get("https://localhost:8443/hello").then().body(containsString("eurosport")); - } finally { - RestAssured.reset(); + @Test(expected = SSLHandshakeException.class) + public void whenEnablingAllowAllHostNamesVerifierWithoutActivatingAKeyStore() throws Exception { + RestAssured.config = RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames()); + try { + RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } finally { + RestAssured.reset(); + } } - } - @Test - public void usingStaticallyConfiguredCertificateAuthenticationWithIllegalHostNameInCertWorksWhenSSLConfigIsConfiguredToAllowAllHostNames() throws Exception { - RestAssured.config = RestAssuredConfig.newConfig().sslConfig(SSLConfig.sslConfig().allowAllHostnames()); - RestAssured.authentication = RestAssured.certificate("jetty_localhost_client.jks", "test1234"); - try { - RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } finally { - RestAssured.reset(); + @Test + public void usingStaticallyConfiguredCertificateAuthenticationWorks() throws Exception { + RestAssured.authentication = RestAssured.certificate("jetty_localhost_client.jks", "test1234", CertificateAuthSettings.certAuthSettings().allowAllHostnames()); + try { + RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } finally { + RestAssured.reset(); + } } - } - @Test - public void givenKeystoreDefinedUsingGivenWhenSpecifyingJksKeyStoreFileWithCorrectPasswordAllowsToUseSSL() throws Exception { - RestAssured.given().trustStore("/jetty_localhost_client.jks", "test1234").then().expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); - } + @Test(expected = SSLException.class) + public void usingStaticallyConfiguredCertificateAuthenticationWithIllegalHostNameInCertDoesntWork() throws Exception { + RestAssured.authentication = RestAssured.certificate("truststore_mjvmobile.jks", "test4321"); + try { + RestAssured.get("https://localhost:8443/hello").then().body(containsString("eurosport")); + } finally { + RestAssured.reset(); + } + } - @Test - public void throwsIOExceptionWhenPasswordIsIncorrect() throws Exception { - exception.expect(IOException.class); - exception.expectMessage("Keystore was tampered with, or password was incorrect"); - - RestAssured.given(). - auth().certificate("jetty_localhost_client.jks", "test4333"). - when(). - get("https://localhost:8443/hello"). - then(). - body(containsString("eurosport")); - } + @Test + public void usingStaticallyConfiguredCertificateAuthenticationWithIllegalHostNameInCertWorksWhenSSLConfigIsConfiguredToAllowAllHostNames() throws Exception { + RestAssured.config = RestAssuredConfig.newConfig().sslConfig(SSLConfig.sslConfig().allowAllHostnames()); + RestAssured.authentication = RestAssured.certificate("jetty_localhost_client.jks", "test1234"); + try { + RestAssured.get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } finally { + RestAssured.reset(); + } + } - @Test - public void certificateAuthenticationWorks() throws Exception { - RestAssured.given(). - auth().certificate("jetty_localhost_client.jks", "test1234", CertificateAuthSettings.certAuthSettings().allowAllHostnames()). - when(). - get("https://localhost:8443/hello"). - then(). - spec(helloWorldSpec()); - } + @Test + public void givenKeystoreDefinedUsingGivenWhenSpecifyingJksKeyStoreFileWithCorrectPasswordAllowsToUseSSL() throws Exception { + RestAssured.given().trustStore("/jetty_localhost_client.jks", "test1234").then().expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); + } - @Test public void - allows_specifying_trust_store_in_dsl() throws Exception { - InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(keyStoreStream, "test1234".toCharArray()); + @Test + public void throwsIOExceptionWhenPasswordIsIncorrect() throws Exception { + exception.expect(IOException.class); + exception.expectMessage("Keystore was tampered with, or password was incorrect"); + + RestAssured.given(). + auth().certificate("jetty_localhost_client.jks", "test4333"). + when(). + get("https://localhost:8443/hello"). + then(). + body(containsString("eurosport")); + } - RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames())).trustStore(keyStore).when().get("https://localhost:8443/hello").then().statusCode(200); - } + @Test + public void certificateAuthenticationWorks() throws Exception { + RestAssured.given(). + auth().certificate("jetty_localhost_client.jks", "test1234", CertificateAuthSettings.certAuthSettings().allowAllHostnames()). + when(). + get("https://localhost:8443/hello"). + then(). + spec(helloWorldSpec()); + } - @Ignore("Temporary ignored but I think this ought to work. Perhaps some issues with config merging?") - @Test public void - allows_specifying_trust_store_statically() throws Exception { - InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(keyStoreStream, "test1234".toCharArray()); + @Test + public void + allows_specifying_trust_store_in_dsl() throws Exception { + InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(keyStoreStream, "test1234".toCharArray()); - RestAssured.trustStore(keyStore); + RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames())).trustStore(keyStore).when().get("https://localhost:8443/hello").then().statusCode(200); + } - try { - RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames())).when().get("https://localhost:8443/hello").then().statusCode(200); - } finally { - RestAssured.reset(); + @Ignore("Temporary ignored but I think this ought to work. Perhaps some issues with config merging?") + @Test + public void + allows_specifying_trust_store_statically() throws Exception { + InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(keyStoreStream, "test1234".toCharArray()); + + RestAssured.trustStore(keyStore); + + try { + RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().allowAllHostnames())).when().get("https://localhost:8443/hello").then().statusCode(200); + } finally { + RestAssured.reset(); + } } - } - @Test public void - allows_specifying_trust_store_and_allow_all_host_names_in_config_using_dsl() throws Exception { - InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(keyStoreStream, "test1234".toCharArray()); + @Test + public void + allows_specifying_trust_store_and_allow_all_host_names_in_config_using_dsl() throws Exception { + InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(keyStoreStream, "test1234".toCharArray()); - RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().trustStore(keyStore).and().allowAllHostnames())).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } + RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().trustStore(keyStore).and().allowAllHostnames())).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } - @Test public void - relaxed_https_validation_works_using_instance_config() { - RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation())).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } + @Test + public void + relaxed_https_validation_works_using_instance_config() { + RestAssured.given().config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation())).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } - @Ignore("Site is not working anymore") - @Test public void - relaxed_https_validation_works_using_instance_dsl() { - RestAssured.given().relaxedHTTPSValidation().when().get("https://bunny.cloudamqp.com/api/").then().statusCode(200); - } + @Ignore("Site is not working anymore") + @Test + public void + relaxed_https_validation_works_using_instance_dsl() { + RestAssured.given().relaxedHTTPSValidation().when().get("https://bunny.cloudamqp.com/api/").then().statusCode(200); + } - @Ignore("Site is not working anymore") - @Test public void - relaxed_https_validation_works_when_defined_statically() { - RestAssured.useRelaxedHTTPSValidation(); + @Ignore("Site is not working anymore") + @Test + public void + relaxed_https_validation_works_when_defined_statically() { + RestAssured.useRelaxedHTTPSValidation(); + + try { + RestAssured.get("https://bunny.cloudamqp.com/api/").then().statusCode(200); + } finally { + RestAssured.reset(); + } + } - try { - RestAssured.get("https://bunny.cloudamqp.com/api/").then().statusCode(200); - } finally { - RestAssured.reset(); + @Ignore("Site is not working anymore") + @Test + public void + relaxed_https_validation_works_when_defined_statically_with_base_uri() { + RestAssured.useRelaxedHTTPSValidation(); + RestAssured.baseURI = "https://bunny.cloudamqp.com"; + + try { + RestAssured.get("/api/").then().statusCode(200); + } finally { + RestAssured.reset(); + } } - } - @Ignore("Site is not working anymore") - @Test public void - relaxed_https_validation_works_when_defined_statically_with_base_uri() { - RestAssured.useRelaxedHTTPSValidation(); - RestAssured.baseURI = "https://bunny.cloudamqp.com"; + @Test + public void + truststore_works_with_static_base_uri() { + RestAssured.baseURI = "https://localhost:8443/hello"; - try { - RestAssured.get("/api/").then().statusCode(200); - } finally { - RestAssured.reset(); + try { + RestAssured.given().trustStore("/jetty_localhost_client.jks", "test1234").when().get().then().spec(helloWorldSpec()); + } finally { + RestAssured.reset(); + } + } + + @Ignore("Temporary ignored since site has changed") + @Test + public void + truststrore_works_with_static_base_uri() throws Exception { + RestAssured.baseURI = "https://bunny.cloudamqp.com/"; + + InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("truststore_cloudamqp.jks"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(keyStoreStream, "cloud1234".toCharArray()); + + try { + RestAssured.given().trustStore(keyStore).when().get("/api/").then().statusCode(200); + } finally { + RestAssured.reset(); + } } - } - @Test public void - truststore_works_with_static_base_uri() { - RestAssured.baseURI = "https://localhost:8443/hello"; - try { - RestAssured.given().trustStore("/jetty_localhost_client.jks", "test1234").when().get().then().spec(helloWorldSpec()); - } finally { - RestAssured.reset(); + @Test + public void + can_make_request_to_sites_that_with_valid_ssl_cert() { + RestAssured.get("https://duckduckgo.com/").then().statusCode(200); } - } - @Ignore("Temporary ignored since site has changed") @Test public void - truststrore_works_with_static_base_uri() throws Exception{ - RestAssured.baseURI = "https://bunny.cloudamqp.com/"; + @Test + public void + allows_specifying_trust_store_statically_with_request_builder() throws Exception { + // Load the trust store + InputStream trustStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(trustStoreStream, "test1234".toCharArray()); - InputStream keyStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("truststore_cloudamqp.jks"); - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(keyStoreStream, "cloud1234".toCharArray()); + // Set the truststore on the global config + RestAssured.config = RestAssured.config().sslConfig(SSLConfig.sslConfig().trustStore(trustStore).and().allowAllHostnames()); - try { - RestAssured.given().trustStore(keyStore).when().get("/api/").then().statusCode(200); - } finally { - RestAssured.reset(); + final RequestSpecification spec = new RequestSpecBuilder().build(); + RestAssured.given().spec(spec).get("https://localhost:8443/hello").then().spec(helloWorldSpec()); } - } + @Test + public void + supports_setting_truststore_in_request_specification() { + final RequestSpecification spec = new RequestSpecBuilder().setTrustStore("/jetty_localhost_client.jks", "test1234").build(); + RestAssured.given().spec(spec).expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); + } - @Test public void - can_make_request_to_sites_that_with_valid_ssl_cert() { - RestAssured.get("https://duckduckgo.com/").then().statusCode(200); + @Test + public void + supports_overriding_truststore_in_request_specification() { + final RequestSpecification spec = new RequestSpecBuilder().setTrustStore("/jetty_localhost_client.jks", "wrong pw").build(); + RestAssured.given().spec(spec).trustStore("/jetty_localhost_client.jks", "test1234").expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); + } } - @Test public void - allows_specifying_trust_store_statically_with_request_builder() throws Exception { - // Load the trust store - InputStream trustStoreStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jetty_localhost_client.jks"); - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - trustStore.load(trustStoreStream, "test1234".toCharArray()); - - // Set the truststore on the global config - RestAssured.config = RestAssured.config().sslConfig(SSLConfig.sslConfig().trustStore(trustStore).and().allowAllHostnames()); + public static class TwoWaySSLTest extends WithJetty { - final RequestSpecification spec = new RequestSpecBuilder().build(); - RestAssured.given().spec(spec).get("https://localhost:8443/hello").then().spec(helloWorldSpec()); - } + @BeforeClass + public static void startJetty() throws Exception { + startJettyTwoWaySSL(); + } - @Test public void - supports_setting_truststore_in_request_specification() { - final RequestSpecification spec = new RequestSpecBuilder().setTrustStore("/jetty_localhost_client.jks", "test1234").build(); - RestAssured.given().spec(spec).expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); - } + @Test(expected = SSLException.class) + public void relaxed_https_validation_throws_exception_without_keystore() { + RestAssuredConfig sslConfig = RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation()); + RestAssured.given().config(sslConfig).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } - @Test public void - supports_overriding_truststore_in_request_specification() { - final RequestSpecification spec = new RequestSpecBuilder().setTrustStore("/jetty_localhost_client.jks", "wrong pw").build(); - RestAssured.given().spec(spec).trustStore("/jetty_localhost_client.jks", "test1234").expect().spec(helloWorldSpec()).when().get("https://localhost:8443/hello"); + @Test + public void relaxed_https_validation_works_using_instance_config() { + RestAssuredConfig sslConfig = RestAssuredConfig.config().sslConfig( + SSLConfig.sslConfig() + .keyStore("keystore.p12", "test1234") + .keystoreType("PKCS12") + .relaxedHTTPSValidation() + ); + RestAssured.given().config(sslConfig).when().get("https://localhost:8443/hello").then().spec(helloWorldSpec()); + } } } diff --git a/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/support/WithJetty.java b/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/support/WithJetty.java index 34cbf3314..ca27e385b 100644 --- a/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/support/WithJetty.java +++ b/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/support/WithJetty.java @@ -23,7 +23,17 @@ import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; -import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.SecuredRedirectHandler; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; @@ -33,7 +43,11 @@ import org.junit.Rule; import org.junit.rules.ExpectedException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; +import java.io.IOException; import java.util.Collections; import static io.restassured.itest.java.support.WithJetty.JettyOption.RESET_REST_ASSURED_BEFORE_TEST; @@ -61,6 +75,10 @@ protected WithJetty(JettyOption jettyOption) { @BeforeClass public static void startJetty() throws Exception { + startJettyOneWaySSL(); + } + + protected static void startJettyOneWaySSL() throws Exception { server = new Server(); HttpConfiguration httpConfig = new HttpConfiguration(); @@ -129,6 +147,46 @@ public static void startJetty() throws Exception { server.start(); } + protected static void startJettyTwoWaySSL() throws Exception { + server = new Server(); + int httpsPort = 8443; + + // Setup HTTP Connector + HttpConfiguration httpConf = new HttpConfiguration(); + httpConf.setSecurePort(httpsPort); + httpConf.setSecureScheme("https"); + + // Setup SSL + String keystore = WithJetty.class.getClassLoader().getResource("keystore.p12").getFile(); + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePassword("test1234"); + sslContextFactory.setTrustAll(true); + sslContextFactory.setNeedClientAuth(true); + + // Setup HTTPS Configuration + HttpConfiguration httpsConf = new HttpConfiguration(); + httpsConf.setSecureScheme("https"); + httpsConf.setSecurePort(httpsPort); + httpsConf.addCustomizer(new SecureRequestCustomizer()); + + // Establish the HTTPS ServerConnector + ServerConnector httpsConnector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(httpsConf)); + httpsConnector.setPort(httpsPort); + + server.addConnector(httpsConnector); + + // Add a Handlers for requests + HandlerList handlers = new HandlerList(); + handlers.addHandler(new SecuredRedirectHandler()); + handlers.addHandler(new HelloHandler()); + server.setHandler(handlers); + + server.start(); + } + private static void dontSendDateHeader(Server server) { // Remove the sending of date header since it makes testing of logging much harder for (Connector y : server.getConnectors()) { @@ -167,4 +225,15 @@ public enum JettyOption { RESET_REST_ASSURED_BEFORE_TEST, DONT_RESET_REST_ASSURED_BEFORE_TEST } + + private static class HelloHandler extends AbstractHandler { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + response.setContentType("text/json;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + response.getWriter().println("{\"hello\": \"Hello Scalatra\"}"); + } + } } diff --git a/examples/rest-assured-itest-java/src/test/resources/keystore.p12 b/examples/rest-assured-itest-java/src/test/resources/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d07388ef3fac601bc8148c6edc5998602f54c1c1 GIT binary patch literal 4286 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 X0~(c2jsbXVeI<{QAiI43)VTivrOGL1 literal 0 HcmV?d00001 diff --git a/rest-assured/src/main/java/io/restassured/config/SSLConfig.java b/rest-assured/src/main/java/io/restassured/config/SSLConfig.java index 4540eff2a..e51b99330 100644 --- a/rest-assured/src/main/java/io/restassured/config/SSLConfig.java +++ b/rest-assured/src/main/java/io/restassured/config/SSLConfig.java @@ -22,14 +22,23 @@ import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; @@ -246,8 +255,10 @@ public SSLConfig trustStore(KeyStore trustStore) { } /** - * Use relaxed HTTP validation. This means that you'll trust all hosts regardless if the SSL certificate is invalid. By using this - * method you don't need to specify a keystore (see {@link #keyStore(String, String)} or trust store (see {@link #trustStore(java.security.KeyStore)}. + * Use relaxed HTTP validation. This means that you'll trust all hosts regardless if the SSL certificate is invalid. + * By using this method you don't need to specify a trust store (see {@link #trustStore(java.security.KeyStore)}. + * If you need to send an SSL certificate, then you can specify a key store (see {@link #keyStore(File, String)} + * and a key store type (see {@link #keystoreType(String)} before calling this method. * This method assumes that the protocol for the {@link SSLContext} instance is {@value #SSL}. If this is not the case use {@link #relaxedHTTPSValidation(String)}. * * @return A new SSLConfig instance. @@ -257,8 +268,10 @@ public SSLConfig relaxedHTTPSValidation() { } /** - * Use relaxed HTTP validation. This means that you'll trust all hosts regardless if the SSL certificate is invalid. By using this - * method you don't need to specify a keystore (see {@link #keyStore(String, String)} or trust store (see {@link #trustStore(java.security.KeyStore)}. + * Use relaxed HTTP validation. This means that you'll trust all hosts regardless if the SSL certificate is invalid. + * By using this method you don't need to specify a trust store (see {@link #trustStore(java.security.KeyStore)}. + * If you need to send an SSL certificate, then you can specify a key store (see {@link #keyStore(File, String)} + * and a key store type (see {@link #keystoreType(String)} before calling this method * * @param protocol The standard name of the requested protocol. See the SSLContext section in the Java Cryptography Architecture Standard Algorithm Name Documentation for information about standard protocol names. * @return A new SSLConfig instance @@ -272,9 +285,18 @@ public SSLConfig relaxedHTTPSValidation(String protocol) { return SafeExceptionRethrower.safeRethrow(e); } - // Set up a TrustManager that trusts everything try { - sslContext.init(null, new TrustManager[]{new X509TrustManager() { + // Set up a KeyManager to use with relaxed HTTPS validation + KeyStore relaxedKeyStore = loadKeyStore(); + KeyManager[] keyManagers = null; + if (relaxedKeyStore != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(relaxedKeyStore, keyStorePassword.toCharArray()); + keyManagers = kmf.getKeyManagers(); + } + // Set up a TrustManager that trusts everything + sslContext.init(keyManagers, new TrustManager[]{new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } @@ -285,7 +307,7 @@ public void checkClientTrusted(X509Certificate[] certs, String authType) { public void checkServerTrusted(X509Certificate[] certs, String authType) { } }}, new SecureRandom()); - } catch (KeyManagementException e) { + } catch (KeyManagementException | UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) { return SafeExceptionRethrower.safeRethrow(e); } @@ -451,8 +473,49 @@ public X509HostnameVerifier getX509HostnameVerifier() { /** * @return true if user has configured this SSL Configuration instance, false otherwise. */ + @Override public boolean isUserConfigured() { return isUserConfigured; } + private KeyStore loadKeyStore() { + if (pathToKeyStore == null) { + return null; + } + + InputStream resource; + try { + if (pathToKeyStore instanceof String) { + String keyStorePath = (String) pathToKeyStore; + if (keyStorePath.trim().isEmpty()) { + return null; + } + resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStorePath); + if (resource == null) { // To allow for backward compatibility + resource = getClass().getResourceAsStream(keyStorePath); + } + if (resource == null) { // Fallback to load path as file if not found in classpath + resource = new FileInputStream(keyStorePath); + } + } else { + resource = new FileInputStream((File) pathToKeyStore); + } + } catch (FileNotFoundException e) { + return SafeExceptionRethrower.safeRethrow(e); + } + + try { + KeyStore relaxedKeyStore = KeyStore.getInstance(keyStoreType); + relaxedKeyStore.load(resource, keyStorePassword.toCharArray()); + return relaxedKeyStore; + } catch (IOException | KeyStoreException | CertificateException | NoSuchAlgorithmException e) { + return SafeExceptionRethrower.safeRethrow(e); + } finally { + try { + resource.close(); + } catch (IOException e) { + // Nothing to do + } + } + } }