Skip to content

Commit

Permalink
Support HTTPS requests to servers that use multiple certificates
Browse files Browse the repository at this point in the history
Use Apache HttpClient 4.3+ SSL handling which supports SNI, and
adapt it to the deprecated pre 4.3 API.

Fixes Netflix#873
  • Loading branch information
Will Tran committed Nov 15, 2016
1 parent 828eea0 commit 3a744ee
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
import com.sun.jersey.client.apache4.config.ApacheHttpClient4Config;
import com.sun.jersey.client.apache4.config.DefaultApacheHttpClient4Config;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
Expand Down Expand Up @@ -180,7 +184,7 @@ class MyDefaultApacheHttpClient4Config extends DefaultApacheHttpClient4Config {
} else if (trustStoreFileName != null) {
cm = createCustomSslCM();
} else {
cm = new MonitoredConnectionManager(clientName);
cm = createDefaultSslCM();
}

if (proxyHost != null) {
Expand Down Expand Up @@ -219,7 +223,8 @@ private void addProxyConfiguration(MonitoredConnectionManager cm) {

private MonitoredConnectionManager createSystemSslCM() {
MonitoredConnectionManager cm;
SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSystemSocketFactory();
SSLConnectionSocketFactory systemSocketFactory = SSLConnectionSocketFactory.getSystemSocketFactory();
SSLSocketFactory sslSocketFactory = new SSLSocketFactoryAdapter(systemSocketFactory);
SchemeRegistry sslSchemeRegistry = new SchemeRegistry();
sslSchemeRegistry.register(new Scheme(PROTOCOL, HTTPS_PORT, sslSocketFactory));
cm = new MonitoredConnectionManager(clientName, sslSchemeRegistry);
Expand All @@ -241,8 +246,9 @@ private MonitoredConnectionManager createCustomSslCM() {
TrustManager[] trustManagers = factory.getTrustManagers();

sslContext.init(null, trustManagers, null);
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslContext);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
SSLConnectionSocketFactory customSslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
SSLSocketFactory sslSocketFactory = new SSLSocketFactoryAdapter(customSslSocketFactory);
SchemeRegistry sslSchemeRegistry = new SchemeRegistry();
sslSchemeRegistry.register(new Scheme(PROTOCOL, HTTPS_PORT, sslSocketFactory));

Expand All @@ -258,6 +264,18 @@ private MonitoredConnectionManager createCustomSslCM() {
}
}
}

/**
* @see SchemeRegistryFactory#createDefault()
*/
private MonitoredConnectionManager createDefaultSslCM() {
final SchemeRegistry registry = new SchemeRegistry();
registry.register(
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
registry.register(
new Scheme("https", 443, new SSLSocketFactoryAdapter(SSLConnectionSocketFactory.getSocketFactory())));
return new MonitoredConnectionManager(clientName, registry);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.netflix.discovery.shared.transport.jersey;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.protocol.HttpContext;

/**
* Adapts a version 4.3+ {@link SSLConnectionSocketFactory} to a pre 4.3
* {@link SSLSocketFactory}. This allows {@link HttpClient}s built using the
* deprecated pre 4.3 APIs to use SSL improvements from 4.3, e.g. SNI.
*
* @author William Tran
*
*/
public class SSLSocketFactoryAdapter extends SSLSocketFactory {

private final SSLConnectionSocketFactory factory;

public SSLSocketFactoryAdapter(SSLConnectionSocketFactory factory) {
// super's dependencies are dummies, and will delegate all calls to the
// to the overridden methods
super(DummySSLSocketFactory.INSTANCE, DummyX509HostnameVerifier.INSTANCE);
this.factory = factory;
}

@Override
public Socket createSocket(final HttpContext context) throws IOException {
return factory.createSocket(context);
}

@Override
public Socket connectSocket(
final int connectTimeout,
final Socket socket,
final HttpHost host,
final InetSocketAddress remoteAddress,
final InetSocketAddress localAddress,
final HttpContext context) throws IOException {
return factory.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
}

@Override
public Socket createLayeredSocket(
final Socket socket,
final String target,
final int port,
final HttpContext context) throws IOException {
return factory.createLayeredSocket(socket, target, port, context);
}

private static class DummySSLSocketFactory extends javax.net.ssl.SSLSocketFactory {
private static final DummySSLSocketFactory INSTANCE = new DummySSLSocketFactory();

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public String[] getDefaultCipherSuites() {
throw new UnsupportedOperationException();
}

@Override
public String[] getSupportedCipherSuites() {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
throw new UnsupportedOperationException();
}
}

private static class DummyX509HostnameVerifier implements X509HostnameVerifier {
private static final DummyX509HostnameVerifier INSTANCE = new DummyX509HostnameVerifier();

@Override
public boolean verify(String hostname, SSLSession session) {
throw new UnsupportedOperationException();
}

@Override
public void verify(String host, SSLSocket ssl) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void verify(String host, X509Certificate cert) throws SSLException {
throw new UnsupportedOperationException();
}

@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
throw new UnsupportedOperationException();
}

}

}

0 comments on commit 3a744ee

Please sign in to comment.