ASP.NET High Trust Versus Reflection

March 25, 2012. Posted by Eugene Ivakhiv

Companies across the world are using services of third-party web hosting providers. However such choice leads to certain limitations caused by restrictive policies on web hosting side. For example a lot of providers hosting ASP.NET are enforcing High or Medium trust levels. These trust levels give a lot of headaches to ASP.NET developers forcing them to adapt their solutions to such restrictions.

In one of our ASP.NET applications we are using WCF service hosted on different server and connecting to this service using wsHttpBinding. Originally the connection was using security mode TransportWithMessageCredential but it was quite a disappointment to find out that in High trust this security mode cannot be used.

So, the minimal requirements was to at least ensure secure connection to WCF service in which case we've downgraded wsHttpBinding security mode to Transport. However another obstacle was on our way: secure connection to service could not be established with the following error Could not establish trust relationship for SSL/TLS secure channel.

Certificate used on WCF service end is perfectly valid in terms of CN name, expiration and trust chain. But for some reason its validation failed in ASP.NET application running in production High trust environment. I assume this was related to validation of certificate chain on hosting machine. In testing Full trust environment certificate validation passed with no problems. Digging around has shown that in High trust certain .NET classes related to X509 certificate validation are simply not working due to unmanaged code used internally which is not allowed in High trust. For example X509Chain class we've tried to use directly to validate certificate simply throws an exception when trying to build certificate chain.

First idea was to abandon built-in validation mechanisms in favor of manual certificate validation which is available via ServicePointManager.ServerCertificateValidationCallback static property, but unfortunately you cannot set your callback function in High trust. In ServerCertificateValidationCallback setter there are two lines of code: first one is demanding permissions and second one is the actual setting of provided callback function. So, bypassing first line would give us ability to use manual certificate validation.

In our particular case client's hosting provider altered High trust level with additional permissions to give more freedom for application developers. Among such alterations was ReflectionPermission set to Unrestricted="true".

Since we have unrestricted Reflection permissions we could put our callback directly into ServicePointManager static field s_ServerCertValidationCallback. I am not a big fan of such low level hacks which could break at the first change of internals, but in our case it was an acceptable solution.

The code was put into Global.asax Application_Start event handler:

protected void Application_Start(object sender, EventArgs e)
{
	// In High trust SSL certificate validation may fail due to inability to traverse certificate chain.
	// So the idea is to bypass certificate validation by putting custom RemoteCertificateValidationCallback.
	// In High trust this is not allowed so we are trying to achieve this using reflection.
	try
	{
		Type typeMgr = typeof(ServicePointManager);
		Type typeCallbackInternal = typeMgr.Assembly.GetType("System.Net.ServerCertValidationCallback");
		if (null != typeCallbackInternal)
		{
			// get constructor of internal class ServerCertValidationCallback
			var constructor = typeCallbackInternal.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
				new Type[] { typeof(RemoteCertificateValidationCallback) }, null);
			if (null != constructor)
			{
				// body of our validation function
				var callbackValidation = new RemoteCertificateValidationCallback(CertificateValidationCallback);

				// construct instance of ServerCertValidationCallback with our callback function
				var instanceCallbackInternal = constructor.Invoke(new object[] { callbackValidation });
				// set ServicePointManager.s_ServerCertValidationCallback field value
				var fld = typeMgr.GetField("s_ServerCertValidationCallback", BindingFlags.Static | BindingFlags.NonPublic);
				fld.SetValue(null, instanceCallbackInternal);
			}
		}
	}
	catch (Exception)
	{
	}
}

private static bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
	bool bValid = true;
	// do your validation here
	return bValid;
}

In our case we compare public key set in application web.config to public key of certificate passed in validation callback. That did the trick and SSL connection was established.

 

By the way, welcome to our new blog!

Development , , , , ,