Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# .NET - Pinning Certificates Authorities - I am doing it correctly?

Tags:

c#

.net

https

pki

ca

My software connects to Dropbox using an HTTPS connection in order to retrieve some sensitive data.

I would like to pin the Certificates Authorities in order to prevent a man-in-the-middle attack.

So far I have the following code:

static bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
        try
        {
            var currentCaPublicKey = chain.ChainElements.Cast<X509ChainElement>().Last().Certificate.GetPublicKeyString();

            var caPublicKeys = new List<string>(){"00ad0e15cee443805cb187f3b760f97112a5aedc269488aaf4cef520392858600cf880daa9159532613cb5b128848a8adc9f0a0c83177a8f90ac8ae779535c31842af60f98323676ccdedd3ca8a2ef6afb21f25261df9f20d71fe2b1d9fe1864d2125b5ff9581835bc47cda136f96b7fd4b0383ec11bc38c33d9d82f18fe280fb3a783d6c36e44c061359616fe599c8b766dd7f1a24b0d2bff0b72da9e60d08e9035c678558720a1cfe56d0ac8497c3198336c22e987d0325aa2ba138211ed39179d993a72a1e6faa4d9d5173175ae857d22ae3f014686f62879c8b1dae45717c47e1c0eb0b492a656b3bdb297edaaa7f0b7c5a83f9516d0ffa196eb085f18774f"};

            return caPublicKeys.Any(s => currentCaPublicKey.Equals(s));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return false;
        }
}

It works fine but I don't know if I am checking the right things. Any advice from some crypto experts would be greatly appreciated.

like image 658
John Avatar asked Oct 16 '17 18:10

John


1 Answers

Your code looks correct for pinning to the Root CA public key.

HPKP requires that you provide at least one backup pin, however, and I would recommend that you follow that guidance. Given that you are pinning to a Root CA it would be appropriate to provide the public key of another Root CA as the backup to mitigate the risk of DoS should something happen to the first CA, e.g., out of business.

Of course, your code accommodates multiple public keys to pin against, so it would merely be a matter of adding the additional key to your list of strings.

Cheers

EDIT 2017.10.23

Here is a sample of what I think reasonable Root CA Public Key pinning and Cert Validation should look like. This quick sample was done in a WebApi project, thus the boiler plate values controller.

Please note that only one (1) Root CA public key is used in my sample, and as mentioned above, a backup pin should be provided (minimum 2 array elements).

This is a sample and not intended to be taken as production code - I suggest that the following be subjected to peer / security review:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Web.Http;

namespace CertPinPocClient.Controllers
{
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            ServicePointManager.ServerCertificateValidationCallback = new PinnedRootCaCertificate(new[]
            {
                "MIICCgKCAgEAzHYh7u+V5haaRSoGSVGm/gC4EYvZHkBR3/c/kQvTJeh1L9Bn/b7U1s7onw85SjvpZ28ohoT7p4vJRoNUBemR6hf3TM1mZmSE0tqnLGzBV9H4Nfrxx1+cubxYyYaOJ8iJfp1XslGGyZqQmUFFjWOUuU9cvOAbz4DqBIUn344JhG0xEHCf5IOF0gfuWE8yQC9vIjlveUQQ7dq/rDNZcQjqDhEb6DcF7za+1ZxjZdmtKewoYgDBPqzf66Gwi85BZsEcYFQTbjzvAhYaq4xPhJF6iPS4ihf+zjnMPxmy2oH1bm8n2fVuyxqV5JgIDU0ualx728UhfJUjcoBl57OLVsiJIdHFHpcDhN8Fn5QUGkNPgQqX27R1aw/+t2HfYTEsg6urH3aam8e7qRKUEXJs8qMKnXZ15aY0zlO7DLtfnK5tq2Cnu+HBBo4FlDhRO4kTBZOisFkvkEWI/Nj6jioOyMWsTsUvOdDK5KUpWZazpc3rwCvQy3KwBz6EyPU7ihrTm+nqqK5wiI9YwRcMjsPRBZfAur1cB0hNi+g98+2zzj+hwyR49KkOzFowp5MvXEWhnYDrY4cHSJ7zSdgMdO9HWPMke1HuKOUuUUUIpQMvPmFDAh4WQpAKqGvI/cOZeubnSwVMQra13QviYdlUeT56tFDTjgdbUNyBy0gxcFPVgTjzTj8CAwEAAQ==",
            }).Valid;

            var httpClient = new HttpClient
            {
                BaseAddress = new Uri("https://local.monitor.iontech.org")
            };

            var httpResponseMessage = httpClient.GetAsync(new Uri("https://local.monitor.iontech.org/api/status/")).Result;
            var result = httpResponseMessage.Content.ReadAsStringAsync().Result;
            return new[] {result};
        }
    }

    public class PinnedRootCaCertificate
    {
        private readonly string[] _rootCaPublicKeys;

        public PinnedRootCaCertificate(string[] rootCaPublicKeys)
        {
            _rootCaPublicKeys = rootCaPublicKeys;
        }

        public bool Valid(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
        {
            if (sslpolicyerrors != SslPolicyErrors.None) return false;

            var rootCertificate = SelfSignedCertificate(chain);
            var publicKey = Convert.ToBase64String(rootCertificate.PublicKey.EncodedKeyValue.RawData);
            return rootCertificate.Verify() && _rootCaPublicKeys.Contains(publicKey);
        }

        private X509Certificate2 SelfSignedCertificate(X509Chain chain)
        {
            foreach (var x509ChainElement in chain.ChainElements)
            {
                if (x509ChainElement.Certificate.SubjectName.Name != x509ChainElement.Certificate.IssuerName.Name) continue;
                return x509ChainElement.Certificate;
            }
            throw new Exception("Self-signed certificate not found.");
        }
    }
}
like image 156
Jim Speaker Avatar answered Sep 20 '22 16:09

Jim Speaker