Skip to content

Instantly share code, notes, and snippets.

@jpda
Created February 6, 2019 22:37
Show Gist options
  • Save jpda/3d503086047fee05c4730c28a53633d6 to your computer and use it in GitHub Desktop.
Save jpda/3d503086047fee05c4730c28a53633d6 to your computer and use it in GitHub Desktop.
ca-verify
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
namespace HttpPrivateCa
{
class Program
{
static void Main(string[] args)
{
var a = new ApiCaller();
a.MakeRequest().Wait();
Console.ReadLine();
}
}
public class ApiCaller
{
private readonly HttpClient _client;
public ApiCaller()
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
return VerifyServerCert(this, cert, chain, errors);
}
};
_client = new HttpClient(handler);
}
public async Task MakeRequest()
{
var d = await _client.GetAsync("https://badssl.jpd.ms/");
Console.WriteLine($"{await d.Content.ReadAsStringAsync()}");
}
//see https://stackoverflow.com/questions/6497040/how-do-i-validate-that-a-certificate-was-created-by-a-particular-certification-a
static bool VerifyServerCert(object sender, X509Certificate2 certificate, X509Chain chainorigin, SslPolicyErrors sslPolicyErrors)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
// This part is very important. You're adding your known root here.
// It doesn't have to be in the computer store at all. Neither certificates do.
String CA_FILE = @"..\..\..\myca.cer";
X509Certificate2 authority = new X509Certificate2(CA_FILE);
chain.ChainPolicy.ExtraStore.Add(authority);
bool isChainValid = chain.Build(certificate);
if (!isChainValid)
{
string[] errors = chain.ChainStatus
.Select(x => string.Format("{0} ({1})", x.StatusInformation.Trim(), x.Status))
.ToArray();
string certificateErrorsString = "Unknown errors.";
if (errors != null && errors.Length > 0)
{
certificateErrorsString = String.Join(", ", errors);
}
throw new Exception("Trust chain did not complete to the known authority anchor. Errors: " + certificateErrorsString);
}
// This piece makes sure it actually matches your known root
var valid = chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint == authority.Thumbprint);
if (!valid)
{
throw new Exception("Trust chain did not complete to the known authority anchor. Thumbprints did not match.");
}
return valid;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment