Skip to content

Commit

Permalink
Merge bc5c482 into 0880fcd
Browse files Browse the repository at this point in the history
  • Loading branch information
tbartley committed Aug 3, 2016
2 parents 0880fcd + bc5c482 commit 147b171
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 3 deletions.
Expand Up @@ -19,9 +19,16 @@
public class DropwizardSSLConnectionSocketFactory {

private final TlsConfiguration configuration;
private final HostnameVerifier verifier;

public DropwizardSSLConnectionSocketFactory(TlsConfiguration configuration) {
this.configuration = configuration;
this.verifier = null;
}

public DropwizardSSLConnectionSocketFactory(TlsConfiguration configuration, HostnameVerifier verifier) {
this.configuration = configuration;
this.verifier = verifier;
}

public SSLConnectionSocketFactory getSocketFactory() throws SSLInitializationException {
Expand All @@ -48,7 +55,12 @@ private String[] getSupportedProtocols() {
private HostnameVerifier chooseHostnameVerifier() {
HostnameVerifier hostnameVerifier;
if (configuration.isVerifyHostname()) {
hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
if (verifier == null) {
hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
}
else {
hostnameVerifier = verifier;
}
} else {
hostnameVerifier = new NoopHostnameVerifier();
}
Expand Down
Expand Up @@ -9,9 +9,11 @@
import io.dropwizard.client.proxy.AuthConfiguration;
import io.dropwizard.client.proxy.NonProxyListProxyRoutePlanner;
import io.dropwizard.client.proxy.ProxyConfiguration;
import io.dropwizard.client.ssl.TlsConfiguration;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.setup.Environment;
import io.dropwizard.util.Duration;
import javax.net.ssl.HostnameVerifier;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.Header;
import org.apache.http.HttpHost;
Expand Down Expand Up @@ -62,6 +64,7 @@ public class HttpClientBuilder {
private Environment environment;
private HttpClientConfiguration configuration = new HttpClientConfiguration();
private DnsResolver resolver = new SystemDefaultDnsResolver();
private HostnameVerifier verifier;
private HttpRequestRetryHandler httpRequestRetryHandler;
private Registry<ConnectionSocketFactory> registry;

Expand Down Expand Up @@ -115,6 +118,17 @@ public HttpClientBuilder using(DnsResolver resolver) {
return this;
}

/**
* Use the give (@link HostnameVerifier} instance.
*
* @param verifier a {@link HostnameVerifier} instance
* @return {@code this}
*/
public HttpClientBuilder using(HostnameVerifier verifier) {
this.verifier = verifier;
return this;
}

/**
* Uses the {@link HttpRequestRetryHandler} for handling request retries.
*
Expand Down Expand Up @@ -332,6 +346,10 @@ public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
builder.setDefaultHeaders(defaultHeaders);
}

if (verifier != null) {
builder.setSSLHostnameVerifier(verifier);
}

return new ConfiguredCloseableHttpClient(builder.build(), requestConfig);
}

Expand Down Expand Up @@ -378,9 +396,15 @@ private Registry<ConnectionSocketFactory> createConfiguredRegistry() {

final SSLConnectionSocketFactory sslConnectionSocketFactory;
if (configuration.getTlsConfiguration() == null) {
sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
if (verifier == null) {
sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
}
else {
sslConnectionSocketFactory = new DropwizardSSLConnectionSocketFactory(new TlsConfiguration(), verifier)
.getSocketFactory();
}
} else {
sslConnectionSocketFactory = new DropwizardSSLConnectionSocketFactory(configuration.getTlsConfiguration())
sslConnectionSocketFactory = new DropwizardSSLConnectionSocketFactory(configuration.getTlsConfiguration(), verifier)
.getSocketFactory();
}

Expand Down
Expand Up @@ -11,6 +11,7 @@
import io.dropwizard.jersey.validation.Validators;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.setup.Environment;
import javax.net.ssl.HostnameVerifier;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.config.Registry;
Expand Down Expand Up @@ -224,6 +225,22 @@ public JerseyClientBuilder using(DnsResolver resolver) {
return this;
}

/**
* Use the given {@link HostnameVerifier} instance.
*
* Note that if {@link io.dropwizard.client.ssl.TlsConfiguration#isVerifyHostname()}
* returns false, all host name verification is bypassed, including
* host name verification performed by a verifier specified
* through this interface.
*
* @param verifier a {@link HostnameVerifier} instance
* @return {@code this}
*/
public JerseyClientBuilder using(HostnameVerifier verifier) {
apacheHttpClientBuilder.using(verifier);
return this;
}

/**
* Use the given {@link Registry} instance of connection socket factories.
*
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
Expand Down Expand Up @@ -262,6 +263,13 @@ public void usesACustomDnsResolver() {
verify(apacheHttpClientBuilder).using(customDnsResolver);
}

@Test
public void usesACustomHostnameVerifier() {
final HostnameVerifier customHostnameVerifier = new NoopHostnameVerifier();
builder.using(customHostnameVerifier);
verify(apacheHttpClientBuilder).using(customHostnameVerifier);
}

@Test
public void usesACustomConnectionFactoryRegistry() throws Exception {
final SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
Expand Down
Expand Up @@ -9,13 +9,17 @@
import io.dropwizard.testing.ResourceHelpers;
import io.dropwizard.testing.junit.DropwizardAppRule;
import io.dropwizard.util.Duration;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.glassfish.jersey.client.ClientResponse;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProcessingException;
Expand Down Expand Up @@ -153,6 +157,25 @@ public void shouldErrorIfHostnameVerificationOnAndServerHostnameDoesntMatch() th
}
}

@Test
public void shouldErrorIfHostnameVerificationOnAndServerHostnameMatchesAndFailVerifierSpecified() throws Exception {
final Client client = new JerseyClientBuilder(TLS_APP_RULE.getEnvironment()).using(jerseyClientConfiguration).using(new FailVerifier()).build("bad_host_broken_fail_verifier");
try {
client.target(String.format("https://localhost:%d", TLS_APP_RULE.getLocalPort())).request().get();
fail("Expected ProcessingException");
} catch (ProcessingException e) {
assertThat(e.getCause()).isExactlyInstanceOf(SSLPeerUnverifiedException.class);
assertThat(e.getCause().getMessage()).isEqualTo("Host name 'localhost' does not match the certificate subject provided by the peer (O=server, CN=localhost)");
}
}

@Test
public void shouldBeOkIfHostnameVerificationOnAndServerHostnameDoesntMatchAndNoopVerifierSpecified() throws Exception {
final Client client = new JerseyClientBuilder(TLS_APP_RULE.getEnvironment()).using(jerseyClientConfiguration).using(new NoopHostnameVerifier()).build("bad_host_noop_verifier_working");
final Response response = client.target(String.format("https://localhost:%d", TLS_APP_RULE.getPort(3))).request().get();
assertThat(response.getStatus()).isEqualTo(200);
}

@Test
public void shouldBeOkIfHostnameVerificationOffAndServerHostnameDoesntMatch() throws Exception {
tlsConfiguration.setVerifyHostname(false);
Expand All @@ -161,6 +184,14 @@ public void shouldBeOkIfHostnameVerificationOffAndServerHostnameDoesntMatch() th
assertThat(response.getStatus()).isEqualTo(200);
}

@Test
public void shouldBeOkIfHostnameVerificationOffAndServerHostnameMatchesAndFailVerfierSpecified() throws Exception {
tlsConfiguration.setVerifyHostname(false);
final Client client = new JerseyClientBuilder(TLS_APP_RULE.getEnvironment()).using(jerseyClientConfiguration).using(new FailVerifier()).build("bad_host_fail_verifier_working");
final Response response = client.target(String.format("https://localhost:%d", TLS_APP_RULE.getLocalPort())).request().get();
assertThat(response.getStatus()).isEqualTo(200);
}

@Test
public void shouldRejectNonSupportedProtocols() throws Exception {
tlsConfiguration.setSupportedProtocols(asList("TLSv1.2"));
Expand All @@ -172,4 +203,13 @@ public void shouldRejectNonSupportedProtocols() throws Exception {
assertThat(e.getCause()).isInstanceOfAny(SocketException.class, SSLHandshakeException.class);
}
}

private static class FailVerifier implements HostnameVerifier {

@Override
public boolean verify(String arg0, SSLSession arg1) {
return false;
}

}
}

0 comments on commit 147b171

Please sign in to comment.