You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ra...@apache.org on 2019/04/10 16:05:31 UTC

[trafficcontrol] branch master updated: Tr default cert (#3392)

This is an automated email from the ASF dual-hosted git repository.

rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 8900fcc  Tr default cert (#3392)
8900fcc is described below

commit 8900fccac5a1a28b0f254c6c1089e7c308c0d076
Author: Andy Schmidt <za...@gmail.com>
AuthorDate: Wed Apr 10 10:05:26 2019 -0600

    Tr default cert (#3392)
    
    * Initial commit of default cert changes
    
    * Changed error message for Certificate Expired
    
    * corrections from PR Review
    
    * Added CertificateExtensions to default SSL cert
    
    * Added fix for Tomcat bug related to error state of sockets
    Improved descriptions in the default self-signed cert
    Improved warning messages in x509 certificate validation
    
    * fixed some spacing and made getInstance synchronized
    
    * fixed some spacing and made getInstance synchronized
    
    * added an if condition to 'getInstance' to prevent double initialization
    
    * Moved default certificate lifecycle into `importCertificateDataList`
    
    * Reformatted `RouterNIOEndpoint`
---
 CHANGELOG.md                                       |   3 +
 .../traffic_router/protocol/RouterNioEndpoint.java | 186 +++++++++++----------
 .../secure/CertificateDataConverter.java           |  16 +-
 .../traffic_router/secure/CertificateRegistry.java |  73 +++++++-
 .../test/java/secure/CertificateRegistryTest.java  |   3 +-
 .../traffic_router/core/external/RouterTest.java   |  32 ++--
 .../api/1.3/cdns/name/thecdn/sslkeys.json          |   8 +-
 7 files changed, 201 insertions(+), 120 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e732d6..24d1ebc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
 ### Added
+- Traffic Router: TR now generates a self-signed certificate at startup and uses it as the default TLS cert. 
+  The default certificate is used whenever a client attempts an SSL handshake for an SNI host which does not match
+  any of the other certificates. 
 - Traffic Ops Golang Endpoints
   - /api/1.4/users `(GET,POST,PUT)`
   - /api/1.1/deliveryservices/xmlId/:xmlid/sslkeys `GET`
diff --git a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/protocol/RouterNioEndpoint.java b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/protocol/RouterNioEndpoint.java
index 85f8c07..6fe6399 100644
--- a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/protocol/RouterNioEndpoint.java
+++ b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/protocol/RouterNioEndpoint.java
@@ -19,103 +19,105 @@ import com.comcast.cdn.traffic_control.traffic_router.secure.CertificateRegistry
 import com.comcast.cdn.traffic_control.traffic_router.secure.HandshakeData;
 import com.comcast.cdn.traffic_control.traffic_router.secure.KeyManager;
 import org.apache.log4j.Logger;
-import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.util.net.NioChannel;
 import org.apache.tomcat.util.net.NioEndpoint;
 import org.apache.tomcat.util.net.SSLHostConfig;
 import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketProcessorBase;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
 import java.util.Map;
 import java.util.Set;
 
 public class RouterNioEndpoint extends NioEndpoint {
-    private static final Logger LOGGER = Logger.getLogger(RouterNioEndpoint.class);
-    // Grabs the aliases from our custom certificate registry, creates a sslHostConfig for them
-    // and adds the newly created config to the list of sslHostConfigs.  We also remove the default config
-    // since it won't be found in our registry.  This allows OpenSSL to start successfully and serve our
-    // certificates.  When we are done we call the parent classes initialiseSsl.
-    @SuppressWarnings({"PMD.SignatureDeclareThrowsException"})
-    @Override
-    protected void initialiseSsl() throws Exception {
-        if (isSSLEnabled()) {
-            destroySsl();
-            sslHostConfigs.clear();
-            final KeyManager keyManager = new KeyManager();
-            final CertificateRegistry certificateRegistry =  keyManager.getCertificateRegistry();
-            replaceSSLHosts(certificateRegistry.getHandshakeData());
-
-            //Now let initialiseSsl do it's thing.
-            super.initialiseSsl();
-            certificateRegistry.setEndPoint(this);
-        }
-    }
-
-    synchronized private void replaceSSLHosts(final Map<String, HandshakeData> sslHostsData) {
-        final Set<String> aliases = sslHostsData.keySet();
-        boolean firstAlias = true;
-        String lastHostName = "";
-
-        for (final String alias : aliases) {
-            final SSLHostConfig sslHostConfig = new SSLHostConfig();
-            final SSLHostConfigCertificate cert = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.RSA);
-            cert.setCertificateKeyAlias(alias);
-            sslHostConfig.addCertificate(cert);
-            sslHostConfig.setCertificateKeyAlias(alias);
-            sslHostConfig.setHostName(sslHostsData.get(alias).getHostname());
-            sslHostConfig.setProtocols("all");
-            sslHostConfig.setConfigType(getSslConfigType());
-            sslHostConfig.setCertificateVerification("none");
-            LOGGER.info("sslHostConfig: "+sslHostConfig.getHostName()+" "+sslHostConfig.getTruststoreAlgorithm());
-
-            if (!sslHostConfig.getHostName().equals(lastHostName)) {
-                addSslHostConfig(sslHostConfig, true);
-                lastHostName = sslHostConfig.getHostName();
-            }
-
-            if (firstAlias && ! "".equals(alias)) {
-                // One of the configs must be set as the default
-                setDefaultSSLHostConfigName(sslHostConfig.getHostName());
-                firstAlias = false;
-            }
-        }
-
-    }
-
-    synchronized public void reloadSSLHosts(final Map<String, HandshakeData> cr) {
-        replaceSSLHosts(cr);
-
-        for (final HandshakeData data : cr.values()) {
-            final SSLHostConfig sslHostConfig = sslHostConfigs.get(data.getHostname());
-            sslHostConfig.setConfigType(getSslConfigType());
-            createSSLContext(sslHostConfig);
-        }
-    }
-
-    @Override
-    protected SSLHostConfig getSSLHostConfig(final String sniHostName) {
-        return super.getSSLHostConfig(sniHostName.toLowerCase());
-    }
-
-    private void unregisterJmx(final SSLHostConfig sslHostConfig) {
-        final Registry registry = Registry.getRegistry(null, null);
-        registry.unregisterComponent(sslHostConfig.getObjectName());
-        for (final SSLHostConfigCertificate sslHostConfigCert : sslHostConfig.getCertificates()) {
-            registry.unregisterComponent(sslHostConfigCert.getObjectName());
-        }
-    }
-
-    @Override
-    public void addSslHostConfig(final SSLHostConfig sslHostConfig, final boolean replace) throws IllegalArgumentException {
-        final String key = sslHostConfig.getHostName();
-        if (key == null || key.length() == 0) {
-            throw new IllegalArgumentException(sm.getString("endpoint.noSslHostName"));
-        }
-
-        SSLHostConfig previous = null;
-        if (replace) {
-            previous = sslHostConfigs.get(key);
-        }
-        super.addSslHostConfig(sslHostConfig, replace);
-        if (previous != null) {
-            unregisterJmx(previous);
-        }
-    }
+	private static final Logger LOGGER = Logger.getLogger(RouterNioEndpoint.class);
+
+	// Grabs the aliases from our custom certificate registry, creates a sslHostConfig for them
+	// and adds the newly created config to the list of sslHostConfigs.  We also remove the default config
+	// since it won't be found in our registry.  This allows OpenSSL to start successfully and serve our
+	// certificates.  When we are done we call the parent classes initialiseSsl.
+	@SuppressWarnings({"PMD.SignatureDeclareThrowsException"})
+	@Override
+	protected void initialiseSsl() throws Exception{
+		if (isSSLEnabled()){
+			destroySsl();
+			sslHostConfigs.clear();
+			final KeyManager keyManager = new KeyManager();
+			final CertificateRegistry certificateRegistry = keyManager.getCertificateRegistry();
+			replaceSSLHosts(certificateRegistry.getHandshakeData());
+
+			//Now let initialiseSsl do it's thing.
+			super.initialiseSsl();
+			certificateRegistry.setEndPoint(this);
+		}
+	}
+
+	synchronized public void replaceSSLHosts(final Map<String, HandshakeData> sslHostsData){
+		final Set<String> aliases = sslHostsData.keySet();
+		String lastHostName = "";
+
+		for (final String alias : aliases){
+			final SSLHostConfig sslHostConfig = new SSLHostConfig();
+			final SSLHostConfigCertificate cert = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.RSA);
+			cert.setCertificateKeyAlias(alias);
+			sslHostConfig.addCertificate(cert);
+			sslHostConfig.setCertificateKeyAlias(alias);
+			sslHostConfig.setHostName(sslHostsData.get(alias).getHostname());
+			sslHostConfig.setProtocols("all");
+			sslHostConfig.setCertificateVerification("none");
+			LOGGER.info("sslHostConfig: " + sslHostConfig.getHostName() + " " + sslHostConfig.getTruststoreAlgorithm());
+
+			if (!sslHostConfig.getHostName().equals(lastHostName)){
+				addSslHostConfig(sslHostConfig, true);
+				lastHostName = sslHostConfig.getHostName();
+			}
+
+			if (CertificateRegistry.DEFAULT_SSL_KEY.equals(alias)){
+				// One of the configs must be set as the default
+				setDefaultSSLHostConfigName(sslHostConfig.getHostName());
+			}
+		}
+	}
+
+	@Override
+	protected SSLHostConfig getSSLHostConfig(final String sniHostName){
+		return super.getSSLHostConfig(sniHostName.toLowerCase());
+	}
+
+	@Override
+	protected SocketProcessorBase<NioChannel> createSocketProcessor(
+			final SocketWrapperBase<NioChannel> socketWrapper, final SocketEvent event){
+		return new RouterSocketProcessor(socketWrapper, event);
+	}
+
+	/**
+	 * This class is the equivalent of the Worker, but will simply use in an
+	 * external Executor thread pool.
+	 */
+	protected class RouterSocketProcessor extends SocketProcessor {
+
+		public RouterSocketProcessor(final SocketWrapperBase<NioChannel> socketWrapper, final SocketEvent event){
+			super(socketWrapper, event);
+		}
+
+		/* This override has been added as a temporary hack to resolve an issue in Tomcat.
+		Once the issue has been corrected in Tomcat then this can be removed. The
+		'SSL.getLastErrorNumber()' removes an unwanted error condition from the error stack
+		in those cases where some error condition has caused the socket to get closed and
+		then the processor was put back on the processor stack for reuse in a future connection.
+		*/
+		@Override
+		protected void doRun(){
+			final SocketWrapperBase<NioChannel> localWrapper = socketWrapper;
+			final NioChannel socket = localWrapper.getSocket();
+			super.doRun();
+			if (!socket.isOpen()){
+				SSL.getLastErrorNumber();
+			}
+		}
+	}
+
 }
+
diff --git a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateDataConverter.java b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateDataConverter.java
index b3990fb..cd1e48b 100644
--- a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateDataConverter.java
+++ b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateDataConverter.java
@@ -64,18 +64,20 @@ public class CertificateDataConverter {
 				log.warn("Service name doesn't match the subject of the certificate = "+certificateData.getHostname());
 			}
 			else if (!modMatch) {
-				log.error("Modulus of the private key does not match the public key modulus for certificate host: "+certificateData.getHostname());
+				log.warn("Modulus of the private key does not match the public key modulus for certificate host: "+certificateData.getHostname());
 			}
 
 		} catch (CertificateNotYetValidException er) {
-			log.error("Failed to convert certificate data for delivery service = " + certificateData.getHostname()
-							+ ", because the certificate is not valid yet. ");
+			log.warn("Failed to convert certificate data for delivery service = " + certificateData.getHostname()
+							+ ", because the certificate is not valid yet. This certificate will not be used by " +
+					"Traffic Router.");
 		} catch (CertificateExpiredException ex ) {
-			log.error("Failed to convert certificate data for delivery service = " + certificateData.getHostname()
-					+ ", because the certificate has expired. ");
+			log.warn("Failed to convert certificate data for delivery service = " + certificateData.getHostname()
+					+ ", because the certificate has expired. This certificate will not be used by Traffic Router.");
 		} catch (Exception e) {
-			log.error("Failed to convert certificate data (delivery service = " + certificateData.getDeliveryservice()
-					+ ", hostname = " + certificateData.getHostname() + ") from traffic ops to handshake data! "
+			log.warn("Failed to convert certificate data (delivery service = " + certificateData.getDeliveryservice()
+					+ ", hostname = " + certificateData.getHostname() + ") from traffic ops to handshake data! This " +
+					"certificate will not be used by Traffic Router. "
 					+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
 		}
 		return null;
diff --git a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateRegistry.java b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateRegistry.java
index 848c8b0..e098418 100644
--- a/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateRegistry.java
+++ b/traffic_router/connector/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/CertificateRegistry.java
@@ -18,13 +18,26 @@ package com.comcast.cdn.traffic_control.traffic_router.secure;
 import com.comcast.cdn.traffic_control.traffic_router.protocol.RouterNioEndpoint;
 import com.comcast.cdn.traffic_control.traffic_router.shared.CertificateData;
 import org.apache.log4j.Logger;
-
+import sun.security.tools.keytool.CertAndKeyGen;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.BasicConstraintsExtension;
+import sun.security.x509.CertificateExtensions;
+import sun.security.x509.ExtendedKeyUsageExtension;
+import sun.security.x509.KeyUsageExtension;
+
+import java.security.PrivateKey;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Vector;
+
+import sun.security.x509.X500Name;
+import java.security.cert.X509Certificate;
+import java.util.Date;
 
 public class CertificateRegistry {
+	public static final String DEFAULT_SSL_KEY = "default.invalid";
 	private static final Logger log = Logger.getLogger(CertificateRegistry.class);
 	private CertificateDataConverter certificateDataConverter = new CertificateDataConverter();
 	volatile private Map<String, HandshakeData>	handshakeDataMap = new HashMap<>();
@@ -40,6 +53,41 @@ public class CertificateRegistry {
 		return CertificateRegistryHolder.DELIVERY_SERVICE_CERTIFICATES;
 	}
 
+	@SuppressWarnings("PMD.UseArrayListInsteadOfVector")
+	private static HandshakeData createDefaultSsl() {
+		try {
+			final CertificateExtensions extensions = new CertificateExtensions();
+			final KeyUsageExtension keyUsageExtension = new KeyUsageExtension();
+			keyUsageExtension.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);
+			keyUsageExtension.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);
+			keyUsageExtension.set(KeyUsageExtension.KEY_CERTSIGN, true);
+			extensions.set(keyUsageExtension.getExtensionId().toString(), keyUsageExtension);
+			final Vector<ObjectIdentifier> objectIdentifiers = new Vector<>();
+			objectIdentifiers.add(new ObjectIdentifier("1.3.6.1.5.5.7.3.1"));
+			objectIdentifiers.add(new ObjectIdentifier("1.3.6.1.5.5.7.3.2"));
+			final ExtendedKeyUsageExtension extendedKeyUsageExtension = new ExtendedKeyUsageExtension( true,
+					objectIdentifiers);
+			extensions.set(extendedKeyUsageExtension.getExtensionId().toString(), extendedKeyUsageExtension);
+			extensions.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(true,
+					new BasicConstraintsExtension(true,-1).getExtensionValue()));
+			final CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA1WithRSA", null);
+			certGen.generate(2048);
+
+			//Generate self signed certificate
+			final X509Certificate[] chain = new X509Certificate[1];
+			chain[0] = certGen.getSelfCertificate(new X500Name("C=US; ST=CO; L=Denver; " +
+					"O=Apache Traffic Control; OU=Apache Foundation; OU=Hosted by Traffic Control; " +
+					"OU=CDNDefault; CN="+DEFAULT_SSL_KEY), new Date(System.currentTimeMillis() - 1000L * 60 ),
+					(long) 3 * 365 * 24 * 3600, extensions);
+			final PrivateKey serverPrivateKey = certGen.getPrivateKey();
+			return new HandshakeData(DEFAULT_SSL_KEY, DEFAULT_SSL_KEY, chain, serverPrivateKey);
+		}
+		catch (Exception e) {
+			log.error("Could not generate the default certificate: "+e.getMessage(),e);
+			return null;
+		}
+	}
+
 	public List<String> getAliases() {
 		return new ArrayList<>(handshakeDataMap.keySet());
 	}
@@ -70,7 +118,6 @@ public class CertificateRegistry {
 		for (final CertificateData certificateData : certificateDataList) {
 			try {
 				final String alias = certificateData.alias();
-
 				if (!master.containsKey(alias)) {
 					final HandshakeData handshakeData = certificateDataConverter.toHandshakeData(certificateData);
 					if (handshakeData != null) {
@@ -89,7 +136,6 @@ public class CertificateRegistry {
 				log.error("Failed to import certificate data for delivery service: '" + certificateData.getDeliveryservice() + "', hostname: '" + certificateData.getHostname() + "'");
 			}
 		}
-
 		// find CertificateData which has been removed
 		for (final String alias : previousData.keySet())
 		{
@@ -110,12 +156,27 @@ public class CertificateRegistry {
 			}
 		}
 
+		// Check to see if a Default cert has been provided by Traffic Ops
+		if (!master.containsKey(DEFAULT_SSL_KEY)){
+			// Check to see if a Default cert has been provided/created previously
+			if (handshakeDataMap.containsKey(DEFAULT_SSL_KEY)) {
+				master.put(DEFAULT_SSL_KEY, handshakeDataMap.get(DEFAULT_SSL_KEY));
+			}else{
+				// create a new default certificate
+				final HandshakeData defaultHd = createDefaultSsl();
+				if (defaultHd == null){
+					log.error("Failed to initialize the CertificateRegistry because of a problem with the 'default' " +
+							"certificate.  Returning the Certificate Registry without a default.");
+					return;
+				}
+				master.put(DEFAULT_SSL_KEY, defaultHd);
+			}
+		}
 		handshakeDataMap = master;
 
 		if (sslEndpoint != null) {
-			sslEndpoint.reloadSSLHosts(changes);
+			sslEndpoint.replaceSSLHosts(changes);
 		}
-
 	}
 
 	public CertificateDataConverter getCertificateDataConverter() {
diff --git a/traffic_router/connector/src/test/java/secure/CertificateRegistryTest.java b/traffic_router/connector/src/test/java/secure/CertificateRegistryTest.java
index d01da22..01119bb 100644
--- a/traffic_router/connector/src/test/java/secure/CertificateRegistryTest.java
+++ b/traffic_router/connector/src/test/java/secure/CertificateRegistryTest.java
@@ -87,6 +87,7 @@ public class CertificateRegistryTest {
 		verify(certificateDataConverter).toHandshakeData(certificateData3);
 
 		assertThat(certificateRegistry.getAliases(),
-			containsInAnyOrder("ds-1.some-cdn.example.com", "ds-2.some-cdn.example.com", "ds-3.some-cdn.example.com"));
+			containsInAnyOrder(CertificateRegistry.DEFAULT_SSL_KEY, "ds-1.some-cdn.example.com",
+					"ds-2.some-cdn.example.com", "ds-3.some-cdn.example.com"));
 	}
 }
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
index 452e4fa..2c6efab 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
@@ -27,6 +27,7 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.ssl.SSLContextBuilder;
@@ -217,8 +218,6 @@ public class RouterTest {
 		InputStream keystoreStream = getClass().getClassLoader().getResourceAsStream("keystore.jks");
 		trustStore.load(keystoreStream, "changeit".toCharArray());
 		TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).init(trustStore);
-
-
 		httpClient = HttpClientBuilder.create()
 			.setSSLSocketFactory(new ClientSslSocketFactory("tr.https-only-test.thecdn.example.com"))
 			.setSSLHostnameVerifier(new TestHostnameVerifier())
@@ -438,9 +437,12 @@ public class RouterTest {
 
 		try (CloseableHttpResponse response = httpClient.execute(httpGet)){
 			int code = response.getStatusLine().getStatusCode();
-			assertThat("Expected to get an ssl handshake error! But got: "+code,
+			assertThat("Expected a server error code (503) But got: "+code,
 					code, greaterThan(500));
 		}
+		catch (SSLHandshakeException she) {
+			// Expected result of getting the self-signed _default_ certificate
+		}
 
 		// Pretend someone did a cr-config snapshot that would have updated the location to be different
 		HttpPost httpPost = new HttpPost("http://localhost:" + testHttpPort + "/crconfig-2");
@@ -494,9 +496,12 @@ public class RouterTest {
 
 		try (CloseableHttpResponse response = httpClient.execute(httpGet)){
 			int code = response.getStatusLine().getStatusCode();
-			assertThat("Expected to get an ssl handshake error! But got: "+code,
+			assertThat("Expected an server error code! But got: "+code,
 					code, greaterThan(500));
 		}
+		catch (SSLHandshakeException she) {
+			// expected result of getting the self-signed _default_ certificate
+		}
 
 		// Go back to the cr-config that makes the delivery service https again
 		// Pretend someone did a cr-config snapshot that would have updated the location to be different
@@ -511,6 +516,11 @@ public class RouterTest {
 		httpPost = new HttpPost("http://localhost:"+ testHttpPort + "/certificates");
 		httpClient.execute(httpPost).close();
 
+		httpClient = HttpClientBuilder.create()
+				.setSSLSocketFactory(new ClientSslSocketFactory("https-additional"))
+				.setSSLHostnameVerifier(new TestHostnameVerifier())
+				.disableRedirectHandling()
+				.build();
 		// Our initial test cr config data sets cert poller to 10 seconds
 		Thread.sleep(25000L);
 
@@ -518,10 +528,13 @@ public class RouterTest {
 		httpGet.addHeader("Host", "tr." + "https-additional" + ".bar");
 
 		try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
-			assertThat(response.getStatusLine().getStatusCode(), equalTo(302));
-	    } catch (SSLHandshakeException e) {
-		// Expected, this means we're doing the right thing
-	    }
+			int code = response.getStatusLine().getStatusCode();
+			assertThat("Expected an server error code! But got: "+code,
+					code, equalTo(302));
+		} catch (SSLHandshakeException e) {
+
+			fail(e.getMessage());
+		}
 
 		httpGet = new HttpGet("https://localhost:" + routerSecurePort + "/stuff?fakeClientIpAddress=12.34.56.78");
 		httpGet.addHeader("Host", "tr." + httpsNoCertsId + ".bar");
@@ -598,7 +611,7 @@ public class RouterTest {
 		private final String host;
 
 		public ClientSslSocketFactory(String host) throws Exception {
-			super(SSLContextBuilder.create().loadTrustMaterial(trustStore, null).build(),
+			super(SSLContextBuilder.create().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(),
 				new TestHostnameVerifier());
 			this.host = host;
 		}
@@ -607,7 +620,6 @@ public class RouterTest {
 			SNIHostName serverName = new SNIHostName(host);
 			List<SNIServerName> serverNames = new ArrayList<>(1);
 			serverNames.add(serverName);
-
 			SSLParameters params = sslSocket.getSSLParameters();
 			params.setServerNames(serverNames);
 			sslSocket.setSSLParameters(params);
diff --git a/traffic_router/core/src/test/resources/api/1.3/cdns/name/thecdn/sslkeys.json b/traffic_router/core/src/test/resources/api/1.3/cdns/name/thecdn/sslkeys.json
index c5a9b3d..f987f6e 100644
--- a/traffic_router/core/src/test/resources/api/1.3/cdns/name/thecdn/sslkeys.json
+++ b/traffic_router/core/src/test/resources/api/1.3/cdns/name/thecdn/sslkeys.json
@@ -4,8 +4,8 @@
       "deliveryservice": "https-nocert",
       "certificate": {
         "comment" : "The following is just a self signed certificate and key to use for testing this is NOT private data from a CA",
-        "key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBMTNESjAybUhY\nUHUyOEh0aEg0UzhVNlpIUjNLRmNmc25hSDZzTllEeWxEVkdTZFFoClhyejJYS3dzWVFzeC9lVTFu\nKzRKZTNHazhNZmJ1cnMzYjVyNWdwNTBvQloxMzNEczM2U0RBTyt6ejdMMHY1TEYKUGJPeHpSaHZq\nRlZJZFZramdzazRUUjFUYVlQNzBMT2p3b1ZkRExNb0RmNXRmZEMvYzFIdzVkd09ibFBHWkMrWApF\nTzREUG12azR3TmwweGFXVDdJbXN5ckJJU01ueUhBUElYQkwyREFHc0pYVXBvQjVhckNnbVNDNU41\nOTQ3Y0VpCjJRY2tIaDBWdWJlOUM5VytNbVUvT1VQTWc3TnVTTk55anlsZldzV2poSUZtUXBZUmo5\nWnppTGYx [...]
-        "crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZsekNDQTMrZ0F3SUJBZ0lDRUFNd0RRWUpL\nb1pJaHZjTkFRRUxCUUF3WkRFTE1Ba0dBMVVFQmhNQ1ZWTXgKRVRBUEJnTlZCQWdNQ0VOdmJHOXlZ\nV1J2TVJBd0RnWURWUVFLREFkRGIyMWpZWE4wTVE0d0RBWURWUVFMREFWSgpVRU5FVGpFZ01CNEdB\nMVVFQXd3WFZHVnpkR2x1WnlCSmJuUmxjbTFsWkdsaGRHVWdRMEV3SGhjTk1UWXdPVEl6Ck1qSTFN\nVE0wV2hjTk16VXhNVEl6TWpJMU1UTTBXakIvTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNC\nTUkKUTI5c2IzSmhaRzh4RHpBTkJnTlZCQWNUQmtSbGJuWmxjakVRTUE0R0ExVUVDaE1IUTI5dFky\nRnpkREVP [...]
+        "key": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzI0b0NxaW1QUy8yZ0gKUUlVRHd5ODRleUNrTXlTeSt6YWhTNEpSOHJaeE9MbEpkZVBHVFZBU0dsandYb2NTV2tvTkFvWGtvN05GVmdEUApjVC9uR1ZZVVQzSEE0N25zcUFBUGlCMEtWUlh0dmtiRk9XL1hzV1d3ZE1mc2psbStsWEl3c1NLTnB4b3NWb28vCkFsbHBEbUU1QmJsQlR0aER0M1libTZGYW9Ud0NSemRGVndqM3Nud0pzRDhHdUMxa0VqMGViY0p3RTY2d0xhRjkKWU14RmZrQk9Ib3NCa2xic0tQWlhJM3J3eVpPYXBlZXhKUlphSC9ZeVJVRnlSdXMvUTRWUlJ4VGVteEh5cG4yKwo0N1NQ [...]
+        "crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURZakNDQWtvQ0NRRG4va0YwcGszc2F6QU5CZ2txaGtpRzl3MEJBUXNGQURCeU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwOHhEREFLQmdOVkJBY01BMFJGVGpFUE1BMEdBMVVFQ2d3R1FYQmhZMmhsTVFzdwpDUVlEVlFRTERBSlVRekVxTUNnR0ExVUVBd3doS2k1b2RIUndjeTF1YjJObGNuUXVkR2hsWTJSdUxtVjRZVzF3CmJHVXVZMjl0TUNBWERURTVNRE13TnpBd05URXlOVm9ZRHpJeE1Ua3dNakV4TURBMU1USTFXakJ5TVFzd0NRWUQKVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEREQUtCZ05WQkFjTUEwUkZUakVQTUEwR0ExVUVDZ3dHUVhCaApZMmhs [...]
       },
       "hostname": "*.https-nocert.thecdn.example.com"
     },
@@ -40,8 +40,8 @@
       "deliveryservice": "https-additional",
       "certificate": {
         "comment" : "The following is just a self signed certificate and key to use for testing this is NOT private data from a CA",
-        "key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBc2Y0NnV5OGJ2\nQk5rMGhCaEVsbHdGT0dqREh6M1hJY1hteDRVNThNZG9Fa1JId0VTCjVONnd3NFV6bDAvRDcyMlJV\nODlMeHB4bldvclJmdVZNQldnOGVFcXBUb2NUS2NOZHhtZmdEUWZTcTZ1ODNTWkUKTmFCZFArK2g5\nYTJJRFZXWGFldVRhcVA3Q3lVVG52Sld5Mm1JalJWZkRGQWRWWHNhU1M4RGRYUWdibEJTelJ6NwpL\nMXFHVWt1RlZQc0R0ODZBYVF3TnN5R2ZDN3ltcUkzNU1FQ3hTdzNPd2lXSlAyZTg3U2E5UG9Pdjcr\nZUs2NVJnCmM3dzNkSXQxZUlyS3B6OWpQV1RPTkJOK0JhWFdvcHNXZ3UvdVd4Q1pnUk9qaXBWVUFK\nNHhrNFRG [...]
-        "crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZuakNDQTRhZ0F3SUJBZ0lDRUFFd0RRWUpL\nb1pJaHZjTkFRRUxCUUF3WkRFTE1Ba0dBMVVFQmhNQ1ZWTXgKRVRBUEJnTlZCQWdNQ0VOdmJHOXlZ\nV1J2TVJBd0RnWURWUVFLREFkRGIyMWpZWE4wTVE0d0RBWURWUVFMREFWSgpVRU5FVGpFZ01CNEdB\nMVVFQXd3WFZHVnpkR2x1WnlCSmJuUmxjbTFsWkdsaGRHVWdRMEV3SGhjTk1UWXdPVEl6Ck1qSXpO\nREl4V2hjTk16VXhNVEl6TWpJek5ESXhXakNCaFRFTE1Ba0dBMVVFQmhNQ1ZWTXhFVEFQQmdOVkJB\nZ1QKQ0VOdmJHOXlZV1J2TVE4d0RRWURWUVFIRXdaRVpXNTJaWEl4RURBT0JnTlZCQW9UQjBOdmJX\nTmhjM1F4 [...]
+        "key": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRREJnK2VuSnRsTkUxMTgKR0tFWGFpM1A0UW5uNnZGMG1vZ3pHOWNrZWEyQTBTYlVmeDBKMFF3TWptaGE0Z3pmNkp3bnkyRDBBSDBVUjgrYwozS0g0ZGJmVnJKL0Uzcjk5ZjZuQmlZaHlxN3hGSzlQQ1BnK05LTEdZZHBzMGhLd0FOelhGOW16dGl3NmZ2d3RBCi9IMy9vV2tlMktTQkdQL2R0NmlSQk92WmQrR0cwMVRDWDZRTmM1OTVVMEc0SEE2R1l1K09lbDVvR3NVOFYxTTIKV2tVcWlTVnRuQjJWM1NJdDRyNERSVXBMYmZqeEFON2hMTGZYQUxraTN5WDZmUXJDUG9iaXJmSFAzU2cvaklweApQMVZM [...]
+        "crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURwakNDQW80Q0NRRElERFk4N1lDM2JEQU5CZ2txaGtpRzl3MEJBUXNGQURDQmt6RUxNQWtHQTFVRUJoTUMKVlZNeEN6QUpCZ05WQkFnTUFrTlBNUXd3Q2dZRFZRUUhEQU5FUlU0eER6QU5CZ05WQkFvTUJrRndZV05vWlRFTApNQWtHQTFVRUN3d0NWRU14TFRBckJnTlZCQU1NSkNvdWFIUjBjQzFoWkdScGRHbHZibUZzTG5Sb1pXTmtiaTVsCmVHRnRjR3hsTG1OdmJURWNNQm9HQ1NxR1NJYjNEUUVKQVJZTmRHTkFZWEJoWTJobExtOXlaekFnRncweE9UQXoKTVRneU1UQXpORFphR0E4eU1URTVNREl5TWpJeE1ETTBObG93Z1pNeEN6QUpCZ05WQkFZVEFsVlRNUXN3Q1FZRApWUVFJ [...]
       },
       "hostname": "*.http-additional.thecdn.example.com"
     }