I recently needed to set up an alert when a certificate is close to expiring or uses an insecure signature algorithm. The first step is to retrieve the certificate. In .NET, you can use the SslStream class to handle the TLS handshake and access the server certificate. You can then schedule a recurring task using Azure Pipelines or GitHub Actions to validate certificates daily, though that setup is outside the scope of this post.
C#
static class CertificateDownloader
{
// Accept any certificate, even if the certificate is invalid
// We don't care about security here. The only goal is to get the certificate, not to transmit data.
private static readonly RemoteCertificateValidationCallback s_certificateCallback = (_, _, _, _) => true;
public static async Task<X509Certificate2?> GetCertificateAsync(string domain, int port = 443)
{
using var client = new TcpClient(domain, port);
using var sslStream = new SslStream(client.GetStream(), leaveInnerStreamOpen: true, s_certificateCallback);
// Initiate the connection, so it will download the server certificate
await sslStream.AuthenticateAsClientAsync(domain).ConfigureAwait(false);
// Duplicate the certificate because "serverCertificate" won't be accessible
// after disposing the stream, so not accessible outsite this method
var serverCertificate = sslStream.RemoteCertificate;
if (serverCertificate != null)
return new X509Certificate2(serverCertificate);
return null;
}
}
Here is how to use this class to retrieve a certificate:
C#
static async Task Main()
{
var certificate = await CertificateDownloader.GetCertificateAsync("www.meziantou.net");
Console.WriteLine($"Subject: {certificate.Subject}");
Console.WriteLine($"Issuer: {certificate.Issuer}");
Console.WriteLine($"NotBefore: {certificate.NotBefore}");
Console.WriteLine($"NotAfter: {certificate.NotAfter}");
Console.WriteLine($"Algorithm: {certificate.SignatureAlgorithm.FriendlyName}");
}
Do you have a question or a suggestion about this post? Contact me!