WindowsCryptographicException: The system cannot find the file specified

sravya shivapuram 211 Reputation points
2022-03-24T14:04:24.663+00:00

Hi,

I am running into the below error -

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException:
The system cannot find the file specified when trying to retrieve the certificate from KeyVault using the DNS path and Certificate Name.

Here's a little background:

I created an azure function in VS 2019 and using .Net Core 3.1 API's Client ID, I am trying to generate the access token. Please see the code snippet below -

This code works absolutely fine locally but when deployed to Azure cloud, it throws the above error. My azure function has been given access in Key vault through access policy. It's permissions match with what the .Net Core 3. API has.

var accessToken = await SPClient.GetApplicationAuthenticatedClient(_settings.ClientId, scopes, _settings.TenantId).ConfigureAwait(false);   

internal static async Task<string> GetApplicationAuthenticatedClient(string clientId, string[] scopes, string tenantId)
        {
            int numOfAttempts = 1;
            string accessToken = null;

            if (string.IsNullOrEmpty(clientId) || scopes.Length == 0 || string.IsNullOrEmpty(tenantId))
            {
                throw new Exception("GetApplicationAuthenticatedClient - missing parameters");
            }

            while (numOfAttempts < 4)
            {
                try
                {
                    X509Certificate2 certificate = await GetAppOnlyCertificate().ConfigureAwait(false);
                    IConfidentialClientApplication clientApp = ConfidentialClientApplicationBuilder
                                                    .Create(clientId)
                                                    .WithCertificate(certificate)
                                                    .WithTenantId(tenantId)
                                                    .Build();
                    AuthenticationResult authResult = await clientApp.AcquireTokenForClient(scopes).ExecuteAsync();
                    accessToken = authResult.AccessToken;
                    break;
                }
                catch (Exception ex)
                {
                    if (numOfAttempts == 3)
                    {
                        //MyTelemetry.TrackException(ex);
                        throw ex;
                    }

                    numOfAttempts++;
                }
            }

            return accessToken;
        }

 private static async Task<X509Certificate2> GetAppOnlyCertificate()
        {
            try
            {
                var azureServiceTokenProvider = new AzureServiceTokenProvider();
                var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
                var certSecret = await kv.GetSecretAsync(KeyVaultDnsLocation, KeyVaultCertName);
                var privateKeyBytes = Convert.FromBase64String(certSecret.Value);
                var sharepointCert = new X509Certificate2(privateKeyBytes, (string)null);
                return sharepointCert;
            }
            catch (Exception ex)
            {
                //MyTelemetry.TrackException(ex);
                throw ex;
            }
        }

I am not sure what's missing. Any help is greatly appreciated. Thank you in advance.

Regards
SLS

Azure Key Vault
Azure Key Vault
An Azure service that is used to manage and protect cryptographic keys and other secrets used by cloud apps and services.
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
0 comments No comments
{count} vote

Answer accepted by question author
  1. MughundhanRaveendran-MSFT 12,511 Reputation points
    2022-03-26T09:42:59.33+00:00

    @sravya shivapuram ,

    Thanks for posting your question in Q&A.

    By default, X509Certificate2's private key is stored in User Profile file. An Function app/App Service will need to have AppSettings WEBSITE_LOAD_USER_PROFILE = 1. The equivalent settings which will enable User Profile indirectly is WEBSITE_LOAD_CERTIFICATES = * or . The app can access the certificate from X509Store and need not worry about the private key file management.

    However, if the app were to explicitly instantiate the X509Certificate2 from a blob (downloaded from Azure Key Vault for instance) or a PFX file deployed with an app, one needs to pay attention to private key file nature. The recommendation is to avoid storing private key file in the first place by using X509KeyStorageFlags.EphemeralKeySet (see https://github.com/suwatch/InMemoryX509Certificate in case this flag is not available in the previous netframework).

    If X509KeyStorageFlags.UserKeySet (or default) flag is used, one private key container file C:\Users\<Site>\AppData\Roaming\Microsoft\Crypto\RSA\<SID>\<KeyContainer> will be created/associated with X509Certificate2 instance. The file is deleted when X509Certificate2 disposal or GC-ed. Hence, the recommendation is to always timely dispose when no longer use the certificate. Often times, application keeps creating this object without proper dispose leading C:\Users\<Site> profile out of disk space.

    Reference : https://github.com/projectkudu/kudu/wiki/Best-X509Certificate2-Practices#private-key-are-stored-in-user-profile-folder

    Similar discussion here : https://github.com/dotnet/runtime/issues/30658

    I hope this helps!

    Please 'Accept as answer' and ‘Upvote’ if it helped so that it can help others in the community looking for help on similar topics.

    4 people found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.