Download, parse, and store SSL certificates in C#
As part of elmah.io Uptime Monitoring, we offer SSL expiration warnings. We recently both simplified the code and had to do some additional parsing of the common names in the certificates. This post is a summary of how we did that.
When dealing with SSL certificates in C#, that part is typically handled by the HTTP client used. In some cases, you may need to download and store the certificate. You may even need to parse the content of the various fields within the certificate. Well, this is the post for you.
I have seen a range of different tips (hacks basically) to useHttpClient
for fetching an SSL certificate. I even used some of them myself until seeing this post from Gérald Barré. To sum up, the easiest way to fetch a certificate from a URL can be done like this:RemoteCertificateValidationCallback certCallback = (_, _, _, _) => true;
using var client = new TcpClient("blog.elmah.io", 443);
using var sslStream = new SslStream(client.GetStream(), true, certCallback);
await sslStream.AuthenticateAsClientAsync(domain);
var serverCertificate = sslStream.RemoteCertificate;
var certificate = new X509Certificate2(serverCertificate);
The code uses the TcpClient
and SslStream
classes to fetch the certificate without a single HttpClient
. Then create a new X509Certificate2
instance which we will work with in the following sections. If you are running on a previous version of C#, make sure to add the following property to your csproj
file:
<LangVersion>9.0</LangVersion>
Once loaded into memory, a certificate can be easily saved by using the Export
method to generate a byte array representation and save it:
var content = certificate.Export(X509ContentType.Cert);
await File.WriteAllBytesAsync("certificate.cer", content);
So, how about displaying the fields from the SSL certificate. You've probably seen the Windows dialog to display an SSL certificate:
Let's build something similar, but for the command line. To illustrate how to parse the various fields within the certificate, I'll go through how we can display the Issued to field.
The X509Certificate2
class has a range of properties to fetch the different information about the certificate. The Issued to information is located in a property named Subject
. In security, the subject means the thing being secured. In this case the domain. Let's write that to the screen:
Console.WriteLine(certificate.Subject);
You might expect this to write out sni.cloudflaressl.com. Sorry to disappoint you:
CN=sni.cloudflaressl.com, O="Cloudflare, Inc.", L=San Francisco, S=California, C=US
That's the "distinguished name" (DName) of the subject. To get the common name (CN
) you could start parsing and splitting the text. Or you can install the Rfc2253
NuGet package by Eric Li:
dotnet add package Rfc2253
The package is great at parsing DNames like the one we already have in the subject:
var subject = DistinguishedName.Create(certificate.Subject);
var commonName = subject.Rdns.FirstOrDefault(x => x.Type.Value == "CN");
if (commonName != null)
{
Console.WriteLine(commonName.Value);
}
Rfc2253
parses the subject into an array of keys and values. All we have to do then is to find the common name by looking for the CN
type.
As expected, we now see the common name in the console:
sni.cloudflaressl.com
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website