しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Web Apps がクライアント証明書に対応したので自己署名証明書で試してみた

Azure 界の抱かれたい男 No.1 という異名を持ち、ブチザッキを書いている @kosmosebi から、Web Apps がクライアント証明書認証に対応したと教えてもらいました。

公式の Azure ブログを読みましたが、めっちゃあっさりと終わってしまっていました。

http://azure.microsoft.com/blog/2015/07/02/enabling-client-certificate-authentication-for-an-azure-web-app/

一応ドキュメントの方にクライアント証明書を有効にするための手順が紹介されているので、それを読んでおきます。やってることは clientCertEnabled を true に設定しているだけです。

Configure TLS mutual authentication - Azure App Service | Microsoft Learn

ドキュメントでは ARMClient を使っていますが、コメントで David Ebbo が書いているように Azure Resource Explorer を使っても出来ます。

ARMClient を使うより、Azure Resource Explorer の方が楽なのでお勧めしたいです。今は設定後に null と表示される問題があるらしいですが、設定自体は反映されているとのことです。

設定後に https で Web Apps にアクセスすると、証明書を選択するダイアログが表示されるようになります。

ここでキャンセルを選択すると、500 エラーが表示されるようになっているみたいです。

証明書の検証とかはどうするのという感じですが、先ほどのドキュメントにはしっかりと書かれています。

Special Considerations for Certificate Validation

The client certificate that is sent to the application does not go through any validation by the Azure Web Apps platform. Validating this certificate is the responsibility of the web app.

Configure TLS mutual authentication - Azure App Service | Microsoft Learn

要約するとクライアント証明書の要求と、送信された証明書は渡すところまでは Web Apps でやるので、後はアプリ側で何とかしろということです。見事なほどの丸投げで惚れ惚れする勢いですね。

つまり、結局のところ Web Apps では ARR に以下の記事のような設定を行うだけです。

Configuring ARR with Client Certificate - AsiaTech: Microsoft APGC Internet Developer Support Team - Site Home - MSDN Blogs

仕方ないので自己署名証明書を作ってクライアント証明書での認証が行えるところまで試してみました。証明書の作り方は以下の記事を参考にしました。というか、そのままです。

クライアント証明書の作り方 | 日々雑記

作成した PFX は Windows にインストールしますが、client-ca.crt の中身はアプリ側に埋め込んでおくことにしました。占有インスタンスならちゃんと動く気がします。

ARR からは X-ARR-ClientCert ヘッダーで Base64 エンコードされた証明書が送られてくるので、アプリ側でデコードして検証を行います。めんどくさくなってきたのでコードを載せます。

protected void Page_Load(object sender, EventArgs e)
{
    var clientCert = Request.Headers["X-ARR-ClientCert"];
    var x509Cert2 = new X509Certificate2(Encoding.ASCII.GetBytes(clientCert));

    var chain = new X509Chain();

    var verify = chain.Build(x509Cert2);

    if (!verify && chain.ChainStatus[0].Status == X509ChainStatusFlags.UntrustedRoot)
    {
        var root = chain.ChainElements[chain.ChainElements.Count - 1];
        verify = root.Certificate.Equals(_clientCaCert);
    }

    if (!verify)
    {
        Response.StatusCode = 401;
        Response.End();
    }
}

private const string ClientCa = "...";
private static readonly X509Certificate2 _clientCaCert = new X509Certificate2(Encoding.ASCII.GetBytes(ClientCa));

信頼された CA から発行されたクライアント証明書を使う場合には X509Certificate2 の Verify メソッドを呼ぶだけで良さそうですが、今回は自己署名証明書なので X509Chain を使いました。

検証に失敗した理由が UntrustedRoot だった場合、チェーンからルート CA の証明書を取ってきて埋め込んだ CA の証明書と同じか確認します。正直、これで合ってるのかは分かりません。

この後にアプリをデプロイするのですが、クライアント証明書を有効にすると Visual Studio からデプロイ不可能になるので、一時的にオフにしておく必要があります。不具合っぽいのでフィードバック済みです。

デプロイ後にアプリにアクセスすると作成したクライアント証明書が一覧に表示されるので、正しい証明書を選択します。この場合には問題なくアクセス可能なはずです。

他の証明書を選択した場合にはページは表示されないはずです。

このあたりの処理は HttpModule か Global.asax にまとめた方がいいと思いました。