From af99e053a74ff2178d7bd4dccec7d4a19c359be2 Mon Sep 17 00:00:00 2001 From: PhilippVogt Date: Tue, 3 May 2016 13:46:08 +0200 Subject: [PATCH 1/2] improved SNISSLSocketFactory to set SNIHostNames for connection --- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 6 + .../httpproxy/SNISSLSocketFactory.java | 115 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index 0ee7c04f..2b6694df 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -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; @@ -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: @@ -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. diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java b/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java new file mode 100644 index 00000000..9b6c790a --- /dev/null +++ b/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java @@ -0,0 +1,115 @@ +/* + * 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 SNI, + * 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); + } + + + @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 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); + } +} From 6ff7f23174bb00525d925461ac9b7960c86b1fcd Mon Sep 17 00:00:00 2001 From: PhilippVogt Date: Tue, 3 May 2016 14:00:54 +0200 Subject: [PATCH 2/2] added missing createSocket implementation --- .../org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java b/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java index 9b6c790a..ac291851 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/SNISSLSocketFactory.java @@ -76,6 +76,14 @@ public SNISSLSocketFactory(SSLSocketFactory sslSocketFactory, } + /** + * {@inheritDoc} + */ + @Override + public Socket createSocket(HttpContext context) throws IOException { + return SSLSocketFactory.getDefault().createSocket(); + } + @Override public Socket connectSocket( int connectTimeout,