Events
Nov 19, 11 PM - Nov 21, 11 PM
Join online sessions at Microsoft Ignite created to expand your skills and help you tackle today's complex issues.
Register nowThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
You can use the addKey and removeKey methods defined on the application and servicePrincipal resources to roll expiring keys programmatically.
As part of the request validation for these methods, a proof-of-possession (PoP) of an existing key is verified before the methods can be invoked. The proof is represented by a self-signed JSON Web Token (JWT). This token must be signed using the private key of one of the application's existing valid certificates. The recommended lifespan for the token is 10 minutes.
This article provides code examples in C# to demonstrate how to:
Important
Applications that don't have any existing valid certificates because certificates haven't been added yet or existing certificates have expired can't use this service action. Instead, use the Update application operation to update the keyCredentials property. For more information, see Add a certificate to an app using Microsoft Graph.
.pfx
format. You can alternatively update the script to only require the public certificate without the private key.The token should contain the following claims:
00000002-0000-0000-c000-000000000000
.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;
}
}
}
}
}
You can also generate the proof using signature in Azure KeyVault. It's important to note that padding character '=' must not be included in the JWT header and payload or an Authentication_MissingOrMalformed error is returned.
Now that you have your PoP token, you can use it to:
To learn more about client assertions, see Microsoft identity platform application authentication certificate credentials.
Events
Nov 19, 11 PM - Nov 21, 11 PM
Join online sessions at Microsoft Ignite created to expand your skills and help you tackle today's complex issues.
Register now