Created
September 23, 2020 15:24
-
-
Save JBirdVegas/a9abc62d8e9d41698f271530013c6879 to your computer and use it in GitHub Desktop.
Certificate pinning example using C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Net; | |
using System.Net.Security; | |
using System.Security.Cryptography.X509Certificates; | |
using System.Text.Json; | |
namespace CertificatePinningTutorial | |
{ | |
internal class Program | |
{ | |
private readonly string _domain; | |
private Program(string domain) | |
{ | |
_domain = domain; | |
} | |
private String GetSha1ThumbprintFromApi() | |
{ | |
ServicePointManager.ServerCertificateValidationCallback = null; | |
var requestUriString = $"https://api.cert.ist/{_domain}"; | |
WebRequest request = WebRequest.Create(requestUriString); | |
HttpWebResponse response = (HttpWebResponse) request.GetResponse(); | |
StreamReader reader = new StreamReader(response.GetResponseStream()!); | |
CertIstApi certIstApi = JsonSerializer.Deserialize<CertIstApi>(reader.ReadToEnd()); | |
return certIstApi.certificate.hashes.sha1; | |
} | |
private void ValidateDomain() | |
{ | |
try | |
{ | |
WebRequest.DefaultWebProxy = null; | |
ServicePointManager.ServerCertificateValidationCallback = PinPublicKey; | |
WebRequest wr = WebRequest.Create($"https://{_domain}"); | |
wr.GetResponse(); | |
} | |
catch (WebException) | |
{ | |
Environment.Exit(1); | |
} | |
} | |
// ReSharper disable once UnusedParameter.Global | |
public static void Main(string[] _) | |
{ | |
new Program("wordpress.com").ValidateDomain(); | |
new Program("urip.com").ValidateDomain(); | |
new Program("xkcd.com").ValidateDomain(); | |
new Program("cert.ist").ValidateDomain(); | |
} | |
private bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain, | |
SslPolicyErrors sslPolicyErrors) | |
{ | |
var keyDerFromApis = GetSha1ThumbprintFromApi(); | |
if (null == certificate) | |
{ | |
return false; | |
} | |
var cert = new X509Certificate2(certificate); | |
var isPinnedThumbprintValid = | |
cert.Thumbprint != null && cert.Thumbprint.ToLower().Equals(keyDerFromApis.ToLower()); | |
Console.WriteLine($"{_domain} certificate sha1 from api: {keyDerFromApis}, is valid: {isPinnedThumbprintValid}"); | |
return isPinnedThumbprintValid; | |
} | |
} | |
} | |
// this is the minimum we can deserialize to pin the certificate | |
// ReSharper disable once ClassNeverInstantiated.Global | |
public class CertIstApiKeyStats | |
{ | |
// ReSharper disable once UnusedAutoPropertyAccessor.Global | |
// ReSharper disable once InconsistentNaming | |
public CertIstApiHashes hashes { get; set; } | |
} | |
// ReSharper disable once ClassNeverInstantiated.Global | |
public class CertIstApi | |
{ | |
// ReSharper disable once UnusedAutoPropertyAccessor.Global | |
// ReSharper disable once InconsistentNaming | |
public CertIstApiKeyStats certificate { get; set; } | |
} | |
// ReSharper disable once ClassNeverInstantiated.Global | |
public class CertIstApiHashes | |
{ | |
// ReSharper disable once UnusedAutoPropertyAccessor.Global | |
// ReSharper disable once InconsistentNaming | |
public string sha1 { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment