45

I have a WinForms application that consumes a WCF, and pass as a parameter to a function a certificate:

mySvcClient.SendDocument(cert.Export(X509ContentType.SerializedCert, "password"));
...

In WCF service, I recreated the certificate from the array of bytes:

public void SendDocument (byte[] binaryCert)
{   
     X509Certificate2 cert = new X509Certificate2(binaryCert, "password");
...

But when using the certificate to sign a xml, I got the error "Keyset does not exist":

if (cert.HasPrivateKey) // WORKS!!!
{   
    signedXml.SigningKey = cert.PrivateKey; // THROW "keyset does not exist" EXCEPTION
...

In my computer, the application works 100%! But in the WebServer, I got this error!

The question is: even X509Certificate2 recreated from an array of bytes, I need some special permission to access private key?

Thank you!

BrunoXP
  • 449
  • 1
  • 4
  • 4

10 Answers10

47

If you are using windows server 2008 or windows 7, then you need the permission to read private key.

  1. use FindPrivateKey tool to find path. For example:

FindPrivateKey My LocalMachine -n "CN=MyCert" –a

it returns the path: C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys[File Name]

  1. Go to that path and open file properties

  2. Go to security tab

  3. Click on "Edit" then "Add"

  4. In opened dialog write: IIS AppPool\[your application pool name] and click OK

Now your application pool has permission to read this private key.

Vano Maisuradze
  • 5,829
  • 6
  • 45
  • 73
  • 7
    Another way of setting the permissions of the certificate is through the Certificates SnapIn, described here: http://stackoverflow.com/a/3176253/844207 – victorvartan Oct 12 '15 at 17:22
41

I have faced this issue, my certificates where having private key but i was getting this error("Keyset does not exist")

Cause: Your web site is running under "Network services" account or having less privileges.

Solution: Change Application pool identity to "Local System", reset IIS and check again. If it starts working it is permission/Less privilege issue, you can impersonate then using other accounts too.

Ralph Willgoss
  • 11,750
  • 4
  • 64
  • 67
  • 1
    There also appears to be a scenario where even if your app pool identity is an account with the correct permissions, you still need to grant the IIS_IUSRS group with read access for the operation to complete if you attempt to access the private key in the Application_Start for the application. – RMD Feb 17 '15 at 20:35
  • in my case I had to change the app pool identity to `Local System` and provide private key access to `IIS_IUSRS`. cert needs to be installed on local computer store. – Amit Feb 10 '16 at 01:46
  • This is a lot easier approach to developer environment and deploying. – Dhanuka777 May 12 '16 at 01:31
  • 1
    Do not use Local System permissions in production environment, it's never recommended as you are giving more than administrator permissions to the app pool. It's dangerous. – Dhanuka777 Jan 12 '17 at 21:12
22

I was facing the same issue, and I don't know how(shame on me), but it worked:

var certificate = new X509Certificate2(filePath, password,
    X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

certificate.PrivateKey; // before: error "KeySet does not exist"!

using (certificate.GetRSAPrivateKey()) { } // pure black magic

certificate.PrivateKey; // after: just works! lol

I hope someone can answer this mystery.

Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
Kim Tranjan
  • 4,521
  • 3
  • 39
  • 38
  • 7
    @Everyone please note that this solution applies to .Net 4.6 or later according to the MSDN page on it. – Richard Barker Jun 08 '16 at 14:41
  • 1
    This solved my issue. Adding more to this confusion, `PrivateKey` worked on my local machine but not on deployed code. – riv_rec Sep 25 '18 at 18:49
  • 1
    Doesn't work. Still throws "Keyset does not exist" randomly – sports Mar 11 '20 at 18:44
  • This also solved my issue and I had the same problem as @riv_rec where it was working locally but not on the deployed version. – Jacob Finamore Apr 08 '22 at 13:52
  • 2
    This solution also works for a .NET 6-based WPF application. In my case, I didn't have to do the black magic part. Just adding X509KeyStorageFlags.PersistKeySet to the key storage flags worked for me. X509Certificate2? certificate = new X509Certificate2(certificateFilePath, password, X509KeyStorageFlags.PersistKeySet);. I appreciate the help on this one Nisarg Shah and @Kim Tranjan. – Tangere Apps Nov 26 '22 at 12:07
5

Vano Maisuradze answer works. If you are looking for the FindPrivateKey tool it is included in Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4, which can be found here: http://www.microsoft.com/en-us/download/confirmation.aspx?id=21459

Once downloaded and extracted, open the project: WF_WCF_Samples\WCF\Setup\FindPrivateKey\CS in Visual Studio and compile it. Then open command prompt and navigate to: WF_WCF_Samples\WCF\Setup\FindPrivateKey\CS\bin

Then continue with Vano Maisuradze answer

ajliptak
  • 429
  • 3
  • 7
3

For local development, make sure the user has permissions to access the certificate, especially if you're installing it in the Local Machine store.

  1. Open certificate manager (mmc.exe or certlm)
  2. Go to certificate
  3. Right Click > All Tasks > Manage Private Keys
  4. Assign permission for current user
  5. Done

This was the case for me when debugging it using Rider.

Muhammad Ibnuh
  • 360
  • 5
  • 14
2

I think the problem is that you need to add the key to the machine's certificate store.

Xint0
  • 5,221
  • 2
  • 27
  • 29
0

Application Pool Identity accounts don't have access to the certificate store by default.

Either you change to Network Services account as pointed by Vaibhav.Inspired or you give access to the certificate.

To allow access do the following command:

WinHttpCertCfg.exe -g -c LOCAL_MACHINE\MY -s "IssuedToName" -a "AccountName"

Notes:

- The tool may need to be installed first. The setup will place the tool at `C:\Program Files (x86)\Windows Resource Kits\Tools\WinHttpCertCfg.exe`.
- `IssuedName` is the issuer property of the certificate that the application will attempt to access
- The command must be run from command prompt with elevated privileges

Reference :https://support.microsoft.com/en-us/help/901183/how-to-call-a-web-service-by-using-a-client-certificate-for-authentica Step 2

Also you need to enable the Mark this key as exportable option when installing the certificate.

MiguelSlv
  • 14,067
  • 15
  • 102
  • 169
0

couple of troubleshooting steps:

  1. Run your program as Administrator
  2. If it is web app deployed in IIS -> then add the IIS_IUSRS to the Certificate permissions. Select certificate in Personal, Right Click-> Manage Private Keys -> Add the user.
  3. Run Visual Studio in Admin mode if in Debug to get this problem sorted out
Jabez
  • 795
  • 9
  • 18
0

If you are able to debug the application, try running the IDE on admin mode..you can also add new users from MMC.

Ishan
  • 285
  • 2
  • 8
0

I had the same issue on c# console application and after reading answeres here I thought that problem was in permissions. Then I run visual studio as administrator and it worked.

Nodo
  • 67
  • 7