Freigeben über


Generieren von PoP-Token (Proof-of-Possession) zum Automatisieren von Schlüsselrollen und Zertifikatupdates mithilfe von Microsoft Graph

Mit den methoden addKey und removeKey , die für die Ressourcen application und servicePrincipal definiert sind, können Sie ablaufende Schlüssel programmgesteuert ausführen.

Im Rahmen der Anforderungsüberprüfung für diese Methoden wird ein Besitznachweis (PoP) eines vorhandenen Schlüssels überprüft, bevor die Methoden aufgerufen werden können. Der Nachweis verwendet ein selbstsigniertes JSON-Webtoken (JWT). Dieses Token muss mit dem privaten Schlüssel eines der vorhandenen gültigen Zertifikate der Anwendung signiert werden. Die empfohlene Lebensdauer des Tokens beträgt 10 Minuten.

Dieser Artikel enthält C#-Codebeispiele, um folgendes zu veranschaulichen:

  1. Berechnen Sie die Clientassertion mithilfe eines vorhandenen gültigen Zertifikats.
  2. Generieren Sie das PoP-Token mithilfe des generierten Clientassertionsschlüssels.
  3. Verwenden Sie das PoP-Token, um mithilfe der addKey-Methode ein neues Zertifikat in das App- oder Dienstprinzipalobjekt hochzuladen.
  4. Verwenden Sie das PoP-Token, um mithilfe der removeKey-Methode ein Zertifikat aus dem App- oder Dienstprinzipalobjekt zu entfernen.

Wichtig

Anwendungen ohne vorhandene gültige Zertifikate können diese Dienstaktion nicht verwenden, da keines hinzugefügt oder vorhandene zertifikate abgelaufen sind. Verwenden Sie stattdessen den Vorgang Anwendung aktualisieren , um keyCredentials zu aktualisieren. Weitere Informationen finden Sie unter Hinzufügen eines Zertifikats zu einer App mithilfe von Microsoft Graph.

Voraussetzungen

  • Sie verfügen über ein gültiges Clientzertifikat für die Ziel-App oder den Dienstprinzipal.
    • Sie benötigen die Details eines gültigen vorhandenen Zertifikats, um den Clientassertionsschlüssel und das PoP-Token zu generieren.
  • Die Client-ID (in der API appId genannt) und die Objekt-ID (in der API als ID bezeichnet) der Anwendung oder des Dienstprinzipals, für die Sie das PoP-Token generieren.

Beispielcode

Das Token enthält die folgenden Ansprüche:

  • aud: Zielgruppe muss sein 00000002-0000-0000-c000-000000000000.
  • iss: Aussteller sollte die ID des Anwendungs - oder servicePrincipal-Objekts sein, das die Anforderung initiiert.
  • nbf: Nicht vor der Zeit.
  • exp: Ablaufzeit sollte der Wert nbf + 10 Minuten sein.
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 used to store app registration info. You can use appsettings.json to store this data.
            //=============================
            string clientId = "Enter_the_Application_Id_Here"; // Client ID or app ID 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 (!string.IsNullOrEmpty(password))
                    signingCert = new X509Certificate2(pfxFilePath, password);
                else
                    signingCert = new X509Certificate2(pfxFilePath);
            }
            catch (System.Security.Cryptography.CryptographicException ex)
            {
                Console.WriteLine("Check the old or uploaded certificate {CertificateDiskPath}. Add the correct certificate path 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("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("An error occurred.");
                            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;
                }

            }
        }
    }
}

Sie können den Nachweis auch mithilfe der Signatur in Azure KeyVault generieren. Es ist wichtig zu beachten, dass das Auffüllzeichen "=" nicht im JWT-Header und der Nutzlast enthalten sein darf, oder dass ein Authentication_MissingOrMalformed Fehler zurückgegeben wird.

Nachdem Sie nun über Ihr PoP-Token verfügen, verwenden Sie es für Folgendes:

Erfahren Sie mehr über Clientassertionen in Microsoft Identity Platform Anmeldeinformationen für das Anwendungsauthentifizierungszertifikat.