Compartir a través de


Generación de tokens de prueba de posesión para claves graduales y actualización de certificados mediante programación

Puede usar los métodos addKey y removeKey definidos en los recursos aplicación y servicePrincipal para poner en servicio las claves de expiración mediante programación.

Como parte de la validación de solicitudes para estos métodos, se comprueba una prueba de posesión (PoP) de una clave existente antes de que se puedan invocar los métodos. La prueba se representa mediante un token web JSON (JWT) autofirmado. Este token debe firmarse mediante la clave privada de uno de los certificados válidos existentes de la aplicación. La duración recomendada para el token es de 10 minutos.

En este artículo se proporcionan ejemplos de código en C# para demostrar cómo:

  1. Calcule la aserción de cliente mediante un certificado válido existente.
  2. Genere el token de PoP mediante la clave de aserción de cliente generada.
  3. Use el token de PoP para cargar un nuevo certificado en la aplicación o el objeto de entidad de servicio mediante el método addKey .
  4. Use el token de PoP para quitar un certificado del objeto de aplicación o entidad de servicio mediante el método removeKey .

Importante

Las aplicaciones que no tienen ningún certificado válido existente porque aún no se han agregado certificados o los certificados existentes han expirado no pueden usar esta acción de servicio. En su lugar, use la operación Actualizar aplicación para actualizar la propiedad keyCredentials . Para obtener más información, vea Agregar un certificado a una aplicación mediante Microsoft Graph.

Requisitos previos

  • Tenga un certificado de cliente válido en la aplicación de destino o la entidad de servicio.
    • Necesita los detalles de un certificado existente válido para generar la clave de aserción de cliente y el token de PoP.
      • Con fines de prueba, puede usar un certificado autofirmado. Para obtener información sobre cómo crear un certificado autofirmado, consulte Creación de un certificado público autofirmado para autenticar la aplicación.
      • Exporte el certificado con su clave privada en .pfx formato. Como alternativa, puede actualizar el script para que solo requiera el certificado público sin la clave privada.
  • El identificador de cliente (denominado appId en la API) y el identificador de objeto (denominado id . en la API) de la aplicación o entidad de servicio para la que se genera el token de PoP.

Código de ejemplo

El token debe contener las siguientes notificaciones:

  • aud: el público debe ser 00000002-0000-0000-c000-000000000000.
  • iss: El emisor debe ser el identificador de la aplicación o el objeto servicePrincipal que inicia la solicitud.
  • nbf: no antes de la hora.
  • exp: el tiempo de expiración debe ser el valor de nbf + 10 minutos.
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;
                }

            }
        }
    }
}

También puede generar la prueba mediante la firma en Azure KeyVault. Es importante tener en cuenta que el carácter de relleno '=' no debe incluirse en el encabezado JWT y la carga o se devuelve un error de Authentication_MissingOrMalformed .

Ahora que tiene el token de PoP, puede usarlo para:

Para más información sobre las aserciones de cliente, consulte Credenciales de certificado de autenticación de aplicaciones de la plataforma de identidad de Microsoft.