How to download SSL/TLS certificates in C#

How to download SSL and TLS certificates in CsharpThis article is about how to download (or save) SSL/TLS certificates from any server by use of C#. Although nowadays certificates can be stored quite simply from the web browser, this is always associated with quite a few clicks. And at the latest when you want to store certificates from mail servers, etc., i.e. systems that can not be addressed directly in the web browser, a programmatic solution, as shown in this post, may be the easier way.

All in all, I would like to introduce two variants today. One variant, which works only for HTTPS connections and another variant, which works for all TCP connections (like mail servers, etc.).

Download HTTPS/SSL certificates in C#

The first addressed solution works only for HTTPS connections and is based on the HttpWebRequest class, which is also part of the well known WebClient class. If you create an HttpWebRequest, you can get an HttpWebResponse object from it.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://google.com:443");
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
response.Close();
X509Certificate2 cert = new X509Certificate2(request.ServicePoint.Certificate);

The certificate is taken from the request instance, but the response must be requested using the GetResponse() function to send the request to the server. Without a request, no certificate is stored in the request object. After running the request, the certificate can be taken from the request.ServicePoint.Certificate property. As object type, I have chosen X509Certificate2 because this class is more extensive than the (old) X509Certificate class.

Get SSL/TLS certificates from TCP connections

As promised, I would like to introduce a second variant. This is somewhat more flexible than the already shown variant, but also somewhat more complicated in the error handling. So we have to create a separate RemoteCertificateValidationCallback here so that we get the certificate in any case. Without this, the code would e.g. throw an exception when the certificate has expired. However, in this case, you may be above all interested in the certificate, which is why we create our own callback and always return “true” for the certificate to be drawn in any case.

try
{
    X509Certificate2 cert = null;
    var client = new TcpClient("imap.gmail.com", 993);
    var certValidation = new RemoteCertificateValidationCallback(delegate (object snd, X509Certificate certificate,
                X509Chain chainLocal, SslPolicyErrors sslPolicyErrors)
    {
        return true; //Accept every certificate, even if it's invalid
    });

    // Create an SSL stream and takeover client's stream
    using (var sslStream = new SslStream(client.GetStream(), true, certValidation))
    {
        sslStream.AuthenticateAsClient("imap.gmail.com");
        var serverCertificate = sslStream.RemoteCertificate;
        cert = new X509Certificate2(serverCertificate);
    }
}
catch (Exception e) {
    //throw some fancy exception ;-)
}

Then we create an SslStream from the TcpClient stream and access the SSL/TLS certificate via its RemoteCertificate property. As I said, this variant is not quite as nice as the first, but it offers the possibility to access certificates for almost all types of servers.

Leave a comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked