Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core 2.2, Azure Web API new X509Certificate2 "The system cannot find the file specified" and "access denied"

My Azure Web API loads a certificate stored as a secret in Key Vault and then attempts to create a new certificate from a byte array. Running locally everything works, but, when deployed to Azure we get Access Denied or The system cannot find the file specified depending on the KeyStorageFlags passed to the constructor. The Web API is using .Net Core 2.2

I've been at this for some time reading lots of information related to this but not this specific scenario.

Using MachineKeySet flag: certificate = new X509Certificate2(pfxBytes, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

Exception

ex=Access denied, stack= at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags) at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)

Using UserKeySet flag: certificate = new X509Certificate2(pfxBytes, "", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable);

Exception

ex=The system cannot find the file specified, stack= at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags) at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)

I followed the load cert as a secret using this article [https://www.rahulpnath.com/blog/pfx-certificate-in-azure-key-vault/][1] This doesn't appear to have anything to do with key vault because it loads the secret from Key Vault just fine locally, and, loading in Azure is not where the exception is thrown. But it might be related to how the certificate is stored?

Here's the full code we're using

private async Task<X509Certificate2> GetSecretCertificateFromKVAPI()
        {
            try
            {
                var keyVaultName = _configuration["Secrets:KeyVaultName"];

                var keyVaultEndpoint = $"https://{keyVaultName}.vault.azure.net:443/secrets/";
                var provider = new AzureServiceTokenProvider();
                var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback));
                SecretBundle secretRetrieved;

                secretRetrieved = await client.GetSecretAsync($"{keyVaultEndpoint}OdysseyCA");

                var pfxBytes = Convert.FromBase64String(secretRetrieved?.Value);

                //note, must pass an empty password when loading from keyvault in Azure
                X509Certificate2 certificate;
                try
                {
                    // or recreate the certificate directly
                    certificate = new X509Certificate2(pfxBytes, "",
                            X509KeyStorageFlags.MachineKeySet |
                            X509KeyStorageFlags.PersistKeySet |
                            X509KeyStorageFlags.Exportable);

                    // alternative, try using import
                    //var coll = new X509Certificate2Collection();
                    //coll.Import(pfxBytes, null, X509KeyStorageFlags.Exportable);
                    //certificate = coll[0];
                }
                catch (Exception ex)
                {
                    _logger.Trace($"create new X509 failed, ex={ex.Message}, stack={ex.StackTrace}");
                    throw;
                }

                return certificate;
            }
            catch (Exception ex)
            {
                _logger.Trace($"GetSecretCertificateFromKVAPI threw an exception, ex={ex.Message}, stack = {ex.StackTrace}");
                throw;
            }
        }

In the code you can see we tried using the .import method of the X509Certificate2Collection class to work around this but the results are the same. We're actively backing up to .Net Core 2.1 and 2.0 to see if this is a new bug and searching for other answers.

How do we get this to work?

Edit: based on a suggestion in the comments below tried the EphemeralKeySet flag but the exception stack is pretty much the same as others:

certificate = new X509Certificate2(pfxBytes, "", X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable);

ex=The system cannot find the file specified, stack = at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags) at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)

Can anyone confirm this works with .Net any version? possible core or 2.2 bug?

like image 314
user2503078 Avatar asked Mar 07 '19 19:03

user2503078


1 Answers

In the API hosted in Azure, adding the application setting : WEBSITE_LOAD_USER_PROFILE=1 solved the problem. We stayed with the EphemeralKeySet but I suspect UserKeySet flags would work as well.

like image 81
user2503078 Avatar answered Nov 14 '22 01:11

user2503078