こんにちは。
テックリードのTerukiです。
今開発している機能で、mTLS通信を行う必要が出てきたので.NETでどうやるのか備忘も兼ねて記事にします。
だいぶニッチな気がしますが調べてもあまり情報が出てこなかったので今後誰かの役に立てば良いなと。
mTLSとは
知っとるわい!という人はスルーで大丈夫です。
相互TLS認証(略してmTLS)は、相互認証方式です。 mTLSは、ネットワーク接続の両端にいる当事者がお互いに正しい秘密鍵を持っていることを確認することで、彼らが主張する人物であることを保証します。各自のTLS証明書内の情報に従って、追加の検証が行われます。
私はサーバだけでなくクライアント側も証明書を使って相互に認証し合ったら安全だねというざっくり理解です。
.NETでのmTLS
mTLS通信をするにはHttpClientにクライアント証明書を読ませる必要があります。
クライアント証明書を読ませるにはHttpMessageHandlerをいじります。
HttpClientを直接使用
var handler = new SocketsHttpHandler();
var cert = X509Certificate2.CreateFromPem("---BEGIN CERTIFICATE-(略)", "---BEGIN PRIVATE KEY-(略)");
handler.SslOptions.ClientCertificates = [ cert ];
if (OperatingSystem.IsWindows()) {
cert = X509CertificateLoader.LoadPkcs12(cert.Export(X509ContentType.Pfx), null);
}
var httpClient = new HttpClient(handler);
.NET 5からpemファイルを直接読み込むことができるようになっているので非常に便利です。
ただし、Windowsの場合pemから読み込んだX509Certificate2を使うと下記のエラーが出るので一旦PFXに変換してあげる必要があります。悲しい。
Authentication failed because the platform does not support ephemeral keys.
サーバ環境でWindowsを使わないのであればWindowsの分岐部分を#if DEBUG等で囲んでも良さそうです。
Generic Host
今日の.NET開発ではASP.NET CoreなどのGeneric Hostを使ったものが多いと思うのでそのやり方も。こちらのほうがメインかも。
services.AddHttpClient<HogeHogeService>()
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler()) // .NET 9ではデフォルトでSocketsHttpClientなのでこの行は不要
.ConfigurePrimaryHttpMessageHandler((handler, serviceProvider) => {
if (handler is not SocketsHttpHandler socketsHttpHandler) {
throw new InvalidOperationException("何かがおかしいです");
}
var cert = X509Certificate2.CreateFromPem("---BEGIN CERTIFICATE-(略)", "---BEGIN PRIVATE KEY-(略)");
handler.SslOptions.ClientCertificates = [ cert ];
if (OperatingSystem.IsWindows()) {
cert = X509CertificateLoader.LoadPkcs12(cert.Export(X509ContentType.Pfx), null);
}
});
.NET 8まではAddHttpClientでDIに追加されたHttpClientのHandlerはHttpClientHandlerという古いものなのでSocketsHttpHandlerに差し替えます。
.NET 9では記載不要です。
もし、HttpClientHandlerが必要な場合は上記のコードを少しいじれば実現可能です。
使う場合は下記の通り。
コンストラクタインジェクションで設定済みのHttpClientを使えます。
public class HogeHogeService(HttpClient HttpClient) {
public async Task DoAsync() {
await HttpClient.SendAsync(/* 略 */).ConfigureAwait(false);
}
}
macOS 15でエラーになる
The specified item is no longer valid. It may have been deleted from the keychain.
Macで動かした時にこんな感じのエラーが出た場合は、インストールしているSDKをアップデートすると解消します。
.NET 6の場合は6.0.34、.NET 8の場合は8.0.10以降で解消します。
おわりに
Windowsでしか起きない問題やMacで起きる問題などで思ったより躓きました。
記事を見返すとmTLS通信の記事というよりは単にクライアント証明書の設定方法記事な気がしますがこれで動くので気にしないことにします
誰かの役に立てば幸いです。
では。
Oh my teethについて
Oh my teethでは未来の歯科体験を創るために日々活動しています。
Techチームではより良いユーザー体験を提供するべく、Webフロントエンドからバックエンド、スマホアプリに機械学習モデルなど、さまざまなプロダクトを開発しています。
一緒に未来の歯科体験を創りませんか?興味がある方は是非こちらを確認してください。
カジュアル面談も可能なので気軽に応募してみてください!