C#.NETでのSSL証明書情報取得方法

2021年8月時点、C# .NETでのモダンなSSL証明書情報(URL, 署名者, 期限日等)取得方法について調査した。

結論的には.NET Core ではServicePoint.Certificateは使えなくなっているため、HttpClientHandler.ServerCertificateCustomValidationCallbackを用いて取得する。

確認環境

.NET Core 3.1 on Windows 10

正常動作するコード

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace GetExpirationDateTest
{
class Program
{
static void Main(string[] args)
{
List tasks = new List();
tasks.Add(Task.Run(() => GetExpirationDate(@”https://example.com/”)));
//tasks.Add(Task.Run(() => GetExpirationDate(@”https://example.com/”)));
Task.WhenAll(tasks);
Console.WriteLine(@”DONE”);
string sign = Console.ReadLine();
}

    static async Task GetExpirationDate(string url)
    {
        Console.WriteLine(url);

        // Create an HttpClientHandler object and set to use default credentials
        HttpClientHandler handler = new HttpClientHandler();

        // Set custom server validation callback
        handler.ServerCertificateCustomValidationCallback = ServerCertificateCustomValidation;

        // Create an HttpClient object
        HttpClient client = new HttpClient(handler);

        // Call asynchronous network methods in a try/catch block to handle exceptions
        try
        {
            HttpResponseMessage response = await client.GetAsync(url);

            response.EnsureSuccessStatusCode();

            string responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Read {responseBody.Length} characters");
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine("\nException Caught!");
            Console.WriteLine($"Message: {e.Message} ");
        }

        // Need to call dispose on the HttpClient and HttpClientHandler objects
        // when done using them, so the app doesn't leak resources
        handler.Dispose();
        client.Dispose();
    }

    private static bool ServerCertificateCustomValidation(HttpRequestMessage requestMessage, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslErrors)
    {
        // It is possible inpect the certificate provided by server
        Console.WriteLine($"Requested URI: {requestMessage.RequestUri}");
        Console.WriteLine($"Effective date: {certificate.GetEffectiveDateString()}");
        Console.WriteLine($"Exp date: {certificate.GetExpirationDateString()}");
        Console.WriteLine($"Issuer: {certificate.Issuer}");
        Console.WriteLine($"Subject: {certificate.Subject}");

        // Based on the custom logic it is possible to decide whether the client considers certificate valid or not
        Console.WriteLine($"Errors: {sslErrors}");
        return sslErrors == SslPolicyErrors.None;
    }
}

}

参考URL:

ServicePoint.Certificate プロパティ – .NET Core では常にnullになり使えないと言及

https://docs.microsoft.com/ja-jp/dotnet/api/system.net.servicepoint.certificate?view=net-5.0

HttpClientHandler.ServerCertificateCustomValidationCallback プロパティ – 使用サンプルあり

https://docs.microsoft.com/ja-jp/dotnet/api/system.net.http.httpclienthandler.servercertificatecustomvalidationcallback?view=net-5.0