-
Notifications
You must be signed in to change notification settings - Fork 0
MS_ASPNETClientCert
-
「Certificate Binding」
(OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens
(MS_OAuthMTLS.md))で必要になったため。 -
開発環境で設定する場合は、1 台の中で、
- クライアント設定と
- サーバ設定の
両方を行う必要がある。
補足(mTLS の全体像): 本ページが扱うのは
**クライアント証明書による相互 TLS 認証(mTLS)**の設定である。
各節に入る前に、全体の流れを押さえておく。【通常の TLS と mTLS】★ 通常の TLS(片方向) クライアント →→ サーバ証明書を検証 →→ サーバ → 「サーバが本物か」だけを確認する 【mTLS(相互 TLS)】 クライアント ←→ 互いに証明書を検証 ←→ サーバ → 「クライアントが誰か」も TLS 層で確認する ★ → ID/パスワードやトークンより前の段階で 接続そのものを拒否できる 【使われる場面】 ・【金融 API(FAPI)】★ ─ 本ページの動機である OAuth 2.0 mTLS Client Authentication ・システム間連携(B2B の専用 API) ・ゼロトラスト環境でのデバイス認証 ・Kubernetes のサービス間通信(サービス メッシュ) 【証明書の流れ(本ページの構成)】 ① クライアントに証明書を入れる(ブラウザ / HttpClient) ② Web サーバで「証明書を要求する」よう設定する (IIS / IIS Express / Kestrel / Apache / nginx) ③ アプリ側で受け取った証明書を読む (ASP.NET / ASP.NET Core)
クライアントがサーバにクライアント証明書を渡すよう構成する。
移行メモ(誤字): 移行元では「クラアント」(「イ」が欠落)と
記載されている箇所が複数あったため、「クライアント」に統一した。
- Windows版 - cybozu.com ヘルプ
https://jp.cybozu.help/ja/settings/browser/certificate/windows.html
- Mac版 - cybozu.com ヘルプ
https://jp.cybozu.help/ja/settings/browser/certificate/mac.html
-
HttpClient でクライアント証明書を設定する方法
https://qiita.com/volpe28v/items/95efad9e8816b00b348c -
HttpClient, HttpClientHandler, and WebRequestHandler Explained – Henrik's Blog
https://blogs.msdn.microsoft.com/henrikn/2012/08/07/httpclient-httpclienthandler-and-webrequesthandler-explained/
補足(
HttpClientでクライアント証明書を付ける現在の書き方):
.NET Core 以降はSocketsHttpHandlerが既定になり、書き方が整理された。// .NET Core / .NET 5 以降 var handler = new HttpClientHandler(); handler.ClientCertificates.Add( new X509Certificate2("client.pfx", "password")); // 自己署名のサーバ証明書を検証したい場合(開発時のみ) // handler.ServerCertificateCustomValidationCallback = // HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; var client = new HttpClient(handler);【HttpClient の使い方の注意(本題より重要)】★ ① 【using で毎回捨ててはいけない】 → ソケットが TIME_WAIT で滞留し、 【ソケット枯渇】を起こす → 静的に 1 つ持つか、 【IHttpClientFactory】を使う ★ ② 【長生きさせすぎても DNS 変更に追随しない】 → IHttpClientFactory が ハンドラを定期的に作り直して解決する ③ 証明書付きのハンドラは → 名前付きクライアントとして DI に登録する services.AddHttpClient("mtls") .ConfigurePrimaryHttpMessageHandler(() => handler);【証明書の置き場所】★ ・開発 … ファイル(.pfx)+ ユーザー シークレット ・本番 … 【Windows 証明書ストア】または 【Azure Key Vault】等 ★ → パスワード付き pfx をリポジトリに置かない ・X509Certificate2 は IDisposable → .NET 6 以降、明示的な破棄が推奨されている
- サーバがクライアントからクライアント証明書を受けるよう構成する。
- クライアント証明書(証明書 を参照)は
Web -> AP に HTTP ヘッダで渡される。- 例えば、Azure Web Apps では、
X-ARR-ClientCertヘッダを使用して渡される。 - 他の中間層では、
X-Client-Certヘッダなどが使用されるらしい。
- 例えば、Azure Web Apps では、
移行メモ(誤字): 移行元の「例えは、Azure Web Apps では」を
「例えば」に修正した。
補足(ヘッダで渡す方式のセキュリティ上の注意): 本節が指摘する
「Web → AP に HTTP ヘッダで渡される」という構成には、重大な前提がある。【なぜヘッダで渡すのか】★ TLS の終端(証明書の検証)は 【前段のリバース プロキシ / ロード バランサ】が行う → アプリ サーバには 平文の HTTP で転送される → よって証明書の情報は 【HTTP ヘッダに詰め替えて】渡すしかない 代表的なヘッダ名 Azure App Service : X-ARR-ClientCert(Base64 の DER)★ nginx : 設定次第(X-SSL-CERT 等を自分で定義) Apache : SSL_CLIENT_CERT(環境変数) AWS ALB : X-Amzn-Mtls-Clientcert Kubernetes Ingress: 実装依存 → 【標準がない】ため、原文の「らしい」は正しい ★ 【致命的な落とし穴】★★ このヘッダは【クライアントが自分で付けられる】 → 前段プロキシが 【受信した同名ヘッダを必ず削除・上書きする】 設定になっていないと、 【任意の証明書になりすませる】 → 認証が完全に破られる 【必ず確認すること】 ① プロキシが該当ヘッダを【常に上書き】する設定か ② アプリ サーバが 【プロキシからの接続しか受け付けない】か (ネットワーク分離 / 相互認証) ③ ASP.NET Core なら ForwardedHeadersOptions の KnownProxies / KnownNetworks を設定する ★
- IISでのクライアント証明書利用設定入門
https://rms.ne.jp/howto/basis/iis_client_cert.html
applicationhost.config(IIS Express を参照)に
以下の様な設定を行う。
<security>
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />
...
<authentication>
<iisClientCertificateMappingAuthentication enabled="true">
</iisClientCertificateMappingAuthentication>
...
</security>- 参考
- How to Configure IIS Express to Accept SSL Client Certificates – Improve & Repeat
https://improveandrepeat.com/2017/07/how-to-configure-iis-express-to-accept-ssl-client-certificates/
- How to Configure IIS Express to Accept SSL Client Certificates – Improve & Repeat
補足(
sslFlagsの意味): 上記 3 つのフラグは役割が違うので
明示しておく。【sslFlags の組み合わせ】★ Ssl … SSL/TLS を必須にする SslNegotiateCert … クライアント証明書を【要求する】 (提示されなくても接続は続く) SslRequireCert … クライアント証明書を【必須にする】★ (なければ 403.7 で拒否) Ssl128 … 128bit 以上を要求(現在は無意味) → 「証明書があれば使う、無ければパスワード認証」 という併用型にしたい場合は 【SslRequireCert を外す】★ 【applicationhost.config の場所】 ・VS 2015 以降のソリューション固有 <ソリューション>\.vs\config\applicationhost.config ★ ・ユーザー既定 %USERPROFILE%\Documents\IISExpress\config\applicationhost.config → 前者を編集しないと反映されないことが多い
-
Microsoft Docs
- ASP.NET Core での HTTPS の適用
https://learn.microsoft.com/ja-jp/aspnet/core/security/enforcing-ssl - ASP.NET Core での Kestrel Web サーバー
https://learn.microsoft.com/ja-jp/aspnet/core/fundamentals/servers/kestrel
- ASP.NET Core での HTTPS の適用
-
ASP.NET
https://github.com/aspnet- Docs
- How to setup the dev certificate when using Docker in development · Issue #6199
https://github.com/aspnet/Docs/issues/6199
- How to setup the dev certificate when using Docker in development · Issue #6199
- KestrelHttpServer
- How to set ClientCertificateMode in core 2.0? · Issue #2005
https://github.com/aspnet/KestrelHttpServer/issues/2005
- How to set ClientCertificateMode in core 2.0? · Issue #2005
- Docs
補足(Kestrel でクライアント証明書を要求する現在の書き方):
本ページ執筆時(.NET Core 2.0 頃)は Issue を追う必要があったが、
現在は正式な API がある。// Program.cs(.NET 6 以降の最小 API 形式) builder.WebHost.ConfigureKestrel(o => { o.ConfigureHttpsDefaults(https => { https.ClientCertificateMode = ClientCertificateMode.RequireCertificate; // ★ https.ClientCertificateValidation = (cert, chain, errors) => { // 自己署名や独自 CA を許容する場合の独自検証 return true; // ← 本番では必ず実装すること }; }); });【ClientCertificateMode の 4 値】★ NoCertificate … 要求しない(既定) AllowCertificate … 要求するが、無くても接続を許す RequireCertificate … 必須。無ければ接続を切る ★ DelayCertificate … 【接続後に改めて要求する】 (.NET 5 以降。特定のパスだけ 証明書を求めたい場合に使う)★ 【認証ミドルウェアとして扱う】★ Microsoft.AspNetCore.Authentication.Certificate パッケージを使うと、 証明書を【ClaimsPrincipal に変換】できる builder.Services .AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(o => { o.AllowedCertificateTypes = CertificateTypes.All; o.Events = new CertificateAuthenticationEvents { OnCertificateValidated = ctx => { /* 検証 */ } }; }); → [Authorize] で保護でき、 User.Identity から証明書の主体を取れる ★
移行メモ(未執筆): 移行元では Linux 配下の 3 節
(Kestrel / Apache / nginx)が見出しのみで本文がない。
見出しは残したうえで、以下に補足を追記した。
補足(Linux 側の設定の要点): 未執筆の 3 節について骨子を示す。
【Kestrel(Linux)】★ → 前掲の Windows 版と【全く同じ】 ClientCertificateMode の設定でよい → Kestrel はクロスプラットフォームであり、 OS による差はない ※ 証明書ストアの扱いだけが違う (Linux には Windows 証明書ストアがないため ファイル or 環境変数から読む)【Apache(mod_ssl)】 SSLVerifyClient require # 必須にする ★ SSLVerifyDepth 2 SSLCACertificateFile /path/ca.crt # AP サーバへ転送する場合 SSLOptions +ExportCertData # SSL_CLIENT_CERT を有効化 ★ RequestHeader set X-Client-Cert "%{SSL_CLIENT_CERT}s" RequestHeader unset X-Client-Cert early # ← 【なりすまし対策】★【nginx】 ssl_client_certificate /path/ca.crt; ssl_verify_client on; # optional にすると任意 ★ ssl_verify_depth 2; location / { proxy_pass http://backend; proxy_set_header X-Client-Cert $ssl_client_escaped_cert; # ★ proxy_set_header X-Client-Verify $ssl_client_verify; proxy_set_header X-Client-DN $ssl_client_s_dn; } ※ $ssl_client_cert は【改行を含む】ため ヘッダに載せると壊れる → 【$ssl_client_escaped_cert】(URL エンコード済み)を使う ★ ※ proxy_set_header で毎回上書きすることが そのまま【なりすまし対策】になる
- Azure WebSitesでClientCertificateを取得するためのコードと設定
https://qiita.com/higty/items/e0d2e1331bded9820aff
- Page
// System.Web.HttpRequest
HttpClientCertificate cs = Request.ClientCertificate;- Controller
// System.Web.HttpRequestBase
HttpClientCertificate cs = Request.ClientCertificate;- ApiController
// System.Net.Http.HttpRequestMessage
// System.Net.Http.HttpRequestMessageExtensions
X509Certificate2 cs = Request.GetClientCertificate();移行メモ(誤字): 移行元の「ApiControler」を「ApiController」に修正した。
-
netstandard2.0 時点では、API は用意されていない模様。
-
参考
- Azure Web App Client Certificate Authentication with ASP.NET Core | Kirk Evans Blog
https://blogs.msdn.microsoft.com/kaevans/2016/04/13/azure-web-app-client-certificate-authentication-with-asp-net-core-2/ - Azure Web Apps がクライアント証明書に対応したので自己署名証明書で試してみた - しばやん雑記
https://blog.shibayan.jp/entry/20150706/1436150053
- Azure Web App Client Certificate Authentication with ASP.NET Core | Kirk Evans Blog
-
(HTTP ヘッダの標準が無いため、)Web-AP の連携が必要なので
- IIS に限定される .NET には実装されているが、
- netcoreapp には実装されていない。
みたいなコトなのだろうか?
-
-
netstandard2.1 で、実装された模様。
// Microsoft.AspNetCore.Http.HttpRequest
X509Certificate2 x509 = Request.HttpContext.Connection.ClientCertificate;-
補足(作者の推測への回答と、現在の実装): 「HTTP ヘッダの標準が無いため
実装されていないのでは?」という推測は、概ね正しい。【なぜ ASP.NET と ASP.NET Core で違ったのか】★ ASP.NET(.NET Framework) → 実行基盤が【IIS 一択】 → IIS が検証済みの証明書を ワーカープロセスへ渡す仕組み(ISAPI)が 【最初から決まっていた】 → HttpRequest.ClientCertificate として提供できた ASP.NET Core → 【Kestrel / IIS / HTTP.sys / nginx / Apache】と ホストが多様 → 「証明書がどう届くか」が ホストごとに違うため、 【共通 API を先に作れなかった】★ → 原文の推測は本質を突いている 【解決の経緯】 ・ConnectionInfo.ClientCertificate → Kestrel が直接 TLS を終端する場合の 共通の入り口として整備された ・【前段プロキシ経由の場合】は依然として別問題 → Microsoft.AspNetCore.Authentication.Certificate と 【CertificateForwarding ミドルウェア】が用意された ★ builder.Services.AddCertificateForwarding(o => { o.CertificateHeader = "X-ARR-ClientCert"; o.HeaderConverter = h => new X509Certificate2(Convert.FromBase64String(h)); }); ... app.UseCertificateForwarding(); // UseAuthentication より前 ★ → これによりヘッダ経由でも ConnectionInfo.ClientCertificate と 同じように扱えるようになった【WebAPI 節が「-」である点について】 ASP.NET Core では 【MVC と WebAPI が同一のパイプライン】であるため、 別扱いする必要がない。 → ControllerBase でも同じく HttpContext.Connection.ClientCertificate で取れる ★ → 原文が「-」としているのは、この意味で正しい
(証明書 の該当節も参照)
以下で生成してみたが、ダメ。
- クライアント証明書の作り方 | 日々雑記
http://y-yagi.tumblr.com/post/18179788088/%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9
-
Visual Studio の署名で作成した PFX であれば利用可能だった。
-
OpenSSL と何が違うか不明。何か属性が足りてないのか?
-
一応、使用するには、
-
ブラウザから使用できるように設定する。
-
サーバから使用できるように設定する。
- Windows
- Linux
両方を行う必要がある。
-
補足(「何が違うか不明」への回答:おそらく EKU): 作者が保留にした
この疑問には、典型的な答えがある。【最有力の原因:拡張キー使用法(EKU)】★ 証明書には【何に使ってよいか】を示す拡張がある Extended Key Usage (EKU) ・serverAuth (1.3.6.1.5.5.7.3.1) … サーバ認証 ・【clientAuth】 (1.3.6.1.5.5.7.3.2) … クライアント認証 ★ ・codeSigning (1.3.6.1.5.5.7.3.3) … コード署名 ・OpenSSL の素朴な手順で作ると 【EKU が入らない】ことが多い → IIS / Windows は clientAuth のない証明書を 【クライアント証明書として選ばせない】★ ・Visual Studio の「署名」で作る PFX は codeSigning が入るが、 環境によっては通ってしまうことがある → 原文が「VS の PFX なら使えた」としているのは この辺りの差と考えられる 【OpenSSL で正しく作る】 # 拡張を書いたファイル(client_ext.cnf) keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth # ← これ ★ subjectAltName = email:user@example.com openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out client.crt -days 365 \ -extfile client_ext.cnf # ← 拡張を適用 ★ openssl pkcs12 -export -inkey client.key -in client.crt \ -certfile ca.crt -out client.pfx # CA も同梱する ★【他によくある原因】 ② 【CA 証明書が信頼されていない】 → サーバ側の「信頼されたルート証明機関」に CA を入れる必要がある → 自己署名(CA を介さない)だと チェーンが作れず弾かれる ★ ③ 【PFX に秘密鍵が入っていない】 → -export 時に -inkey を忘れている ④ 【証明書ストアの置き場所】 → ブラウザ(Chrome/Edge)は Windows の【現在のユーザー > 個人】を見る → サーバ側は【ローカル コンピューター】 ⑤ 【古い署名アルゴリズム】 → SHA-1 / 1024bit RSA は 現在の Windows / ブラウザで【拒否される】★ → SHA-256 / RSA 2048 以上、または ECDSA P-256
(OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens
(MS_OAuthMTLS.md)の該当節を参照)
- [Unity/C#]WWW/HttpWebRequestにおける
中間者攻撃の危険性を考慮した通信プログラムまとめ - Qiita
https://qiita.com/harmegiddo/items/b72ca4f430292251c8a6
Bouncy Castle で HTTP ヘッダから取得した pem を読む的なイメージでイイのでは?
補足(今なら Bouncy Castle は不要): この検討は
.NET Core 2.x 時代のものであり、現在は標準 API で完結する。// PEM からの読み込み(.NET 5 以降)★ var cert = X509Certificate2.CreateFromPem(pemCertText); // 秘密鍵も含める場合(.NET 6 以降) var certWithKey = X509Certificate2.CreateFromPem(pemCert, pemKey);【標準化された経緯】★ .NET Framework 時代 → PEM の読み書きが標準 API になく、 【Bouncy Castle】が定番だった .NET 5 → X509Certificate2.CreateFromPem 追加 .NET 6 → ImportFromPem / ExportPkcs8PrivateKeyPem 等が RSA / ECDsa にも追加され、【一通り揃った】 → 今から書くなら 【外部ライブラリなしで PEM を扱える】★ ※ Bouncy Castle が今も要るのは、 CMS / OpenPGP / 特殊な曲線 / 独自 ASN.1 など 標準 API が扱わない領域【ヘッダから受け取った証明書の検証で必ずやること】★ ① チェーン検証(信頼された CA から辿れるか) var chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.Build(cert); ② 有効期限(NotBefore / NotAfter) ③ 【失効確認】(CRL / OCSP)★ → 退職者・紛失端末の締め出しはここでしかできない ④ EKU に clientAuth が含まれるか ⑤ 【想定した主体(Subject / SAN / 拇印)と一致するか】★ → 「正しい CA が出した証明書」であることと 「その利用者であること」は別 ★ → OAuth mTLS では クライアント登録時の拇印と突き合わせる
Tags: 移行, .NET開発, .NET Core, ASP.NET, ASP.NET MVC, セキュリティ, 暗号化, 証明書
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。