We're transitioning to client certificate authentication in our application and are currently working on retrieving the count of deleted application objects from Microsoft Graph API following https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers#client-credentials-provider. Here's the code we've developed so far:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var clientId = "YOUR_CLIENT_ID";
var tenantId = "YOUR_TENANT_ID";
var clientCertificate = new X509Certificate2("MyCertificate.pfx");
var options = new ClientCertificateCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};
var clientCertCredential = new ClientCertificateCredential(
tenantId, clientId, clientCertificate, options);
var graphClient = new GraphServiceClient(clientCertCredential, scopes);
try
{
var result = await graphClient.Directory.DeletedItems.GraphApplication.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Count = true;
requestConfiguration.QueryParameters.Orderby = new string[] { "deletedDateTime asc" };
requestConfiguration.QueryParameters.Select = new string[] { "id", "displayName", "deletedDateTime" };
requestConfiguration.Headers.Add("Consistencylevel", "Eventual");
});
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
However, we encounter the following exception when attempting to list deleted applications:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The specified network password is not correct.'
We've ensured that the calling application has been granted the required permissions, including Directory.Read.All and Application.Read.All.
Could you please assist in identifying the issue and providing guidance on how to resolve it? Any help would be greatly appreciated.
I agree with @user2250152, you need to pass private key along with certificate path while using client-certificate authentication.
In my case, I created certificates with private key using below PowerShell commands:
$certname = "graphcert03"
$cert = New-SelfSignedCertificate -Subject "CN=$certname" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256
Export-Certificate -Cert $cert -FilePath "C:/demo/$certname.cer" ## Specify your preferred location
$mypwd = ConvertTo-SecureString -String "password" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:/demo/$certname.pfx" -Password $mypwd
Response:
Now, I uploaded this certificate in calling app registration that has required permissions like Application.Read.All:
When I ran below modified code by including private key with certificate, I got the response with expected results like this:
using Azure.Identity;
using Microsoft.Graph;
using System.Security.Cryptography.X509Certificates;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var clientId = "appID";
var tenantId = "tenantId";
// Load certificate from file
var certificatePath = "C:/demo/graphcert03.pfx";
var certificatePassword = "password"; // Provide the password here
var clientCertificate = new X509Certificate2(certificatePath, certificatePassword);
// using Azure.Identity;
var options = new ClientCertificateCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};
var clientCertCredential = new ClientCertificateCredential(
tenantId, clientId, clientCertificate, options);
var graphClient = new GraphServiceClient(clientCertCredential, scopes);
var apps= await graphClient.Directory.DeletedItems.GraphApplication.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Count = true;
requestConfiguration.QueryParameters.Orderby = new string[] { "deletedDateTime asc" };
requestConfiguration.QueryParameters.Select = new string[] { "appId", "DisplayName", "deletedDateTime" };
requestConfiguration.Headers.Add("Consistencylevel", "Eventual");
});
Console.WriteLine($"Total deleted apps: {apps.OdataCount}\n");
foreach (var app in apps.Value)
{
Console.WriteLine($"App ID: {app.AppId}");
Console.WriteLine($"Application Name: {app.DisplayName}");
Console.WriteLine($"Deleted Date and Time: {app.DeletedDateTime}");
Console.WriteLine();
}
Response:
To confirm that, I checked in Portal where the total count of deleted app registrations is same as below: