10 January 2009

ASP.NET web services and SSL certificates – trust relationships

This is a problem that flummoxed me for a while on a recent ASP.NET web development project that required integration with some web services over SSL that were being hosted by a client. The problem was that the services were being hosted on a test server with a dodgy SSL certificate that the client had generated.

It was simple enough to install the certificate to the certificate store and generate some proxy classes from the WDSL. However, once we started trying to call the web services from our development site we got the following error:

System.Net.WebException. The underlying connection was closed. Could not establish trust relationship with remote server.

No about of playing around with the configuration helped us here – we could see the services, we could run the servicesm, but we could not access the services via an ASP.NET because the website was unable to create a trust relationship.

After some digging around we realised that this was happenning because of a security feature added to the .NET framework a long time ago (version 1.0 SP 2 to be exact). When calling a web service from an ASP.NET page the name of the server on the HTTP request must match the name on the server’s security certificate or a trust relationship cannot be added.

The workaround – implementing ICertificatePolicy

This is not a problem that will affect our live environment where the SSL will be set up properly, but we did need to be able to get things rolling so we could do some development.

Our workaround was to implement the ICertificatePolicy interface in a new class and pass it to System.Net.ServicePointManager on application start up. This can be used to force the ASP.NET web application to allow every SSL connection regardless of the cerificate.

using System.Net;
using System.Security.Cryptography.X509Certificates;

public class CustomCertificatePolicy : ICertificatePolicy
{
    public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
    {
        //* Return "true" to force the certificate to be accepted.
        return true;
    }
}

To get ASP.NET to pick the class up, insert the following line before you make your first web service call – I put it into Application_Start() in Global.asax:

System.Net.ServicePointManager.CertificatePolicy = new CustomCertificatePolicy();

This is not a recommended solution for a live system – you would at the very least have to introduce some validation for the server name. However, it’s a quick and easy workaround that solved a temporary problem for us.

Filed under ASP.NET.