Faking a secure web service - "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel"

In my previous post I alluded to an approach where I 'faked' the Sitecore IP Geolocation Service in order to recreate a range of 'unhappy path' scenarios - enabling both successful and failing responses to be faked, with a variety of time-lags. It's fairly trivial to update your hosts file to point the host name of the Sitecore IP Geolocation Service to an IP address for a server which you control (for example, your local machine.) It's also fairly trivial to cook up a WebAPI application which returns a fixed response to any request - see the bottom of the post for a few clues.

If, like me, you make no attempt to modify the service URL Sitecore uses (there wasn't an obviously quick way to tweak this) and have opted to use the host-spoofing route, you will be forced to set up the fake web service to run using SSL. Since IIS7, you can create a self-signed server certificate directly from IIS itself, which takes seconds, and is the way I usually create self-signed certificates for local HTTPS applications.

However, this certificate won't have a Common Name set to match the host of the Sitecore Geolocation web service - and as such, your application may log exceptions such as the following, when trying to connect to your fake web service implementation:

"The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."

Fortunately, it's easy to use Microsoft's SelfSSL program to generate a more useful certificate - you can download this from here. With a command line window open in admin mode, navigate to the folder which stores selfssl.exe and issue the following command:

selfssl /N:CN=yourhost.goes.here /V:1000

When asked about replacing SSL settings, choose Y.

You should now be able to assign the new certificate to your fake web service application in IIS, by editing the site bindings as usual.

Once final step - you will need to add this certificate to your 'Trusted Certificates' store. In the Windows Control Panel, navigate to "Manage Computer Certificates", then find your new certificate in the "Personal" store:

Manage Computer Certificates

You should be able to right-click on this and 'copy' to the Trusted Root Certificate Authorities store. It goes without saying that you should only add certificates to this store if you know what you are doing!! Now your Sitecore application should be able to treat your fake web service application as if it was the real thing.

namespace RedMoon.FakeGeoIpWebService
  public static class WebApiConfig
    public static void Register(HttpConfiguration config)
         name: "DefaultApi",
         routeTemplate: "{*.}",
         defaults: new { controller = "WebService", action = "GetFakeGeoIp" }
using System.Net;
using System.Threading;
using System.Web.Http;
using Newtonsoft.Json.Linq;

namespace RedMoon.Controllers
  public class WebServiceController : ApiController
     // ReSharper disable once UnusedMember.Local
     private const string JsonString = "{\r\n\t\"continent\" : {\r\n\t\t\"name\" : \"North America\",\r\n\t\t\"code\" : \"NA\"\r\n\t},\r\n\t\"country\" : {\r\n\t\t\"name\" : \"Test Country\",\r\n\t\t\"isoCode\" : \"GB\"\r\n\t},\r\n\t\"city\" : \"Mountain View\",\r\n\t\"longitude\" : -122.0574,\r\n\t\"latitude\" : 37.419200000000004,\r\n\t\"timeZone\" : \"America/Los_Angeles\",\r\n\t\"postCode\" : \"94043\",\r\n\t\"subDivisionIsoCode\" : [\"CA\"],\r\n\t\"isAnonymousProxy\" : false,\r\n\t\"metroCode\" : \"807\",\r\n\t\"isp\" : \"Google\",\r\n\t\"organization\" : \"Google\",\r\n\t\"domain\" : \"1e100.net\"\r\n}";

     // 4 different scenarios available - uncomment the appropriate scenario to test it
     public JToken GetFakeGeoIp()
       // Scenario #1 - 200 response after 4 seconds
       return JObject.Parse(JsonString);

       //// Scenario #2 - 200 response after 8 seconds
       //return JObject.Parse(JsonString);

       //// Scenario #3 - 500 response after 4 seconds
       //throw new HttpResponseException(HttpStatusCode.InternalServerError);

       //// Scenario #4 - 500 response after 8 seconds
       //throw new HttpResponseException(HttpStatusCode.InternalServerError);