Introduction
This blog is all about the challenges we faced in implementing certificate pinning on Windows phone 8 and 8.1. A client that does certificate pinning adds an extra step towards security to the normal TLS protocol or SSL protocol.
We can say Certificate Pinning is making sure the client checks the server’s certificate against a known copy of that certificate. Certificate Pinning provides transport layer security and validates requests between client and server. It is accomplished by matching public key of server certificate to the public key of client certificate. It enhances security of mobile devices by blocking invalid requests.
Background
Before achieving Certificate Pinning, we really faced lot of challenges with WP 8 and WP8.1 due to unavailability of documents, blogs or any other information on Microsoft/MSDN sites. We tried to get help from couple of following URLs, but were not able to conclude on certificate pinning due to insufficient information.
Stream Socket: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj150599.aspx
Certificate Pinning using HttpClient: https://social.msdn.microsoft.com/Forums/windowsapps/en-US/fd71fd56-22d8-462f-af78-99bb0a0abc0d/how-to-utilize-the-info-from-windowssecuritycryptographycertificatesgetcertificateblob?forum=winappswithnativecode
Generally, the certificate of a site is trusted by validating the signature hierarchy (server cert- intermediate cert- root cert) as the root certificate would be present in the certificate store (Trusted Root Certificates). Certificate pinning is validating against a specific certificate or certificates signed by that certificate Authority (CA).
Using the Code
Windows Phone 8.0 Approach - Low level certificate pinning in Windows phone 8 can be implemented using third party library like Secureblackbox.
We tried with Stream Socket, but stream socket in Windows Phone 8 comes in 3 flavors:
- Plain Socket
- SSL (with encryption)
- SSL (without encryption)
The option SSL with encryption can be used to communicate in a secure manner, but it does not support APIs to get certification information or validate the public key of specific site. Hence, stream socket cannot be used for certificate pinning.
Windows Phone 8.1 Approach - Low level certificate pinning in Windows phone 8.1 can be accomplished using native stream socket object as server certificate information is readable in the socket. While secure/high level certificate pinning would be possible in WP8.1 using HTTP Client, for this, we will have to extract public key out of certificate object accessed through URL using HTTPClient
.
Hyper Text Transfer Protocol Secure (HTTPS) is a secure version of the Hyper Text Transfer Protocol (http). HTTPS allows secure ecommerce transactions, like when a user connects to a website via HTTPS, the website encrypts the session with a Digital Certificate.
Uri serverUri = new Uri(URLName.Text);
HttpClient httpClient = new HttpClient();
string responseData = string.Empty;
HttpResponseMessage response = new HttpResponseMessage();
string data = "test=something";
response = await httpClient.PostAsync(serverUri,
new HttpStringContent(data, Windows.Storage.Streams.UnicodeEncoding.Utf8,
"application/json"));
(serverUri, HttpCompletionOption.ResponseContentRead);
responseData = await response.Content.ReadAsStringAsync();
This will help you to extract all available certificates from Server using URL certificate object like Root, Intermediate and child. One server could have multiple Intermediate certificates.
Also, OID which we had used into the following source code is Global for RSA algorithems, not sure it will work for all URLs but still we have checked for almost ten different sites with different root certificates and it worked as expected.
for(int i = 0;
i < response.RequestMessage.TransportInformation.ServerIntermediateCertificates.Count; i++)
{
Certificate aCertificate =
response.RequestMessage.TransportInformation.ServerIntermediateCertificates[i];
IBuffer buffer = aCertificate.GetCertificateBlob();
byte[] bCert = buffer.ToArray();
string scert = BitConverter.ToString(bCert);
byte[] rsaOID = EncodeOID("1.2.840.113549.1.1.1");
string sOID = BitConverter.ToString(rsaOID);
int length;
int index = FindX509PubKeyIndex(bCert, rsaOID, out length);
if (index > -1)
{
byte[] X509PublicKey = new byte[length];
Array.Copy(bCert, index, X509PublicKey, 0, length);
URLCertPublicKey = BitConverter.ToString(X509PublicKey);
txtServerPK.Text = URLCertPublicKey;
}
While we had referred source code for extracting Public Key from given OID from MSDN Blog using Base64String please refer the same, while code which is available on this blog has been modified as follows for the following routine:
if (index > -1)
{
while (index > 0 && Reference[index] != 0x30) index++;
while (index > 0 && Reference[index] != 0x30) index++;
}
Following function will help you to get public key from certificate available on your device.
private async Task<string> GetClientPublicKey()
{
string DevCertPublicKey = null;
string CertFileName = "GeoTrustGlobalCArootcert.cer";
try
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
if (folder != null)
{
StorageFile file = await folder.GetFileAsync(CertFileName);
IBuffer CertBlob = await FileIO.ReadBufferAsync(file);
Certificate rootCert =new Certificate(CertBlob);
IBuffer buffer1 = rootCert.GetCertificateBlob();
byte[] bCert1 = buffer1.ToArray();
string scert1 = BitConverter.ToString(bCert1);
byte[] rsaOID1 = EncodeOID("1.2.840.113549.1.1.1");
string sOID1 = BitConverter.ToString(rsaOID1);
int length1;
int index1 = FindX509PubKeyIndex(bCert1, rsaOID1, out length1);
if (index1 > -1)
{
byte[] X509PublicKey1 = new byte[length1];
Array.Copy(bCert1, index1, X509PublicKey1, 0, length1);
DevCertPublicKey = BitConverter.ToString(X509PublicKey1);
txtClientPK.Text = DevCertPublicKey;
}
return DevCertPublicKey;
}
return DevCertPublicKey;
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
Once user has both public keys, i.e., from client certificate and server certificate, comparing them will give the result whether certificates pinning is successful or failed. We are able to extend certificate pinning on WebView successfully. In webview, on Start_Navigation
event will able to validate each certificate before redirecting to target URL.
Certificate Pinning Issues
- Might have application performance issues due to certificate pinning.
- Certificate embedded in your app will eventually expire. You will have to either plan for an app update that contains an updated certificate, or code a way for the application to download the new certificate.
Points of Interest
We really spend lot of efforts to achieve this success, but finally we achieved what we want and that feeling itself only helped us to forget all problems and issues we faced.....really it was great experience and teamwork!
From my team Mukesh Gupta, Preeti Arora, Atanu Ray, Santhiya Raman and all others also worked with me to achieve this success.