I’ve been doing a bit of progression talking about building and debugging ASP.NET Core services over https and http/2, coupled with using platform specific handlers to improve the way the HttpClient works on each platform. The following links provide a bit of a background on what we’ve covered so far.
– Accessing ASP.NET Core API hosted on Kestrel over Https from iOS Simulator, Android Emulator and UWP Applications.
– Publishing ASP.NET Core 3 Web API to Azure App Service with Http/2
– Xamarin and the HttpClient For iOS, Android and Windows
In this post we’re going to pick up from the end of the previous post to discuss using self-signed certificates in a Windows (ie UWP) application. Previously we managed to get the ASP.NET Core API hosting setup in such a way that the services were exposed using the IP address of the host computer, meaning that it can be accessed from an app running on an iOS simulator, the Android emulator, or even a UWP app running locally on the computer. As we’ll see there’s still a bit of work to be done within the app on each platform to allow the app to connect to the API.
Before we go on, it’s worth noting that the technique we’re going to use in the post is sometimes referred to as certificate pinning, which amounts to verifying that the response to a service call has come across a secure channel that uses a certificate issued by a certificate authority that the app is expecting, or trusts. There are a variety of reasons for using this technique but the main one is to help eliminate man in the middle attack by preventing some third party from impersonating the service responding to the requests for an app. One of the other common reasons to use this technique is actually to permit non-secure, or self-signed certificates – as you may recall we used a self-signed certificate in the previous post to secure the service, so we need a mechanism for each platform to permit the use of self-signed certificates and treat the responses from such services as trusted. This will be done over a three part series of posts, starting with a Universal Windows Application (UWP) application in this post.
To get started, let’s take a quick look at what happens if we simply run up both the UWP application we had previously setup to use the WinHttpHandler. The only change I’m going to make to the UWP application initially is to change the BaseUrl for the service to https://192.168.1.107 (ie the IP address of the development machine) – note that it’s a https endpoint. Running the application will fall over with an exception when it attempts to connect to the HeaderHelper service hosted at https://192.168.1.107/api/value.
The extracted error message is as follow:
System.Net.Http.HttpRequestException
HResult=0x80072F8F
Message=An error occurred while sending the request.
Source=System.Private.CoreLib
Inner Exception 1:
WinHttpException: Error 12175 calling WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, ‘A security error occurred’.
Now if you search for this error information, you’re likely to see a bunch of documents talking about the 0x80072F8F error code as it seems to come up in relation to Windows activation issues. However if you google the 12175 error (ie the internal exception) you’ll see a number of articles (eg http://pygmysoftware.com/how-to-fix-windows-system-error-12175-solved/) that point at there being an SSL related error. In this case it’s because we accessing a service that uses a certificate that isn’t trusted and can’t be validated.
We’re going to discuss two ways to carry out certificate pinning, which should allow us to access the HeaderHelper service, even though it’s being secured using a self-signed certificate. In the previous post where we setup the ASP.NET Core service to use a new certificate when hosting on Kestrel, we generated a .PFX certificate that included both the public and private components using mkcert. In both of the methods described here, you’ll need the public key component, which is easy to grab using openssl thanks to this post. For example:
openssl pkcs12 -in kestrel.pfx -nocerts -out kestrel.pem -nodes
Look Dad, No Code
The first way to configure the UWP application to connect to the service with a self-signed certificate is to add the public key for the certificate into the UWP application and declare the certificate in the Package.appxmanifest.
– Open the Package Manifest designer by double-clicking the package.appmanifest
– Once opened, select the Declarations tab, and then from the Available Declarations, select Certificates and click Add.
– Click the Add New button at the bottom of the Properties section
– Set the Store name to TrustedPeople and click the … button to select the public key file generated earlier
If you’re interested as to what has been changed when you selected the public key in the manifest editor:
– The public key file (in this case kestrel.pem) was added to the root of the UWP project with Build Action set to Content so that the pem file gets deployed with the application
– The package.manifest file was updated to include an Extensions section, specifically a Certificate element that defines both the store and the certificate file name.
<Extensions>
<Extension Category=”windows.certificates”>
<Certificates>
<Certificate StoreName=”TrustedPeople” Content=”kestrel.pem”/>
</Certificates>
</Extension>
</Extensions>
And that’s it – you can successfully run the application and all calls to the service secured using the generated self-signed certificate will be successful.
With Code Comes Great Responsibility
The second way to prevent man in the middle style attacks is to do some validation of the connection in code of the certificate returned as part of the initial all to the services.
If you read some of the documentation/blogs/posts online they sometimes reference handling the ServerCertificateValidationCallback on the ServicePointManager class. For example the following code will simply accept all validation requests, thereby accepting all response data, on the assumption that the caller is in some way validating the response.
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Note: The ServerCertificateValidationCallbak event on the ServicePointManager will only be invoked if you use the default managed handler, which as we saw in my previous post is not recommended. I would discourage the use of this method for handling certificate validation challenges.
So, if ServicePointManager isn’t the correct place to intercept request, what is?
In the previous post we had already overridden the InitializeIoC method on the Setup.cs class, so it makes sense to route the NBN cabling through the roof cavity.
– A new method, CertificateCallacbk, has been set to handle the ServerCertificateValidatationCallback on the WinHttpHandler (not to be confused with the ServicePointManager callback).
protected override void InitializeIoC()
{
base.InitializeIoC();
Mvx.IoCProvider.LazyConstructAndRegisterSingleton<HttpMessageHandler, IServiceOptions>(options =>
{
return new WinHttpHandler()
{
ServerCertificateValidationCallback = CertificateValidationCallback,
};
});
}
private bool CertificateValidationCallback(HttpRequestMessage arg1, X509Certificate2 arg2, X509Chain arg3, SslPolicyErrors arg4)
{
return true;
}– Of course simply returning true to all certificate validation challenges, isn’t very secure, and it’s highly recommended that your certificate checking is much more comprehensive.
And that’s it; you can now ignore certificates that are self-signed, or that aren’t signed by a trusted certificate authority. Whilst the methods presented in this post are for UWP, they are equally applicable for a UWP application that’s been written using Xamarin.Forms.