🍐
.NET MAUIでオレオレ証明書を使ったクライアント認証
序
いわゆる「オレオレ証明書」はその名前からか、世間の風当たりが強い今日この頃です。「母さん助けて証明書」に改名すればイメージが改善するのでしょうか? OS ベンダーによる包囲網が狭まり、使用にあたっては肩身が狭いというか、どんどん実装が難しくなっているような気がします。
今回は、クライアント証明書を使った HTTPS 通信を MAUI でどう実現するか、Windows・Android・iOS でどう共通実装したかについて解説します。
いきなり解答
クライアント認証を受けダウンロード.cs
private HttpClientHandler _handler;
private HttpClient _request;
private ManualResetEvent _event = new ManualResetEvent(false);
init()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2("p12 ファイル"));
store.Close();
_handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
ClientCertificateOptions = ClientCertificateOption.Automatic
};
}
public bool download()
{
_request = new HttpClient(_handler) { BaseAddress = new Uri("サーバー"), Timeout = TimeSpan.FromSeconds(秒) };
_event.Reset();
downloadAsync();
if (_event.WaitOne(ミリ秒))
{
return true;
}
_request.CancelPendingRequests();
_request.Dispose();
return false;
}
private async void downloadAsync()
{
try
{
using (var stream = await _httpRequest.GetStreamAsync("ファイル名"))
{
using (var file = new FileStream("保存先"), FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(file);
}
}
_event.Set();
}
catch
{
}
return;
}
実は、Windows では動作するのですが、Android では SSL 通信で例外が発生、iOS ではそもそも X509Store に追加できません。X509Store に追加しないで Manual モードでClientCertificates.Add()
してもダメです。試行錯誤している中で確認したエラーメッセージから推測するに、おそらくオレオレ証明書だから例外が発生するのであって、きちんとした証明書なら問題なく動くのだと思います(実証はしてませんが…)。
Andoid では別途、鼻薬を嗅がせる必要あり
Android では接続先の信頼性判定に使う CA 証明書がオレオレだと、「この証明書は信頼できる認証機関のものではありません」と通信を拒否します。これの回避方法は公式ドキュメントにあります。
下記のように、接続先と対応する CA 証明書を列挙し、AndroidManifest.xml
に食わせます。オレオレ CA 証明書でも大丈夫です。既出のHttpClient
を使った実装でも、これから紹介するHttpWebRequest
を使った実装でも、必須の作業となります。
network_security_config.xml
<?xml version="1.0" encoding="utf-8" ?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">接続先</domain>
<trust-anchors>
<certificates src="@raw/(拡張子を除くファイル名)" />
...
</trust-anchors>
</domain-config>
</network-security-config>
では、どうする?
HttpClient
は諦めます。いつまで通用するかわかりませんがHttpWebRequest
を使います。
クライアント認証を受けダウンロード(その2).cs
private HttpWebRequest _request;
private ManualResetEvent _event = new ManualResetEvent(false);
public bool download()
{
_request = (HttpWebRequest)WebRequest.Create("サーバー");
_request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
_request.ClientCertificates.Add(new X509Certificate2("p12 ファイル"));
_event.Reset();
downloadAsync();
if (_event.WaitOne(ミリ秒))
{
return true;
}
_request.Abort();
return false;
}
private async void downloadAsync()
{
try
{
using (var response = await _request.GetResponseAsync())
{
using (var stream = response.GetResponseStream())
{
using (var file = new FileStream("保存先"), FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(file);
}
}
}
_event.Set();
}
catch
{
}
return;
}
(了)
Discussion