noxi雑記

.NET、Angularまわりの小ネタブログ

.NET で MSAL トークンを X.509 証明書で取ってみる

この記事は .NET Core 3.1 のデーモンコンソールアプリで MSAL トークンをクライアント ID と X.509 証明書で取得してみた時の作業メモです。
やったこと:

  1. Azure Key Vault で証明書作成
  2. Azure AD アプリ作成
  3. .NET からトークン取得

環境

  • Azure Key Vault
  • .NET Core 3.1 (Win 10 x64)
  • Microsoft.Identity.Client @ 4.27.0
  • Microsoft.Graph @ 3.25.0

やったこと

1. Azure Key Vault で証明書作成

ただ証明書を作りたいだけなのでどこで作っても良いのですが、今回は Key Vault から作成しました。作成時のオプションはフラグを「デジタル署名」のみに変更した以外はデフォルト値です。

f:id:noxi515:20210223222139p:plain
証明書作成時のオプション

生成された証明書を pfx ファイルでダウンロードします。ダウンロードした pfx をクライアントにインストールします。そして証明書ツール( certmgr )から秘密キーを含まない cer ファイルとしてエクスポートします。証明書ツールはスタートメニューから cert で検索するとアクセスしやすいです。個人とコンピューターとで違うので注意しましょう。私は個人の方にインポートました。

f:id:noxi515:20210223222210p:plain
証明書のエクスポート

登録した証明書の拇印は証明書ツールまたは PowerShellGet-ChildItem Cert:\CurrentUser\My から確認できます。

f:id:noxi515:20210223222243p:plain
PowerShell で証明書の拇印を確認してみた例

2. Azure AD アプリ登録

コンソールアプリからの認証で使用する Azure AD アプリを登録します。登録後、「証明書とシークレット」から先にエクスポートした秘密キーを含まない cer ファイルをアップロードします。

3. .NET からトークン取得

最後に .NET コンソールアプリから Azure AD アプリのクライアント ID と証明書を使用してアクセストークンを取得します。

using System;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Identity.Client;

namespace ConsoleApp1
{
    class Program
    {
        private const string TenantId = "*** AAD Tenant ID ***";
        private const string ClientId = "*** AAD Client ID ***";
        private const string CertThumbprint = "*** x.509 Certificate Thumbprint ***";

        static async Task Main(string[] args)
        {
            // 証明書ストアから認証に使用する証明書を拇印で取得
            using var store = new X509Store();
            store.Open(OpenFlags.ReadOnly);

            var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, CertThumbprint, false);
            var certificate = certificates.OfType<X509Certificate2>().Single();

            // ConfidentialなIdentityClientをクライアントIDと証明書で生成
            var app = ConfidentialClientApplicationBuilder.Create(ClientId)
                .WithCertificate(certificate)
                .WithTenantId(TenantId)
                .Build();

            // MS Graphからユーザー一覧を取得
            var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async req =>
            {
                // アクセストークン取得
                // "xxx//.default" で取らないと怒られる
                // Azure AD上でスコープの設定を忘れずに
                var scopes = new[] { @"https://graph.microsoft.com//.default" };
                var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
                req.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
            }));
            var users = await graphServiceClient.Users.Request().GetAsync();

            Console.ReadLine();
        }
    }
}