背景
Let's Encryptの証明書は90日で期限が切れちゃうので、定期的に期限を確認して、切れそうだったら教えてくれるプログラムを作りたいと思った。
しかし、いまいちC#でサーバ証明書を取得する正しい方法がわからない…
思いついた方法で無理やり作ったので、知っている人いたら教えてください。
プログラム
HttpClient
は、内部でSSLサーバ証明書を取得していて、有効かどうかを判定している。
HttpClientHandler
のServerCertificateCustomValidationCallback
を指定すると、証明書が有効かどうかの判定を上書きできる。
その時に証明書にアクセスできるので、それを取り出せば、めでたくサーバ証明書を取得できる。
public static async Task<X509Certificate2> GetCertificateAsync(string uri)
{
var tcs = new TaskCompletionSource<X509Certificate2>();
_ = Task.Run(async () =>
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_1, c, _2, _3) =>
{
tcs.TrySetResult(new X509Certificate2(c.RawData));
return false;
},
};
try
{
using (var hc = new HttpClient(handler))
{
await hc.GetAsync(uri);
}
}
catch (Exception exp)
{
tcs.TrySetException(exp);
}
});
return await tcs.Task;
}
ServerCertificateCustomValidationCallback
で証明書にアクセスできるが、その証明書はすぐに破棄されてしまうようなので、RawData
を使って証明書を複製している。WEBページの内容を取得したいわけじゃないので、false
(証明書が無効であること)を返して、内容を取得しないことを期待している。
使い方
using (var certificate = await GetCertificateAsync("https://qiita.com/"))
{
Console.WriteLine($"[{certificate.NotBefore:yyyy/MM/dd HH:mm:ss}]から[{certificate.NotAfter:yyyy/MM/dd HH:mm:ss}]まで有効");
}