Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Ben Michel
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Ben Michel@obensource )

Fixing Connection Failure: Unable To Determine MIME Type Errors With sslCertificateInstall() In Lucee CFML 5.3.3.62

By Ben Nadel on
Tags: ColdFusion

Yesterday, I took a look at tracking feature flags in New Relic Transactions as part of a Lucee CFML application. As part of that write-up, I had a CommandBox Lucee Server hitting my local development environment in order to simulate a steady stream of external HTTP traffic. At first, the traffic simulation was getting Connection Failure errors because my local development environment uses a self-signed certificate. However, I was able to quickly fix this issue by installing the self-signed SSL certificate using the sslCertificateInstall() function in Lucee CFML 5.3.3.62.

To be honest, I don't know all that much about SSL certificates or how HTTPS works. My basic understanding is that when a Client machine makes an HTTPS request to another machine, the two machines have to have a shared public key in order to successfully encrypt and decrypt the traffic. Without this shared key, attempting to use CFHTTP to make HTTPS requests to the target server will result in the following error: "Connection Failure: Unable To Determine MIME Type of File".

CFHTTP error for an HTTPS request: Connection Failure - Unable to determine MIME type of file.

Historically, I've solved this problem by navigating to the secure site in my browser; downloading the SSL certificate via the browser; and then, using keytool to install the SSL certificate locally in whichever JVM I was using. This was laborious and often involved a bunch of trial and error on my part since I don't really know what I'm doing.

Fortunately, Lucee CFML provides a native function, sslCertificateInstall(), which does all of the grunt work for me. Now, I just need to give it a Domain Name and it locates the SSL certificate and installs it locally on the server.

To see this in action, here's the code for the "Traffic Simulator" that I created for my New Relic demo:

<cfscript>

	// This is the local development environment that we'll be hitting via CFHTTP.
	domain = "projects.local.invisionapp.com";

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// When this "traffic simulator" starts to run, the HTTP service won't be able to
	// contact the local development environment since it uses a self-signed certificate.
	// As such, if this is the first run of the traffic simulator, let's install the SSL
	// certificate that is being used by the local development server.
	if ( isNull( url.startedAt ) ) {

		sslCertificateInstall( domain );

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// At this point, we should be OK to start making HTTP requests.
	// --
	// NOTE: I am NOT trying to create load-test here. I'm only trying to create a steady
	// stream of traffic so that I can test some New Relic metrics locally. As such, it
	// doesn't matter that any one request here will block the next one.
	for ( i = 0 ; i < 10 ; i++ ) {

		trafficRequest = new Http(
			method = "GET",
			url = "https://#domain#/d/ben/default"
		);

		trafficResult = trafficRequest.send().getPrefix();

		echo( trafficResult.fileContent & "<br />" );
		cfflush( interval = 1 );
		sleep( randRange( 10, 50 ) );

	}

</cfscript>

<cfoutput>
	<script type="text/javascript">

		// Refresh the page for the next batch of simulated traffic requests.
		window.location.href = "#cgi.script_name#?startedAt=#getTickCount()#";

	</script>
</cfoutput>

This Traffic Simulator is super simple (and is in no way a "load testing" tool); it just makes a couple of HTTPS requests to the local development environment and then refreshes itself. However, on the first run, before it makes any HTTPS requests, it uses the sslCertificateInstall() function download the self-signed SSL certificate and install it locally. This function appears to be safe to call multiple times (for example, if I stop and then restart the traffic simulator).

How cool is that? This is so much easier that manually installing the self-signed SSL certificate with keytool. And, once it is installed, Lucee provides another method, sslCertificateList(), to see which SSL certificates are installed for a given domain. So, after I run my traffic simulator, I can then look to see the state of my CommandBox server:

<cfscript>
	
	// This is the local development environment that we'll be hitting via CFHTTP.
	domain = "projects.local.invisionapp.com";

	// Examine the collection of SSL Certificates installed on THIS server for the given
	// target domain.
	for ( cert in sslCertificateList( domain ) ) {

		dump( cert );

	}

</cfscript>

Running this ColdFusion code gives us:

List of installed SSL certificates via sslCertificateList() in Lucee CFML 5.3.3.62.

As you can see, the self-signed SSL certificate from my local development environment has been persisted to my CommandBox Lucee server.

From my [mostly naive] understanding, I believe that servers usually come with a set of pre-installed public keys for the most common SSL Certificate Authorities / providers. As such, you can usually make an HTTPS request over SSL using ColdFusion's CFHTTP service without any problem. However, if the SSL Certificate in question is self-signed, or uses a non-standard public key, then you will get the aforementioned "Connection Failure" error. Thankfully, this is no longer an issue with the sslCertificateInstall() function in Lucee CFML.



Reader Comments

OK. This looks super awesome.

I used to have a problem with PayPal, when using their API. So, I would make a CFHTTP request to a PayPal REST endpoint and I would get a similar error. My solution, as I remember, at the time, was to install PayPal's latest security certificate in Lucee's JRE keystore, using CF Administrator.

Maybe, your solution will take care of stuff like this?

I am about to transfer one of my old VPS, running Windows 2008R2 [yes, don't laugh...] & Lucee 4.5, to a new VPS.

I am looking forward to installing the latest version of Lucee, at last, so that I can try out all these new features!

Reply to this Comment

@Charles,

Yeah, I used to do the same kind of thing. I honestly didn't even realize you could install SSL certs in the CF Administrator. I think maybe that is a relatively recent feature (otherwise I just totally missed it). So, I used to have to do it at the command-line in the ColdFusion server.

The trick with this approach will be figuring out when to do it, since you definitely don't want to have to be doing it constantly. I'm not sure on this part yet. Perhaps you could do it in a try/catch or something, and check the error. Or, maybe on server-start? Not really sure what the good move is.

Reply to this Comment

Actually that's interesting. When to do it?

It might be useful in the context of a schedule task.
So, you could loop through a list of providers on a daily basis and update those that aren't already installed. Obviously, someone would need to keep the provider list up to date! The only problem here, is working out how to keep the provider list, up to date? When I got the PayPal API error, I had to do some pretty in depth research to find out which certificate I needed to update! But, maybe some APIs provide an API method to read the name of the latest certificate required? So you could call this method first and compare it with the list of certificates, already installed.

I am fairly sure this is a Client SSL certificate tool. I have manually installed Server SSL Certificates, on my VPS, and this is quite a lengthy process, requiring a CSR stage, followed by a Windows MMC snap-in stage. It would be totally awesome if Lucee was able to automate this process, but I am fairly sure, this isn't possible.

And, as for when this feature was available in Lucee CF Administrator. I am running Lucee 4.5, so it has certainly been available since that version!

Anyway, it sure is a cool feature!

Reply to this Comment

@Charles,

In my personal life, I used "managed hosting", so having someone install SSL certs for me is part of the cost. And, at work, we have Ops people who deal with all the SSL stuff. So, I literally know nothing about installing SSL certs :P

Well, that's not entirely fair - I have played a tiny with the Let's Encrypt "cert bot" that does the provisioning of free certificates. But, that was a lot of trial-and-error for me.

Using a scheduled task is an interesting idea - as long as you can get it to run prior to having to use it first. This also makes me think about what happens when SSL certs expire -- will sslCertificateInstall() install the new SSL cert over the expired one? Now I'm really out of my depth.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.