5

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:

  1. Add a certificate to my keyvault in azure (self signed)
  2. Download certificate via azure powershell (pfx)
  3. Create Azure App Instance to identify my app
  4. Associate certificate with app
  5. Create service principal for the azure app
  6. 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?

James Wierzba
  • 16,176
  • 14
  • 79
  • 120

3 Answers3

14

An alternative easier way to grant NETWORK SERVICE user permission on certificate private key (easier than my other answer):

  1. Open certificate snap-in in MMC: WIN + R -> type mmc -> File -> Add/Remove Snap-in -> Add Certificates (Computer Account).
  2. Find your certificate -> Right click and choose All Tasks/Manage Private Keys
  3. Grant Read Permission for NETWORK SERVICE user
James Wierzba
  • 16,176
  • 14
  • 79
  • 120
  • Works perfectly, great, thank you :) An additon: If your windows service (or other exe) is working with a custom account, you need to grant this account as well. – Ahmet Altun Mar 14 '23 at 08:25
0

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)

James Wierzba
  • 16,176
  • 14
  • 79
  • 120
0

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

  1. define a service principal corresponding to local machine
  2. 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>