0
votes

I have an Azure app service I did not create but now maintain. The app service finds a certificate in a Key Vault by thumbprint and in turn uses that to get a token for doing some SQL work via nightly jobs.

It appears the certificate was set to auto renew after 80% of its valid date (12 months). The day the cert renewed my nightly jobs started to fail. I'm reasonably certain the new certificate is at the root of the problem.

As best I can tell it designed to work like this:

  • Job fires via Azure Logic App
  • annomyous POST to a reports processing API (end result should be .PDF report creation for email atachment)
  • API has Appsetting.json that contains the current certificates thumbprint
  • Thumbprint is used in the line of code below to find the certificate in the cert store
  • Cert is used to aquire access token and perform work

When I install both the old certificate and the new certificate on my local machine and run the entire process it works find with the old certificate and fails on this line with the new auto-generated certificate. It also fails with any new certificates I try to make in Azure and export from Azure and import to my dev machine. I've double/triple checked the appsettings to make sure the Thumbprint in question is correct and updated.

var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);

When this was configured a year ago the process was to go into App Service, TLS/SSL setting blade, select Private Key Certificates (.pfx) and finally + Import Key Vault Certificate.

From there you selected the Key Vault and Certificate, then changed the Appsettings.Json to have the new Thumbprint.

Why will it work with the old (soon to expire) certificate and corresponding Thumbprint entry into appsettings but fails to work with any newly created certificates and corresponding correct Thumbprint entry?

I've looked at the Configuration for the App Service in question and it has the following setting when I understand is supposed to let the app service see all certificates registered to it, right?

WEBSITE_LOAD_CERTIFICATES = *

EDIT: After some more testing I find that any cert I export form Azure and import on my computer will successfully iterate the cert store and find any Certificate I provide a valid Thumbprint. What it won't do is use that cert to obtain a access token. The complete code is below.

The certificate that is due to expire soon will get a proper access token and run the rest of the process by getting the proper data from the DB.

The exception I get with all the other certificates suggest something about base64 encoding but I can't quite figure that out. Any ideas?

Exception for all but the original certificate:

Client assertion contains an invalid signature

Successful Access token with this code only with original certificate:

private async Task<string> GetDatabaseTokenFromCert()
    {
        X509Certificate2 cert;
        var store = new X509Store(StoreLocation.CurrentUser);
        var authContext = new AuthenticationContext(_appSettings.AzureAD.AADInstance + _appSettings.AzureAD.TenantId);

        try
        {
            store.Open(OpenFlags.ReadOnly);
            var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
            
            if (signingCert == null)
            {
                throw new FileNotFoundException("Cannot locate certificate for DB access!", _appSettings.AzureAD.CertificateThumbprint);
            }

            cert = signingCert;

        }
        finally
        {
            store.Close();
        }

        var certCred = new ClientAssertionCertificate(_appSettings.AzureAD.ClientId, cert);

        var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));
        
        return result?.AccessToken;

    }
1
Did you try to use this example docs.microsoft.com/en-us/azure/app-service/…?Andriy Bilous
Yes, it utilizes a X509Certificate I need a X509Certificate2 this line takes the X509Certificate2 as the variable cert to get the SQL access token var certCred = new ClientAssertionCertificate(_appSettings.AzureAD.ClientId, cert);RichP

1 Answers

0
votes

Turns out you need to also add the new certificate to the app registration. As a .cer file without the private key, obviously.

So if you get the error message:

AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: 'YourNewCert'

Go to Key Vault, export new cert as .cer file and import it into the App Service that is trying to obtain the Access token from AcquireTokenAsync

In my case the order of operation is:

  • Logic app fires off anonymous call as a POST to web API
  • Web API uses Thumbprint of Cert in question via appsetting.json
  • Finds cert with thumbprint that is in App Registration of the web API
  • AcquireTokenAsync takes Azure info and Cert and returns Access Token

This will work or fail on this line of original post

var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));