次の方法で共有


ローリング キーの所有証明トークンを生成し、プログラムで証明書を更新する

アプリケーションservicePrincipal リソースで定義されている addKeyremoveKey メソッドを使用して、期限切れ間近なキーをプログラムでローリングすることができます。

これらのメソッドの要求検証の一環として、メソッドを呼び出す前に、既存のキーの所有証明 (PoP) が検証されます。 証明は、自己署名 JSON Web トークン (JWT) によって表されます。 このトークンは、アプリケーションのいずれかの既存の有効な証明書の秘密キーを使用して署名する必要があります。 トークンの推奨有効期間は 10 分です。

この記事では、次の方法を示す C# のコード例を示します。

  1. 既存の有効な証明書を使用してクライアント アサーションを計算します。
  2. 生成されたクライアント アサーション キーを使用して PoP トークンを生成します。
  3. PoP トークンを使用して、 addKey メソッドを使用して新しい証明書をアプリまたはサービス プリンシパル オブジェクトにアップロードします。
  4. PoP トークンを使用して、 removeKey メソッドを使用してアプリまたはサービス プリンシパル オブジェクトから証明書を削除します。

重要

証明書がまだ追加されていないか、既存の証明書の有効期限が切れているため、既存の 有効な 証明書がないアプリケーションでは、このサービス アクションを使用できません。 代わりに、アプリケーションの 更新 操作を使用して keyCredentials プロパティを 更新します。 詳細については、「 Microsoft Graph を使用してアプリに証明書を追加する」を参照してください。

前提条件

  • ターゲット アプリまたはサービス プリンシパルに有効なクライアント証明書を設定します。
    • クライアント アサーション キーと PoP トークンを生成するには、有効な既存の vertificate の詳細が必要です。
      • テスト目的で、自己署名証明書を使用できます。 自己署名証明書を作成する方法については、「自己署名 パブリック証明書を作成してアプリケーションを認証する」を参照してください。
      • 秘密キーを使用して証明書を .pfx 形式でエクスポートします。 または、秘密キーのない公開証明書のみを必要とするようにスクリプトを更新することもできます。
  • PoP トークンを生成するアプリケーションまたはサービス プリンシパルのクライアント ID (API では appId と呼ばれます) とオブジェクト ID (API では id と呼ばれます)。

サンプル コード

トークンには、次の要求を含める必要があります。

  • aud: 対象ユーザーは 00000002-0000-0000-c000-000000000000する必要があります。
  • iss: Issuer は、要求を開始する アプリケーション または servicePrincipal オブジェクトの ID である必要があります。
  • nbf: 時間の前ではありません。
  • exp: 有効期限は nbf + 10 分の値にする必要があります。
using System;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using System.Net.Http;
using Microsoft.IdentityModel.Tokens;

namespace SampleCertCall
{
    class Program
    {
        static void Main(string[] args)
        {
            //=============================
            // Global variables which will be used to store app registation info, you can use appsettings.json to store such data
            //=============================
            string clientId = "Enter_the_Application_Id_Here"; //client ID or appId of the target app or service principal
            string tenantID = "Enter_the_Tenant_Id_Here"; // Tenant ID value
            string scopes = "https://graph.microsoft.com/.default"; // The "https://graph.microsoft.com/.default" is required in the client credentials flow, see the consent documentation (https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc#the-default-scope)
            string objectId = "Enter_the_Object_Id_Here"; // The object ID is the identifier of the app or service principal you want to work with. Depending on the endpoint you use, it can be either the application objectId (https://graph.microsoft.com/v1.0/applications)) or the service principal objectId (https://graph.microsoft.com/v1.0/ServicePrincipals)).
            string api = "Graph_API/ENDPOINT"; // Choose the graph endpoint you need to use, depending on whether you are working with (https://graph.microsoft.com/v1.0/applications) or (https://graph.microsoft.com/v1.0/servicePrincipals)
            string aud_POP = "00000002-0000-0000-c000-000000000000"; // audience for client assertion must always be 00000002-0000-0000-c000-000000000000
            string aud_ClientAssertion = "https://login.microsoftonline.com/{YOUR_TENANT_ID_HERE}/v2.0"; // audience for PoP must always be in the format https://login.microsoftonline.com/{YOUR_TENANT_ID_HERE}/v2.0

            // pfxFilePath -> Use an existing valid cert used/uploaded to the app or service principal to generate access token and PoP token.
            string pfxFilePath = "Current_Active_Certificate_Path"; // Replace the file path with the location of your certificate.
            string password = "Current_Active_Certificate_Password"; // If applicable, replace the password value with your certificate password.
            X509Certificate2 signingCert = null;
            try
            {
                if (!password.IsNullOrEmpty())
                    signingCert = new X509Certificate2(pfxFilePath, password);
                else
                    signingCert = new X509Certificate2(pfxFilePath);
            }
            catch (System.Security.Cryptography.CryptographicException ex)
            {
                Console.WriteLine("Check the old/uploaded certificate {CertificateDiskPath}, you need to add a correct certificate path and/or password for this sample to work\n" + ex.Message);
                Environment.Exit(-1);
            }

            // newCerFilePath -> This is the new cert which will be uploaded. The cert can also be stored in Azure Key Vault.
            string newCerFilePath = "New_Certificate_Path"; // Replace the file path with the location of your new certificate to be uploaded using the Graph API.
            string newCertPassword = "New_Certificate_Password"; // If applicable, replace the password value with your new certificate password.
            X509Certificate2 newCert = null;
            try
            {
                if (newCertPassword != "")
                    newCert = new X509Certificate2(newCerFilePath, newCertPassword);
                else
                    newCert = new X509Certificate2(newCerFilePath);
            }
            catch (System.Security.Cryptography.CryptographicException ex)
            {
                Console.WriteLine("Check the new certificate {NewCertificateDiskPath}, you need to add a correct certificate path and/or password for this sample to work\n" + ex.Message);
                Environment.Exit(-1);
            }

             //========================
             //Get acessToken via client assertion
             //========================
             var client_assertion = Helper.GenerateClientAssertion(aud_ClientAssertion, clientId, signingCert);
             var token = Helper.GenerateAccessTokenWithClientAssertion(client_assertion, clientId, tenantID);

            //========================
            //Get PoP Token
            //========================
            var poP = Helper.GeneratePoPToken(objectId, aud_POP, signingCert);

            // Get the new certificate info which will be uploaded via Microsoft Graph API call
            var key = Helper.GetCertificateKey(newCert);
            var graphClient = Helper.GetGraphClient(scopes, tenantID, clientId, signingCert);

            int choice = -1;
            while (choice != 0)
            {
                Console.WriteLine("\n=================================================");
                Console.WriteLine("Please choose one of the following options:");
                Console.WriteLine("=================================================");
                Console.WriteLine("0. Exit");
                Console.WriteLine("1. Display access token");
                Console.WriteLine("2. Display client assertion");
                Console.WriteLine("3. Display PoP token");
                Console.WriteLine("4. Display certificate Info");
                Console.WriteLine("5. Upload certificate using Graph SDK");
                Console.WriteLine("6. Upload certificate using Graph API");
                Console.WriteLine("7. Delete certificate using Graph SDK");
                Console.WriteLine("8. Delete certificate using Graph API");
                Console.WriteLine("\nEnter the choose number here:");
                choice = Int32.TryParse(Console.ReadLine(), out choice) ? choice : -1;

                HttpStatusCode code;
                KeyCredential response;
                string certID;
                Guid val;

                // Process user choice
                switch (choice)
                {
                    case 0:
                        // Exit the program
                        Console.WriteLine("\nGoodbye...\n");
                        break;
                    case 1:
                        // Display access token
                        Console.WriteLine("\n\"Access Token Value is:\"\n__________________");
                        Console.WriteLine($"Access Token: {token}");
                        Console.WriteLine("__________________\n");
                        break;
                    case 2:
                        // Display client assertion
                        Console.WriteLine("\n\"Client Assertion Token Value is\"\n__________________");
                        Console.WriteLine($"client_assertion: {client_assertion}");
                        Console.WriteLine("__________________\n");
                        break;
                    case 3:
                        // Display client assertion
                        Console.WriteLine("\n\"Proof of Possession Token Value is\"\n__________________");
                        Console.WriteLine($"PoP token: {poP}");
                        Console.WriteLine("__________________\n");
                        break;
                    case 4:
                        // Display certificate key
                        Helper.DisplayCertificateInfo(newCert);
                        break;
                    case 5:
                        // Call the addKey SDK using Graph SDK
                        if (newCertPassword != "")
                        {
                            response = GraphSDK.AddKeyWithPassword_GraphSDKAsync(poP, objectId, key, newCertPassword, graphClient).GetAwaiter().GetResult();
                        }
                        else
                        {
                            response = GraphSDK.AddKey_GraphSDKAsync(poP, objectId, key, graphClient).GetAwaiter().GetResult();
                        }
                        if (response != null)
                        {
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("Uploaded Successfully!");
                            Console.WriteLine("______________________\n");
                        }
                        else
                        {
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("Something went wrong!");
                            Console.WriteLine("______________________\n");
                        }

                        break;
                    case 6:
                        // Call the addKey API directly without using SDK
                        if (!password.IsNullOrEmpty())
                        {
                            code = GraphAPI.AddKeyWithPassword(poP, objectId, api, token, key, newCertPassword);
                        }
                        else
                        {
                            code = GraphAPI.AddKey(poP, objectId, api, token, key);
                        }
                        if (code == HttpStatusCode.OK)
                        {
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("Uploaded Successfully!");
                            Console.WriteLine("______________________\n");
                        }
                        else
                        {
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("Something went wrong!");
                            Console.WriteLine("HTTP Status code is " + code);
                            Console.WriteLine("______________________\n");
                        }
                        break;
                    case 7:
                        // Call the removeKey API using Graph SDK
                        Console.WriteLine("\nEnter certificate ID that you want to delete:");
                        certID = Console.ReadLine();

                        if (Guid.TryParse(certID, out val))
                        {
                            var res = GraphSDK.RemoveKey_GraphSDKAsync(poP, objectId, certID, graphClient).GetAwaiter().GetResult();

                            if (res)
                            {
                                Console.WriteLine("\n______________________");
                                Console.WriteLine("Cert Deleted Successfully!");
                                Console.WriteLine("_____________________\n");
                            }
                            else
                            {
                                Console.WriteLine("\n______________________");
                                Console.WriteLine("Something Went Wrong!");
                                Console.WriteLine("ERROR: Unable to delete certificate");
                                Console.WriteLine("______________________\n");
                            }
                        }
                        else
                        {
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("ERROR: Invalid Certificate ID");
                            Console.WriteLine("______________________\n");
                        }
                        break;
                    case 8:
                        // Call the removeKey API directly without using API
                        Console.WriteLine("\nEnter certificate ID that you want to delete:");
                        certID = Console.ReadLine();
                        try
                        {
                            if (Guid.TryParse(certID, out val))
                            {
                                code = GraphAPI.RemoveKey(poP, objectId, api, certID, token);

                                if (code == HttpStatusCode.NoContent)
                                {
                                    Console.WriteLine("\n______________________");
                                    Console.WriteLine("Cert Deleted Successfully!");
                                    Console.WriteLine("______________________\n");
                                }
                                else
                                {
                                    Console.WriteLine("\n______________________");
                                    Console.WriteLine("Something went wrong!");
                                    Console.WriteLine("HTTP Status code is " + code);
                                    Console.WriteLine("______________________\n");
                                }
                            }
                            else
                            {
                                Console.WriteLine("\n------------------------------");
                                Console.WriteLine("ERROR: Invalid Certificate ID");
                                Console.WriteLine("______________________________\n");
                            }
                        }

                        catch (HttpRequestException ex)
                        {
                            Console.WriteLine(ex.InnerException.Message);
                            Console.WriteLine("\n______________________");
                            Console.WriteLine("ERROR: " + ex.Message);
                            Console.WriteLine("______________________\n");
                        }
                        break;
                    default:
                        Console.WriteLine("\n______________________");
                        Console.WriteLine("Invalid choice");
                        Console.WriteLine("______________________\n");
                        break;
                }

            }
        }
    }
}

Azure KeyVault で署名を使用して証明を生成することもできます。 JWT ヘッダーとペイロードに埋め込み文字 '=' を含めないようにするか、 Authentication_MissingOrMalformed エラーが返されることに注意してください。

これで PoP トークンが作成されたので、それを使用して次のことができます。

クライアント アサーションの詳細については、「 Microsoft ID プラットフォーム アプリケーション認証証明書の資格情報」を参照してください。