Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improved SNISSLSocketFactory to set SNIHostNames for connection #89

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
Expand Down Expand Up @@ -168,6 +169,7 @@ protected void initTarget() throws ServletException {
* SystemDefaultHttpClient uses PoolingClientConnectionManager. In any case, it should be thread-safe. */
@SuppressWarnings({"unchecked", "deprecation"})
protected HttpClient createHttpClient(HttpParams hcParams) {
/*
try {
//as of HttpComponents v4.2, this class is better since it uses System
// Properties:
Expand All @@ -182,6 +184,10 @@ protected HttpClient createHttpClient(HttpParams hcParams) {

//Fallback on using older client:
return new DefaultHttpClient(new ThreadSafeClientConnManager(), hcParams);
*/

return HttpClientBuilder.create().useSystemProperties().disableCookieManagement().disableRedirectHandling()
.setSSLSocketFactory(SNISSLSocketFactory.createFromSystem()).build();
}

/** The http client used.
Expand Down
123 changes: 123 additions & 0 deletions src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright MITRE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.mitre.dsmiley.httpproxy;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.TextUtils;

import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
* An SSL Socket Factory supporting <a href="">SNI</a>,
* at least on the Sun/Oracle JDK. {@link SSLConnectionSocketFactory} was introduced in
* HttpClient 4.3; previously this was possible using
* {@link org.apache.http.conn.ssl.SSLSocketFactory} which is deprecated.
*/
public class SNISSLSocketFactory extends SSLConnectionSocketFactory {
/** Use commons-logging because that's what HttpClient uses (no new dependencies). */
private final Log log = LogFactory.getLog(getClass());

public static SNISSLSocketFactory createFromSystem() {
// See HttpClientBuilder.build when it creates an SSLSocketFactory when systemProperties==true
return new SNISSLSocketFactory(
(SSLSocketFactory) SSLSocketFactory.getDefault(),
split(System.getProperty("https.protocols")),
split(System.getProperty("https.cipherSuites")),
null);//hostnameVerifier will default
}

// public SNISSLSocketFactory(SSLSocketFactory sslSocketFactory,
// String[] supportedProtocols, String[] supportedCipherSuites,
// HostnameVerifier hostnameVerifier) {
// super(sslSocketFactory, supportedProtocols, supportedCipherSuites, hostnameVerifier)
// }

// copy of HttpClientBuilder.split
private static String[] split(String s) {
return TextUtils.isBlank(s) ? null : s.split(" *, *");
}

// note: the constructors of our superclass are all either introduced in v4.4 or are
// v4.4+

/** Note: We support HttpClient v4.3 so we must use a deprecated constructor. */
@SuppressWarnings({"deprecation"})
public SNISSLSocketFactory(SSLSocketFactory sslSocketFactory,
String[] supportedProtocols, String[] supportedCipherSuites,
X509HostnameVerifier hostnameVerifier) {
super(sslSocketFactory, supportedProtocols, supportedCipherSuites, hostnameVerifier);
}


/**
* {@inheritDoc}
*/
@Override
public Socket createSocket(HttpContext context) throws IOException {
return SSLSocketFactory.getDefault().createSocket();
}

@Override
public Socket connectSocket(
int connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
// For SNI support, we call setHost(hostname) on the socket. But this method isn't part of
// the SNLSocket JDK class; it's on a Sun/Oracle implementation class:
// sun.security.ssl.SSLSocketImpl So we invoke it via reflection.
/*
try {
socket.getClass().getDeclaredMethod("setHost", String.class)
.invoke(socket, host.getHostName());
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
log.debug("Couldn't invoke setHost on " + socket.getClass() + " for SNI support.", ex);
}
*/
if (socket instanceof SSLSocket) {

// see https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SNIExtension

final SSLSocket sslSocket = (SSLSocket) socket;
final javax.net.ssl.SNIHostName serverName = new javax.net.ssl.SNIHostName(host.getHostName());
final List<javax.net.ssl.SNIServerName> serverNames = new ArrayList<>(1);
serverNames.add(serverName);

final SSLParameters params = sslSocket.getSSLParameters();
params.setServerNames(serverNames);
sslSocket.setSSLParameters(params);
}

return super.connectSocket(connectTimeout, socket, host, remoteAddress,
localAddress, context);
}
}