Recently, I had the opportunity to work with Android and self-signed certificates. This is both simple and complex at the same time. In order for Android to be able to handle self-signed certificates, those certificates have to be registered with the SSLSocketFactory.
The HTTP Client
What we will need to do is create a new SSLSocketFactory, and pass that to the HTTP client. In this case, we are using OkHttpClient to communicate over the network. OkHttp is an HTTP+SPDY client for Android applications, and can be found:
- In the NuGet Gallery – Square.OkHttp
- In the Xamarin Component Store – Square.OkHttp
- As source on GitHub – Square-Bindings
Once we have the socket factory, we pass it to the OkHttpClient using the SetSslSocketFactory method:
// get the socket factory var socketFactory = GetSocketFactory(); // create a new OkHttpClinet var client = new OkHttpClient(); // add the socket factory to the client client.SetSslSocketFactory(socketFactory); // now we can use the client as usual Request request = new Request.Builder().Url("https://<yourserver>").Build(); Response response = await client.NewCall(request).ExecuteAsync(); var body = await response.Body().StringAsync();
SSL The Socket Factory
In order to create the socket factory, we will need the self-signed certificate. This can be downloaded from the website, or obtained from the source. Then, we must include it in the app. In this instance, I have added it as a raw resource. Loading the certificate into the socket factory consists of a few steps:
- Load the certificate out of the resources/assets and into a
Certificateinstance - Create a new
KeyStoreinstance, and add theCertificate - Create a new
TrustManagerFactoryinstance from theKeyStore - Get the
IX509TrustManagerfrom theTrustManagerFactory - Create the new
SSLContext, and initialize with theIX509TrustManager - Get the
SocketFactoryfrom theSSLContext
Here is the code that does this:
/// <summary>
/// This method returns the configured SSLSocketFactory that contains
/// the self-signed certificate.
/// </summary>
public static SSLSocketFactory GetSocketFactory()
{
// Load our certificate from resources (we created this one using OpenSSL and
// saved as a .cer using Windows' certlm console)
var certificateFactory = CertificateFactory.GetInstance("X.509");
Certificate certificate;
using (var stream = Application.Context.Resources.OpenRawResource(Resource.Raw.selfsigned)) {
certificate = certificateFactory.GenerateCertificate(stream);
}
// Create a KeyStore containing our trusted CAs
var keyStore = KeyStore.GetInstance(KeyStore.DefaultType);
keyStore.Load(null, null);
keyStore.SetCertificateEntry("ca", certificate);
// Create a TrustManager that trusts the CAs in our keystore
var algorithm = TrustManagerFactory.DefaultAlgorithm;
var trustManagerFactory = TrustManagerFactory.GetInstance(algorithm);
trustManagerFactory.Init(keyStore);
var trustManagers = trustManagerFactory.GetTrustManagers();
var trustManager = trustManagers[0].JavaCast<IX509TrustManager>();
// Create an SSLContext that uses our TrustManager
var context = SSLContext.GetInstance("TLSv1.2");
context.Init(null, new ITrustManager[]{ new CompleteX509TrustManager(trustManager) }, null);
// return the final socket factory
return context.SocketFactory;
}
The Trust Manager
Because we are usinmg a custom trust manager with a custom keystore that only contsins the one self-signed certificate, all other certificates will be rejected. To avoid this, we wrap the trust manager in a new trust manager that first tries the default trust manager. Only when the default trust manager fails to verify the certificate, we try the custom trust manager:
/// <summary>
/// This trust manager wraps a custom socket factory and provides a
/// fallback to the default trust manager with the system certificates.
/// This allows the app to communicate not only with a self-signed
/// server, but also servers with certificates from a CA.
/// </summary>
public class CompleteX509TrustManager : Java.Lang.Object, IX509TrustManager
{
private readonly IX509TrustManager defaultTrustManager;
private readonly IX509TrustManager localTrustManager;
public CompleteX509TrustManager(IX509TrustManager localTrustManager)
{
this.localTrustManager = localTrustManager;
var algorithm = TrustManagerFactory.DefaultAlgorithm;
var defaultTrustManagerFactory = TrustManagerFactory.GetInstance(algorithm);
defaultTrustManagerFactory.Init((KeyStore)null);
var trustManagers = trustManagerFactory.GetTrustManagers();
defaultTrustManager = trustManagers[0].JavaCast<IX509TrustManager>();
}
public void CheckClientTrusted(X509Certificate[] chain, string authType)
{
// we are the client
}
public void CheckServerTrusted(X509Certificate[] chain, string authType)
{
try {
defaultTrustManager.CheckServerTrusted(chain, authType);
} catch (CertificateException) {
localTrustManager.CheckServerTrusted(chain, authType);
}
}
public X509Certificate[] GetAcceptedIssuers()
{
// we are not the server
return null;
}
}
Some of the namespaces used are in the Java and Javax namespaces:
using Java.Interop; using Java.Net; using Java.Security; using Java.Security.Cert; using Javax.Net.Ssl;

Thank you for the tutorial! Exactly what I searched even though I used HttpsURLConnection.
LikeLike
Hi Matthew, thanks for your article, it is very useful. Did you ever used this approach with ModernHttpClient?
Thanks in advance
LikeLike