Intermittent error accessing Azure key vault - Keyset does not exists - c#

I am using Azure Key Vault in my MVC web application. I connect to the Key Vault using certificate. Below is my sample code
AssertionCert = GetCertificate(WebConfigurationManager.AppSettings[KeyVaultConstant.ApplicationID], WebConfigurationManager.AppSettings[KeyVaultConstant.CertificateThumbprint], false);
var keyClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback((authority, resource, scope) => GetToken(authority, resource, scope, AssertionCert)));
var secrets = keyClient.GetSecretsAsync(WebConfigurationManager.AppSettings["VaultUri"]).GetAwaiter().GetResult();
On some instances I get an error stating
Cryptographic Exception: Keyset does not exists
On research I found that I have to give IIS_IUser permission to the folder where primary key is stored. I did that, Yet once in a while I get this error. This does NOT happen everytime.

You are right about the permission issue . Here are the troubleshooting steps for the same:
Troubleshooting Steps:
First check the service account which is running CRM App pool or ADFS App pool
Now open certificate manager, in personal store, locate your certificate. Right click on the certificate and select "Managed Private Keys" option
In the permission window, check if your app pool service account is given appropriate permission (Read permission should be fine, otherwise you can give Full control)
In below screenshot I have given full permission on my wildcard certificate *.test.local to my App Pool account – "test\AppPoolSvc"
Once the permission is given, perform an IISRESET and try accessing.
For further reference , please check. Also please try to restart the machine to see if it works.

Related

"WindowsCryptographicException: The system cannot find the path specified" when trying to use DPAPI from LocalSystem account

I use DPAPI with DataProtectionScope.LocalMachine in a Windows Service.
I tested my service by running it directly on my user account. It works.
Also works when run as administrator.
Then I install it as a Windows Service on LocalSystem account.
Then it throws something like this:
Exception Info: Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The system cannot find the path specified.
at System.Security.Cryptography.ProtectedData.ProtectOrUnprotect(Byte[] inputData, Byte[] optionalEntropy, DataProtectionScope scope, Boolean protect)
at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope)
I changed service user to my user account and it works. So - the data protection fails only if the service is run as LocalSystem.
Fragment that throws:
public byte[] Unprotect(byte[] data, DataProtectionScope scope = DataProtectionScope.CurrentUser)
=> ProtectedData.Unprotect(data, null, scope.AsSystemType());
The scope is set to DataProtectionScope.LocalMachine.
I also tried to run the service as "NT Authority\NetworkService" user. Same behavior. Can't access local machine protection keys.
Why is that and how can I fix that?
So, it seems like a bug in Windows DPAPI to me. It requires a user account different from LOCAL SYSTEM and NETWORK SERVICE.
As LOCAL SYSTEM is in Administrator role, I see no reason why the key access is denied for the user.
As a workaround I just use a different API.
Microsoft.AspNetCore.DataProtection - to be exact.
That API requires the new key to be created for the first time.
For practical implementation see:
https://github.com/HTD/Woof/tree/master/Packages/Woof.DataProtection
https://github.com/HTD/Woof/blob/master/Packages/Woof.DataProtection/Api/WindowsLocalSystemKey.cs
You can also check the https://github.com/HTD/Woof/tree/master/Demos/Woof.ServiceInstaller.TestService
for testing of a data protected service configuration.

IIS will not bind certificate

Using the BouncyCastle API I create certificates and add them to the certificate store, marking the Private Key persisted and exportable.
My key shows up in the local machine's personal store and is a selectable option inside IIS manager, however, when I click OK to bind my certificate, I get an error dialog with the following message:-
A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)
In Windows Event Viewer, the following is logged:-
A fatal error occurred when attempting to access the TLS server credential private key. The error code returned from the cryptographic module is 0x8009030D. The internal error state is 10001.
I have verified that my private key is exportable by going to Action -> All Tasks -> Export... in Certificates MMC and can see that the radio button to export private key is not grayed out.
Suspecting that it could be a file permissions issue, I also from the MMC went to All Tasks -> Manage Private Keys and tried setting Full Control + Read on the application pool the site uses in IIS. Same error, in desperation, I have even tried setting it to Everyone with no success.
I am unsure what else could be causing this issue, or what other steps I should try.
Here is a link to a pastebin of the code I am using to generate the certificate, in case it provides any useful information.

Service Fabric: Authenticating with Azure KeyVault via cert: "KeySet does not exist"

This is the scenario I am trying to enable:
I wish to authenticate to an azure keyvault from my web service application (azure service fabric) via a client certificate.
These are the steps I'm following:
Add a certificate to my keyvault in azure (self signed)
Download certificate via azure powershell (pfx)
Create Azure App Instance to identify my app
Associate certificate with app
Create service principal for the azure app
Give principal access to keyvault
All looks good. When I spin up my service (local service fabric cluster), and try to connect to keyvault to retrieve a secret key+value that I have stored inside, I get error:
CryptographicException: "KeySet does not exist"
When I try to examine PrivateKey property value of the X509Certificate2 object at runtime, it throws the same exception.
The certificate is found, and the private key exists (I verified this via MMC as well as some command line tools).
What can I be missing? Only cause I can think of for this failure is that service fabric user context (Network Service, I think) does not have permission to look at private key? It is stored in "LocalMachine" certificate store, under
Personal" folder (also referred to as "My"). From what I know, applications should be able to read from LocalMachine store without special permissions?
An alternative easier way to grant NETWORK SERVICE user permission on certificate private key (easier than my other answer):
Open certificate snap-in in MMC: WIN + R -> type mmc -> File -> Add/Remove Snap-in -> Add Certificates (Computer Account).
Find your certificate -> Right click and choose All Tasks/Manage Private Keys
Grant Read Permission for NETWORK SERVICE user
Ok, my suspicion was correct. I explicitly granted Network Service user (the user context under which local service fabric cluster runs) access to private key file. Now I can authenticate with keyvault.
cacls.exe "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\{private-key-filename}" /e /g "Network Service":R
I found private key location via a tool program "FindPrivateKey.exe"
findprivatekey.exe My LocalMachine -t "{thumbprint}" -a
Tool can be obtained from https://www.microsoft.com/en-us/download/confirmation.aspx?id=21459
(It is a source code sample located in directory \WCF\Setup\FindPrivateKey\CS\FindPrivateKey.sln, you need to build it yourself)
I ran into the same problem and granting read permission to Network Service worked for me. There is also another way to run run high-privilege service as Local System user described here:
https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-run-script-at-service-startup
You can modify your Service Fabric ApplicationManifest.xml to
define a service principal corresponding to local machine
use that service principal to run the service fabric package created
final ApplicationManifest.xml would look something like:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
<ServiceManifestImport>
...
<Policies>
<RunAsPolicy CodePackageRef="Code" UserRef="LocalSystemUser" /> <!-- 2. run service fabric as defined principal -->
</Policies>
</ServiceManifestImport>
<Principals>
<Users>
<User Name="LocalSystemUser" AccountType="LocalSystem" /> <!-- 1. define principal -->
</Users>
</Principals>
</ApplicationManifest>

Issue while retrieving KeyVault Secret

Trying to retrieve the KeyVault Secret by AD authentication using Certificate.
Documentation Reffered for Creating KeyVault & AD application using Certificate
public static async Task<string> GetAccessToken(string authority, string resource, string scope) {
var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, AssertionCert);
return result.AccessToken; }
Code fails while
var result = await context.AcquireTokenAsync(resource,AssertionCert)
InnerException contains: "Keyset does not exists"
And some times "Invalid provider type specified." error occurs
Not sure where is the issue.
I have resolved this issue after running my application as Administrator.
Assuming the AssertionCert is valid, it's likely a permission issue to access the private key (which is needed for authentication here).
Try this:
Create a Microsoft Management Console (MMC) with the Certificates
snap-in that targets the Local Machine certificate store
Expand the MMC and select Manage Private Keys.
On the Security tab, add the account you're running with, with Read
access.
The KeySet does not exist usually happens when the user you are trying to read the certificate as does not have access to the private keys of the cert. Can you double check your ACLing on the certs?

Azure web app sporadically throws CryptographicException: Keyset does not exist

I have a Identity Server web app hosted on Azure. It has a .pfx file in it's root directory for signing. The problem is that when newly published it works perfectly fine but after some time it starts throwing CryptographicException: Keyset does not exist.
Based on CryptographicException KeySet does not exists I would assume that it is a file access issue, but why out of the sudden azure is messing up with file access.
I've been seing the same exception sporadically. In my case, it was caused by Data Protection keys changing when I swap between deployment slots. When using services.AddDataProtection() with the default configuration and hosting the app on Azure App Service, the data protection keys are stored in %HOME%\ASP.NET\DataProtection-keys. This directory is backed by a network share to make sure the keys are available on all your app instances, BUT this is not the case for deployment slots. So when you switch from one deployment slot to another, the keys will change and you will see CryptographicException: Keyset does not exist when your app tries to unprotect a payload.
To make sure your app is always using the same key, you need to configure a different key storage provider. I.e. use Redis or Azure Blob Storage as backing store.
You can find more information about how to configure this in the official documentation.
I've also described the issue I had on my blog.
I recommend that you do not retrieve your signing cert from disk firstly. Rather upload the certificate to the associated web app in Azure Portal.
and retrieve the certificate on application startup
something like.
X509Certificate2 Certificate = null;
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint,"CERTIFICATE_THUMBPRINT_HERE",false);
Certificate = certCollection[0];

Categories