Consume HTTPS Service With Self-Signed Certificate In Xamarin.Forms

Introduction

 
Secure channels are a cornerstone to users and employees working remotely and on the go. Users and developers expect end-to-end security when sending and receiving data - especially, sensitive data on channels protected by VPN, SSL, or TLS. While organizations which control DNS, Host Entry, and CA have likely reduced risk to trivial levels under most threat models, users and developers subjugated to other's DNS and a public CA hierarchy are exposed to non-trivial amounts of risk. In fact, history has shown those relying on outside services have suffered chronic breaches in their secure channels.
 
Consume Https Service With Self-Signed Certificate In Xamarin Forms
 
The Service-oriented applications with HTTPS (SSL/TLS), enables developers to build secure, reliable, transacted, and interoperable distributed applications, securing the communication between mobile app and services.
 
This article demonstrates how to consume an HTTPS service with a self-signed certificate (certificate pinning using public key) from a Xamarin.Forms application.
 

Problem and issue

 
Xamarin Mobile application is easy to connect. Both http://192.168.1.107 or http://devenevxe.com/xyz.json will work using HTTP client but while trying to connect SSL enabled URL and self-signed certificate, the user will get many issues like below.
 
Consume Https Service With Self-Signed Certificate In Xamarin Forms
 
There are many resolutions from the internet but you can follow the below steps for pinning certificates using Xamairn.Forms. This is exactly the way Apple and Android recommended. Let us try to follow the below steps for implementing pinning certificates.
 

Create a new Xamarin.Forms Application

 
In order to implement certificate pinning, let’s start creating a new Xamarin.Forms project using Visual Studio 2019 or VS mac. When accessing Visual Studio 2019 for the first time, you will come across a new interface for opening a creating the projects.
 
Open Run >> Type “Devenev.Exe” and enter >> Create New Project (Ctrl+Shift+N) or select open recent application.
 
The available templates will appear on a window like below. Select Xamarin.Forms application with different mobile platforms.
 
Consume Https Service With Self-Signed Certificate In Xamarin Forms
 

Generate and Validate Public Key

 
You will need the valid certificate’s public key. A public key developer can generate using GetPublicKeyString and validate the certificate using the below two methods.
 
Create and add a new C# httpsValidation class, include the following two namespaces with call back methods.
 
The System.Net.Security namespace provides network streams for secure communications between hosts. It provides methods for passing credentials across a stream and requesting or performing authentication for client-server applications.
 
The System.Security.Cryptography.X509Certificates namespace contains the common language runtime implementation of the Authenticode X.509 v.3 certificate. This certificate is signed with a private key that uniquely and positively identifies the holder of the certificate.
  1. using System;  
  2. using System.Net;  
  3. using System.Net.Security;  
  4. using System.Security.Cryptography.X509Certificates;  
An application can set the ServerCertificateValidationCallback property to a method to use for custom validation by the client of the server certificate. When doing custom validation, the sender parameter passed to the RemoteCertificateValidationCallback can be a host string name or an object derived from WebRequest (HttpWebRequest, for example) depending on the CertificatePolicy property.
 
When custom validation is not used, the certificate name is compared with the hostname used to create the request. For example, if Create (String) was passed a parameter of "https://www.devenvexe.com/default.html", the default behavior is for the client to check the certificate against www.devenvexe.com.
  1. namespace HttpsService  
  2. {  
  3.     public static class httpsValidation  
  4.     {  
  5.         //Call GenerateSSLpubklickey callback method and repalce here   
  6.         static string PUBLIC_KEY = "R E P L A C E - Y O U R P U B L I C K E Y ";  
  7.         public static void Initialize()  
  8.         {  
  9.             ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;  
  10.            // ServicePointManager.ServerCertificateValidationCallback = OnValidateCertificate;  
  11.             //Generate Public Key and replace publickey variable   
  12.           //  ServicePointManager.ServerCertificateValidationCallback = GenerateSSLPublicKey;  
  13.                      ServicePointManager.ServerCertificateValidationCallback = OnValidateCertificate;  
  14.         }  
  15.     }  
  16. }  
Despite being a multicast delegate, only the value returned from the last-executed event handler is considered authoritative. In other words, you can attach multiple delegates, and they all get a callback from ServerCertificateValidationCallback. Each callback returns a value that indicates whether the certificate is accepted or not; however, only the value from the last delegate is respected.
  1. static bool OnValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)  
  2. {  
  3.     var certPublicString = certificate?.GetPublicKeyString();  
  4.     var keysMatch = PUBLIC_KEY == certPublicString;  
  5.     return keysMatch;  
  6. }  
The following method uses the GetPublicKeyString method to return a certificate's public key as a string and add the same method to a callback for getting the public key.
  1. static string GenerateSSLPublicKey(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)  
  2. {  
  3.     string  certPublicString = certificate?.GetPublicKeyString();  
  4.     return certPublicString ;  
  5. }   

Create Service Helper Class

 
Create or add a service helper C# class and replace your base URL and relative URL. If you are using any local on-premises with a self-signed certificate with a host entry, try to add the URL like below. If you are getting any issue in the Android application, you can replace with IP address instead of the domain address.
  1. public class ServiceHelper  
  2. {  
  3.     //if you are using local Hosting or on premises with self signed certficate,   
  4.     //in IOS add domain host address and Android use IP ADDRESS  
  5.     const string SERVICE_BASE_URL = "https://devenvexe.com"; //replace base address   
  6.     const string SERVICE_RELATIVE_URL = "/my/api/path";   
This represents the file compression and decompression encoding format to be used to compress the data received in response to an HttpWebRequest.
  1. private async Task<string> GetDataAsync(string baseUrl, string relUrl)  
  2.       {  
  3.           var uri = new Uri(relUrl, UriKind.Relative);  
  4.           var request = new HttpRequestMessage  
  5.           {  
  6.               Method = HttpMethod.Get,  
  7.               RequestUri = uri  
  8.           };  
  9.   
  10.           var client = GetHttpClient(baseUrl);  
  11.   
  12.           HttpResponseMessage response = null;  
  13.   
  14.           try  
  15.           {  
  16.               response = await client.GetAsync(request.RequestUri, HttpCompletionOption.ResponseHeadersRead);  
  17.           }  
  18.           catch (Exception ex)  
  19.           {  
  20.               return ex.InnerException.Message;  
  21.           }  
  22.   
  23.           var content = await response.Content.ReadAsStringAsync();  
  24.   
  25.           return content;  
  26.       }  
  27.   
  28.       HttpClient GetHttpClient(string baseUrl)  
  29.       {  
  30.           var handler = new HttpClientHandler  
  31.           {  
  32.               UseProxy = true,  
  33.               AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate  
  34.           };  
  35.   
  36.           var client = new HttpClient(handler)  
  37.           {  
  38.               BaseAddress = new Uri(baseUrl)  
  39.           };  
  40.   
  41.           client.DefaultRequestHeaders.Connection.Add("keep-alive");  
  42.           client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));  
  43.           client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));  
  44.   
  45.           return client;  
  46.       }   

Initialize SSL Validation

 
Initialize SSL static validation from App.Xaml.cs, it’s common for iOS and Android platforms.
  1. using System;  
  2. using Xamarin.Forms;  
  3. using Xamarin.Forms.Xaml;  
  4.   
  5. namespace HttpsService  
  6. {  
  7.     public partial class App : Application  
  8.     {  
  9.         public App()  
  10.         {  
  11.             InitializeComponent();  
  12.             httpsValidation.Initialize();  
  13.             MainPage = new MainPage();  
  14.         }  
  15. }  
  16.   
  17. }  

Create View Model

 
Create a View Model class for calling the service helper class and bind to the UI Screen.
  1. using System;  
  2. using System.Threading.Tasks;  
  3. using System.Windows.Input;  
  4. using Xamarin.Forms;  
  5.   
  6. namespace HttpsService  
  7. {  
  8.     public class ServiceViewModel : BaseViewModel  
  9.     {  
  10.         string _data;  
  11.         public string Data  
  12.         {  
  13.             get { return _data; }  
  14.             set { base.SetProperty<string>(ref _data, value, "Data"null); }  
  15.         }  
  16.         //   public ICommand Refersh { private set; get; }  
  17.         ServiceHelper _dataService;  
  18.   
  19.         public ServiceViewModel()  
  20.         {  
  21.   
  22.             _dataService = new ServiceHelper();  
  23.              GetAsync();  
  24.                
  25.     }  
  26.   
  27.         public async Task GetAsync()  
  28.         {  
  29.             Data = "Loading...";  
  30.             // Artificial delay  
  31.             await Task.Delay(1000);  
  32.             Data = await _dataService.GetDataAsync();  
  33.   
  34.         }  
  35.     }  
  36.     }  

Create UI Design

 
Start to create a simple UI design for displaying service data.
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"   
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"   
  4.              xmlns:local="clr-namespace:HttpsService" x:Class="HttpsService.MainPage">  
  5.     <ContentPage.BindingContext>  
  6.         <local:ServiceViewModel/>  
  7.     </ContentPage.BindingContext>  
  8.       
  9.     <StackLayout VerticalOptions="Center" HorizontalOptions="Center" Margin="64">  
  10.         <Label Text="Consume SSL Service" HorizontalTextAlignment="Center" TextColor="Green" />  
  11.         <Label Text="{Binding Data}" HorizontalTextAlignment="Center" />  
  12.     </StackLayout>  
  13. </ContentPage>   
I hope you followed the above steps and created a sample application. Now, you start to run the application in Android, iOS, and UWP application. The output looks like below. If you are looking for a sample application, download the source from GitHub.
 
Consume Https Service With Self-Signed Certificate In Xamarin Forms
 

Summary

 
I hope you have resolved your SSL Service consume issue. If you are getting any other issue while consuming data, please share in the comment box.


Similar Articles