🔑
.NET で MSAL トークンを X.509 証明書で取ってみる
はじめに
この記事は .NET Core 3.1 のデーモンコンソールアプリで MSAL トークンをクライアント ID と X.509 証明書で取得してみた時の作業メモです。
やったこと:
- Azure Key Vault で証明書作成
- Azure AD アプリ作成
- .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 から作成しました。作成時のオプションはフラグを「デジタル署名」のみに変更した以外はデフォルト値です。
証明書作成時のオプション
生成された証明書を pfx ファイルでダウンロードします。ダウンロードした pfx をクライアントにインストールします。そして証明書ツール( certmgr )から秘密キーを含まない cer ファイルとしてエクスポートします。証明書ツールはスタートメニューから cert
で検索するとアクセスしやすいです。個人とコンピューターとで違うので注意しましょう。私は個人の方にインポートました。
証明書のエクスポート
登録した証明書の拇印は証明書ツールまたは PowerShell で Get-ChildItem Cert:\CurrentUser\My
から確認できます。
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();
}
}
}
Discussion