VB.NET Tips / Tricks / Examples and Help

SSL testing certificate and the .NET Framework

I was faced with an odd problem today. I needed to test an update for an application that is already in production. Normally this would be pretty simple to do in a staging environment of some sort, but that is what made this task different. There was none.

Basically what I was given was an application to install (no source code) and information on where it downloads its updates from. It did the downloading using a WebClient and calls over HTTPS to a directory on an IIS webserver that doesn't have anonymous access on. So the WebClient passes credentials as well over HTTPS to authenticate. The URLS were hard coded in the app, so I had no way to redirect them somewhere other than the live server, and that was what made this problem interesting.

I had to get a little clever and get in MacGyver mode since this was going to take a good amount of mucking around with the system itself. Since this was just for testing, I didn't mind screwing up the testbox, which was just a virtual machine anyway.

My first issue was how to I redirect hard coded URLS that point to https://www.somedomain.com/ to my local IIS server where I had a local mirror of the update site's contents. I ended up using the HOSTS file in Windows to accomplish this. Basically if you don't know what the HOSTS file is, it is a file (located at %systemroot%\system32\drivers\etc\ on most Windows versions) that is a list of manual DNS resolutions at the machine level. Opening it will show you a single DNS resolution for localhost, which points to the system loopback of 127.0.0.1.

I added an entry for the customers domain and the IP address of my local IIS server so it looked something like this:
192.168.1.145 www.somedomain.com

This made the testbox redirect to my local IIS IP address whenever a request was made for the actual domain.

Ok so problem solved with redirecting the traffic, however at this point I was not aware that the calls were made over HTTPS. I soon found that out as the tests were failing because my local IIS had no SSL installed on it.

Looking around for a way to make a testing certificate sounded like it was going to be somewhat of a process, even using some free tools like OpenSSL to create a self signed certificate for my testing purposes. Luckily I found a free download from Microsoft IIS resource kit (thanks to a tip from Maurice de Beijer). It is geared towards IIS6, however it will install and at least do what is need for this task at least on XP Professional with IIS 5.1, which is what I was using for my testing server.

Installing this resource kit gives you an entry in your start menu for IIS Resources -> SelfSSL.

This little command line utility will easily create and install a self signed certificate into IIS on the machine.

So all done right? Well not exactly, this is where things got even more complicated.

So over to my testbox, I was able to connect to my local IIS when I type their domain in my browser due to the HOSTS file modification, however any requests for HTTPS versus HTTP would make IE yell foul that the cert doesn't match the domain, and also it was not trusted or signed by a CA, which of course is true, it was signed by me Smile.

To fix this for IE I just needed to go into advanced options and uncheck the box to "warn about certificate address mismatch". I also needed to continue through the IE warning and view the site. I clicked on the red cert error box once I was on the site, and selected to install the certificate to the machine. After doing these 2 steps, Internet Explorer worked just as it would if it was a legit signed cert from a legit CA. I imagine an alternative to this method would be to export the cert to a .cer file from the IIS box, and then copy it over to the testbox and import it manually using the certificates MMC snapin.

So all done right? Nope.

So at this point I figured I was all done and was patting myself on the back for a job well done. Until I tried to actually run their app and deliver the update to it from my local IIS server. It just wouldn't connect, and while it didn't crash because of the exception handling in the application, it simply acted as if it could not find the internet site it was trying to.

Examining some log files showed me the error was specifically this:
"The underlying connection is closed: Could not establish trust relationship for the ssl/tls secure channel"

With an inner exception message of:
"The remote certificate is invalid according to the validation procedure"

At this point I tried everything. I imported the self signed certificate into every possible store I could, I added the MMC snapin for certificates and added the cert to all levels in there (machine and user level). Simply nothing worked at all, I always would get the same error.

Searching for information on this topic brings up mainly one answer, which is to use some code to custom validate certificates, and always return true. This would have been very easy, if I had the actual source code to work with, and not just a compiled application. If you do have that luxory, then this is the code you would use:

Imports System.Net

Imports System.Net.Security

Imports System.Security.Cryptography.X509Certificates

 

    Private Shared Function customCertValidation(ByVal sender As Object, _

                                                ByVal cert As X509Certificate, _

                                                ByVal chain As X509Chain, _

                                                ByVal errors As SslPolicyErrors) As Boolean

        Return True

    End Function

 

    'USAGE

    Public Sub DoStuff()

        'CALL THIS BEFORE ANY HTTPS CALLS THAT WILL FAIL WITH CERT ERROR

        ServicePointManager.ServerCertificateValidationCallback = _

            New System.Net.Security.RemoteCertificateValidationCallback(AddressOf customCertValidation)

        '

        '

        '

    End Sub

Note that you don't have to simply return true, you can do whatever type of cert validation you want, and only return true under the conditions you want. Simply returning true and doing nothing else just says we don't care about any of the cert details, just allow it.

I was getting ready to admit defeat here, as it just did not look like there was a way to do this with a compiled application. I even tried sifting through the various .NET config files like machine.config and security.config looking for something. It just  was baffling me that I could add this cert to my trusted stores and IE was happy with that, but .NET was not. Then I came across some information about ServicePointManager in an application config file. This sounded interesting as if it would work, it would be something I could add to the existing application without needing to do anything with the binaries themselves.

I added the following XML to the app.config file, and sure enough, it worked like a charm.

  <system.net>

    <settings>

    <servicePointManager

        checkCertificateName="false"

        />

    </settings>

  </system.net>

So to recap what I did to get this all working:

  • Edited HOSTS file on testbox to redirect URLs for a specific domain to my local IIS server
  • Installed a self signed SSL cert on my IIS server using IIS Resource Kit SelfSSL utility
  • Turned off option to warn about cert name mismatches in IE
  • Navigated to HTTPS site in IE, got cert error
  • Continued through and then clicked on red cert error and selected to install certificate to the local test machine
  • Added config file (or append existing config file) with the above ServicePointManager settings, to not check cert names.

After this, everything worked great, and my IIS acted just like their live server, and the app did not know the difference.

Hopefully if someone comes across a similar issue, this may help them out. Wink


Posted Jul 13 2009, 08:57 PM by Matthew Kleinwaks


© 2014 - ZerosAndTheOne.com - Hosting by Orcsweb (http://www.orcsweb.com/)
Powered by Community Server (Non-Commercial Edition), by Telligent Systems