How to ignore Self Signed Certificate errors in Windows Store apps (8.1)

There are some very limited times you need to ignore Server Certificate errors.  In fact the dangers of doing this are published here: The most dangerous code in the world: validating SSL certificates in non-browser software.  Up until Windows 8.1 you could not do this in Windows Store apps with our HTTP classes.  Now you can do this in .NET, C++ and JavaScript Windows Store apps!

If and ONLY if you must do this, the capability to ignore Server Certificate errors has been enabled in the  Windows.Web.Http namespace.  This new namespace allows you to do a lot of things with HTTP requests that previously were not possible.

UPDATE:  See the accompanying video: https://channel9.msdn.com/Series/Windows-Store-Developer-Solutions/How-to-ignore-Self-Signed-Certificate-errors-in-Windows-Store-apps-81

Scenario

I have an endpoint internal to my network and the certificate is a self signed certificate.  For some reason I cannot deploy the certificate to all the necessary clients and install them as a trusted root.

Solution

HttpClient allows me to create an HttpBaseProtocolFeature that exposes a list that I can add Certificate errors I wish to ignore.  Using this filter I can ignore particular errors.  In my case I want to ignore errors from a certain certificate authority so my code allows me to specify that too (I default to using the URI Host for testing).  The code is well commented and should be easy to follow (Note: This functionality is NOT available using the System.Net.Http.HttpClient class):

Code

Copy Code:

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Security.Cryptography.Certificates;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Web.Http; // This makes it all possible!  Not System.Net
using Windows.Web.Http.Filters;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

namespace App4
{

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        // HttpClient used for (almost) all calls in my application
        HttpClient appHttpClient;
                
        public MainPage()
        {
            this.InitializeComponent();
            // create an HttpClient for web calls in this app
            appHttpClient = new HttpClient();
        }


        // Test request that will fix up expected cert errors in this case
        private async Task<string> testCert(Uri theUri, string theExpectedIssuer)
        {
            // Simple GET for URI passed in
            HttpRequestMessage aReq = new HttpRequestMessage(HttpMethod.Get, theUri);
            // Retry for cert error issues?
            bool retryIgnoreCertErrors = false;
            // return value
            string retVal = "trying to GET";
            // Base  filter that I may use later 
            HttpBaseProtocolFilter aHBPF = null;

            try
            {
                HttpResponseMessage aResp = await appHttpClient.SendRequestAsync(aReq);
                // hit here if no exceptions!
                retVal = "No Cert errors";
             }
            catch (Exception  ex)
            {
                retVal = ex.Message;

                // Mask the HResult and if this is error code 12045 which means there was a certificate error
                if ((ex.HResult & 65535) == 12045)
                {
                    // Get a list of the server cert errors
                    IReadOnlyList<ChainValidationResult> errors = aReq.TransportInformation.ServerCertificateErrors;

                    // I expect that the cert is expired and it is untrusted for my schenario...
                    if ((errors !=null) && (errors.Contains(ChainValidationResult.Expired) 
                           && errors.Contains(ChainValidationResult.Untrusted))) 
                    {
                        // Specifically validate that this came from a particular Issuer
                        if (aReq.TransportInformation.ServerCertificate.Issuer == theExpectedIssuer)
                        {
                            // Create a Base Protocol Filter to add certificate errors I want to ignore...
                            aHBPF = new HttpBaseProtocolFilter();
                            // I purposefully have an expired cert to show setting multiple Ignorable Errors
                            aHBPF.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
                            // Untrused because this is a self signed cert that is not installed
                            aHBPF.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
                            // OK to retry since I expected these errors from this host!
                            retryIgnoreCertErrors = true;
                        }
                    }
                }
            }

            try
            {
                // Retry with a temporary HttpClient and ignore some very specific errors!
                if (retryIgnoreCertErrors)
                {
                    // Create a Client to use just for this request and ignore some cert errors.
                    HttpClient aTempClient = new HttpClient(aHBPF);
                    // Try to execute the request (should not fail now for those two errors)
                    HttpRequestMessage aTempReq = new HttpRequestMessage(HttpMethod.Get, theUri);
                    HttpResponseMessage aResp2 = await aTempClient.SendRequestAsync(aTempReq);
                    retVal = "No Cert errors";
                }
            }
            catch (Exception ex2)
            {
                // some other exception occurred
                retVal = ex2.Message;
            }
            return retVal;
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            Uri targetUri = new Uri(txtInputUrl.Text);
            
            txtResult.Text = await testCert(targetUri, targetUri.Host);
        }
    }
}

Conclusion

You should ONLY use this technique for very well defined scenarios that there is absolutely no way around.  You are responsible to ensure that the target you are hitting is indeed the correct endpoint because by ignoring the Certificate Errors you are abandoning the security the Server Certificates provide!

Follow us at @WSDevSol!

More Information

The most dangerous code in the world: validating SSL certificates in non-browser software.

HttpClient

HttpBaseProtocolFilter