Skip to content

Commit 672b8ea

Browse files
committed
Using netty SslContext instead of JDK SSLContext when creating client and server connections using ImpersonatingMitmManager
1 parent 93f9f84 commit 672b8ea

File tree

2 files changed

+35
-75
lines changed

2 files changed

+35
-75
lines changed

mitm/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import com.google.common.base.Suppliers;
55
import com.google.common.cache.Cache;
66
import com.google.common.cache.CacheBuilder;
7+
import io.netty.buffer.ByteBufAllocator;
8+
import io.netty.handler.ssl.SslContext;
9+
import io.netty.handler.ssl.SslContextBuilder;
710
import net.lightbody.bmp.mitm.CertificateAndKey;
811
import net.lightbody.bmp.mitm.CertificateAndKeySource;
912
import net.lightbody.bmp.mitm.CertificateInfo;
@@ -25,13 +28,11 @@
2528
import org.slf4j.Logger;
2629
import org.slf4j.LoggerFactory;
2730

28-
import javax.net.ssl.KeyManager;
29-
import javax.net.ssl.SSLContext;
3031
import javax.net.ssl.SSLEngine;
32+
import javax.net.ssl.SSLException;
3133
import javax.net.ssl.SSLParameters;
3234
import javax.net.ssl.SSLSession;
3335
import java.security.KeyPair;
34-
import java.security.KeyStore;
3536
import java.security.PrivateKey;
3637
import java.security.cert.X509Certificate;
3738
import java.util.Collections;
@@ -47,32 +48,21 @@
4748
public class ImpersonatingMitmManager implements MitmManager {
4849
private static final Logger log = LoggerFactory.getLogger(ImpersonatingMitmManager.class);
4950

50-
/**
51-
* The KeyStore password for impersonated server KeyStores. This value can be anything, since it is only used to store and immediately extract
52-
* the Java KeyManagers after creating an impersonated server certificate.
53-
*/
54-
private static final String IMPERSONATED_SERVER_KEYSTORE_PASSWORD = "impersonationPassword";
55-
56-
/**
57-
* The alias for the impersonated server certificate. This value can be anything, since it is only used to store the cert in the KeyStore.
58-
*/
59-
private static final String IMPERSONATED_CERTIFICATE_ALIAS = "impersonatedCertificate";
60-
6151
/**
6252
* The SSLContext that will be used for communications with all upstream servers. This can be reused, so store it as a lazily-loaded singleton.
6353
*/
64-
private final Supplier<SSLContext> upstreamServerSslContext = Suppliers.memoize(new Supplier<SSLContext>() {
54+
private final Supplier<SslContext> upstreamServerSslContext = Suppliers.memoize(new Supplier<SslContext>() {
6555
@Override
66-
public SSLContext get() {
56+
public SslContext get() {
6757
return SslUtil.getUpstreamServerSslContext(trustAllUpstreamServers);
6858
}
6959
});
7060

7161
/**
72-
* Cache for impersonating SSLContexts. SSLContexts can be safely reused, so caching the impersonating contexts avoids
62+
* Cache for impersonating netty SslContexts. SslContexts can be safely reused, so caching the impersonating contexts avoids
7363
* repeatedly re-impersonating upstream servers.
7464
*/
75-
private final Cache<String, SSLContext> sslContextCache;
65+
private final Cache<String, SslContext> sslContextCache;
7666

7767
/**
7868
* Generator used to create public and private keys for the server certificates.
@@ -175,7 +165,7 @@ public ImpersonatingMitmManager(CertificateAndKeySource rootCertificateSource,
175165
@Override
176166
public SSLEngine serverSslEngine(String peerHost, int peerPort) {
177167
try {
178-
SSLEngine sslEngine = upstreamServerSslContext.get().createSSLEngine(peerHost, peerPort);
168+
SSLEngine sslEngine = upstreamServerSslContext.get().newEngine(ByteBufAllocator.DEFAULT, peerHost, peerPort);
179169

180170
// support SNI by setting the endpoint identification algorithm. this requires Java 7+.
181171
SSLParameters sslParams = new SSLParameters();
@@ -191,9 +181,9 @@ public SSLEngine serverSslEngine(String peerHost, int peerPort) {
191181
@Override
192182
public SSLEngine clientSslEngineFor(SSLSession sslSession) {
193183
try {
194-
SSLContext ctx = getHostnameImpersonatingSslContext(sslSession);
184+
SslContext ctx = getHostnameImpersonatingSslContext(sslSession);
195185

196-
return ctx.createSSLEngine();
186+
return ctx.newEngine(ByteBufAllocator.DEFAULT);
197187
} catch (RuntimeException e) {
198188
throw new MitmException("Error creating SSLEngine for connection to client to impersonate upstream host: " + sslSession.getPeerHost(), e);
199189
}
@@ -207,15 +197,15 @@ public SSLEngine clientSslEngineFor(SSLSession sslSession) {
207197
* @param sslSession the upstream server SSLSession
208198
* @return SSLContext which will present an impersonated certificate
209199
*/
210-
private SSLContext getHostnameImpersonatingSslContext(final SSLSession sslSession) {
200+
private SslContext getHostnameImpersonatingSslContext(final SSLSession sslSession) {
211201
final String hostnameToImpersonate = sslSession.getPeerHost();
212202

213203
//TODO: generate wildcard certificates, rather than one certificate per host, to reduce the number of certs generated
214204

215205
try {
216-
return sslContextCache.get(hostnameToImpersonate, new Callable<SSLContext>() {
206+
return sslContextCache.get(hostnameToImpersonate, new Callable<SslContext>() {
217207
@Override
218-
public SSLContext call() throws Exception {
208+
public SslContext call() throws Exception {
219209
return createImpersonatingSslContext(sslSession, hostnameToImpersonate);
220210
}
221211
});
@@ -231,7 +221,7 @@ public SSLContext call() throws Exception {
231221
* @param hostnameToImpersonate hostname (supplied by the client's HTTP CONNECT) that will be impersonated
232222
* @return an SSLContext presenting a certificate matching the hostnameToImpersonate
233223
*/
234-
private SSLContext createImpersonatingSslContext(SSLSession sslSession, String hostnameToImpersonate) {
224+
private SslContext createImpersonatingSslContext(SSLSession sslSession, String hostnameToImpersonate) {
235225
long impersonationStart = System.currentTimeMillis();
236226

237227
// generate a Java KeyStore which contains the impersonated server certificate and the certificate's private key.
@@ -272,25 +262,21 @@ private SSLContext createImpersonatingSslContext(SSLSession sslSession, String h
272262
serverKeyPair,
273263
serverCertificateMessageDigest);
274264

275-
// bundle the newly-forged server certificate into a java KeyStore, for use by the SSLContext
276-
KeyStore impersonatedServerKeyStore = securityProviderTool.createServerKeyStore(
277-
MitmConstants.DEFAULT_KEYSTORE_TYPE,
278-
impersonatedCertificateAndKey,
279-
caRootCertificate,
280-
IMPERSONATED_CERTIFICATE_ALIAS, IMPERSONATED_SERVER_KEYSTORE_PASSWORD
281-
);
265+
X509Certificate[] certChain = {impersonatedCertificateAndKey.getCertificate(), caRootCertificate};
266+
SslContext sslContext;
267+
try {
268+
sslContext = SslContextBuilder.forServer(impersonatedCertificateAndKey.getPrivateKey(), certChain).build();
269+
} catch (SSLException e) {
270+
throw new MitmException("Error creating SslContext for connection to client using impersonated certificate and private key", e);
271+
}
282272

283273
long impersonationFinish = System.currentTimeMillis();
284274

285275
statistics.certificateCreated(impersonationStart, impersonationFinish);
286276

287277
log.debug("Impersonated certificate for {} in {}ms", hostnameToImpersonate, impersonationFinish - impersonationStart);
288278

289-
// retrieve the Java KeyManagers that the SSLContext will use to retrieve the impersonated certificate and private key
290-
KeyManager[] keyManagers = securityProviderTool.getKeyManagers(impersonatedServerKeyStore, IMPERSONATED_SERVER_KEYSTORE_PASSWORD);
291-
292-
// create an SSLContext for this communication with the client that will present the impersonated upstream server credentials
293-
return SslUtil.getClientSslContext(keyManagers);
279+
return sslContext;
294280
}
295281

296282
/**

mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package net.lightbody.bmp.mitm.util;
22

3+
import io.netty.handler.ssl.SslContext;
4+
import io.netty.handler.ssl.SslContextBuilder;
35
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
46
import net.lightbody.bmp.mitm.exception.SslContextInitializationException;
57
import org.slf4j.Logger;
68
import org.slf4j.LoggerFactory;
79

8-
import javax.net.ssl.KeyManager;
9-
import javax.net.ssl.SSLContext;
10+
import javax.net.ssl.SSLException;
1011
import javax.net.ssl.SSLPeerUnverifiedException;
1112
import javax.net.ssl.SSLSession;
12-
import javax.net.ssl.TrustManager;
13-
import java.security.KeyManagementException;
14-
import java.security.NoSuchAlgorithmException;
1513
import java.security.cert.Certificate;
1614
import java.security.cert.X509Certificate;
1715

@@ -22,53 +20,29 @@ public class SslUtil {
2220
private static final Logger log = LoggerFactory.getLogger(SslUtil.class);
2321

2422
/**
25-
* Creates an SSLContext for use when connecting to upstream servers. When trustAllServers is true, no upstream certificate
23+
* Creates a netty SslContext for use when connecting to upstream servers. When trustAllServers is true, no upstream certificate
2624
* verification will be performed. <b>This will make it possible for attackers to MITM communications with the upstream
2725
* server</b>, so use trustAllServers only when testing.
2826
*
2927
* @param trustAllServers when true, no upstream server certificate validation will be performed
3028
* @return an SSLContext to connect to upstream servers with
3129
*/
32-
public static SSLContext getUpstreamServerSslContext(boolean trustAllServers) {
30+
public static SslContext getUpstreamServerSslContext(boolean trustAllServers) {
3331
//TODO: add the ability to specify an explicit additional trust source, so clients don't need to import trust into the JDK trust source or forgo trust entirely
3432

35-
try {
36-
if (trustAllServers) {
37-
log.warn("Disabling upstream server certificate verification. This will allow attackers to intercept communications with upstream servers.");
38-
39-
TrustManager[] trustManagers = InsecureTrustManagerFactory.INSTANCE.getTrustManagers();
33+
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
4034

41-
// start with the default SSL context, but override the default TrustManager with the "always trust everything" TrustManager
42-
SSLContext newSslContext = SSLContext.getInstance("TLS");
43-
newSslContext.init(null, trustManagers, null);
35+
if (trustAllServers) {
36+
log.warn("Disabling upstream server certificate verification. This will allow attackers to intercept communications with upstream servers.");
4437

45-
return newSslContext;
46-
} else {
47-
return SSLContext.getDefault();
48-
}
49-
} catch (NoSuchAlgorithmException | KeyManagementException e) {
50-
throw new SslContextInitializationException("Error creating new SSL context for connection to upstream server", e);
38+
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
5139
}
52-
}
5340

54-
/**
55-
* Creates an SSLContext for use with clients' connections to this server. The specified keyManagers should contain
56-
* the impersonated server certificate and private key used to encrypt communications with the client.
57-
*
58-
* @param keyManagers keyManagers that will be used to encrypt communications with the client; should contain the impersonated upstream server certificate
59-
* @return SSLContext for use with clients' connections to this server
60-
*/
61-
public static SSLContext getClientSslContext(KeyManager[] keyManagers) {
6241
try {
63-
SSLContext sslContext = SSLContext.getInstance("TLS");
64-
sslContext.init(keyManagers, null, null);
65-
66-
return sslContext;
67-
} catch (NoSuchAlgorithmException | KeyManagementException e) {
68-
throw new SslContextInitializationException("Error creating new SSL context for connection to client", e);
42+
return sslContextBuilder.build();
43+
} catch (SSLException e) {
44+
throw new SslContextInitializationException("Error creating new SSL context for connection to upstream server", e);
6945
}
70-
71-
7246
}
7347

7448
/**

0 commit comments

Comments
 (0)