Connect-MGgraph - "Keyset does not exist" error when trying to authenticate with certificate

I’m working on updating our non-interactive scripts with the Micrsoft Graph cmdlets but have been running into this error when trying to authenticate with a certificate. I have a pretty basic understanding of certificates, but I’ll try to explain that part as best I can.

The app identity has been registered in our Azure tenant and a certificate was generated in-house by someone in our security group using a client/server authentication template. It’s been uploaded to the app id in Azure and deployed via group policy to Cert:\LocalMachine\Root\ on both of our machines.

$data = import-csv "\\drive\storage\example\AppIDInfo.csv" 
$cert = Get-ChildItem -Path "Cert:\LocalMachine\Root\$thumbprint" 
$tenantid = $data.tenantid 
$clientid = $data.appid 
Connect-MgGraph -Certificate $cert -TenantId $TenantId -ClientId $clientid

According to this thread on the SDK’s github, this should work.

The guy who generated the certificate is able to make it work, but I get the error - ClientCertificateCredential authentication failed: Keyset does not exist
when I run it from either of my machines. Elevated window or now.

I’m currently using the client secret as a workaround, but we’d like to get certificate-based authentication for app identities set up in the long run.

Any idea what I (or the security guy) can do?

Howdy!

So I happen to use certs and graph quite a bit :slight_smile: So you’ve installed the cert with the private key under local machine? In what context are you running the powershell session? If it’s not as an administrative window, that’s probably the issue. You’ll need to run the session as an admin. You might be able to also grant access to view the private key as well instead of running PS as an admin. I can’t remember.

Alternatively, you can install the cert with its private key under the current user (whatever user is running the process locally on the box), and that scenario should also work/be supported. I am fairly certain that’s what we do.

I’m guessing the guy who used the certificate can get it to work because he’s running PS as an admin :slight_smile:

1 Like

Yes, this is the way.
In certmgr.msc, right-click the certificate and select All Tasks > Manage Private Keys. Add the group or service account and grant it Read access.

1 Like

I prefer the other option myself, just install it under the user context for that specific local user but to each their own :smiley:

edit, thanks for note on how to do it, i couldnt remember those exact steps, and on my phone haha. I can never remember those steps and i have to always fumble through the GUI to find it.

Thank you both for the responses!

Both of us are running powershell as an admin, and anyone who will be running the scripts that utilize this app id in the future will be running it under admin context as well.

The certificate is being deployed via group policy to his and my machine. There isn’t a way to deploy it directly into the users personal store, but we learned that deploying it to the machine certificate store also puts it in the trusted root for the user certificate store.

That being said, you only have the option to right-click > manage on certs stored in the currentuser\personal store, not the trusted root store. I’ve seen that suggestion elsewhere though.

We used to take that approach, in fact I think in the earlier versions of the Graph SDK we were relying on it finding the certificate by thumbprint and it would only find the certificate if it was in the account’s store.

However, when one of our issuing CAs certificate was expiring, and we needed to update all certificates, we figured it was much easier to have the certificate in a central place, particularly if the App Registration was shared across multiple automations.

No you don’t, but you can only see the current user by default. Create a custom mmc to manage the computer certificates:

Start | Run | mmc
File > Add/Remove Snap-in
Certificates > Add
Computer Account > Next
Local Computer > Finish > OK

Ah cool. We’re probably got some more room to learn in this space. Right now, we actually use self-signed certs for these processes. I know there’s a ton of ‘warnings’ but we are using it for ourselves, so we haven’t really felt it was a security risk and our security team was okay with it. It’s not an app we expose to others for them to utilize, we actually are using it for our own purrposes. For example, we have an app that connects to graph purely for ‘metrics’ so we have granted it access to only reports endpoints.

yeah i mean iirc as long as you can read the private key from your session, you should be good to go. This might be helpful, in one of our scripts we have a comment that states:

   <# Graph only can see thumbprints that are in the local user store. To get around this limitation we are grabbing the entire cert and using 
            it to connect instead.
            #>

not directly what you are doing but what happens if you just use the cert itself to auth instead of the thumb?

For example the code i’m using looks something like this:

# Connect to Graph

        if ($CertConnection) {

            <# Graph only can see thumbprints that are in the local user store. To get around this limitation we are grabbing the entire cert and using 
            it to connect instead.
            #>

            $ConnectionSplat = @{
                TenantID    = "${script:AzureTenantPrefix}.onmicrosoft.com"
                AppID       = $script:AzureAppID
                Certificate = $script:AzureAppCert
            }

            Write-Verbose -Message "Tenant ID: $($ConnectionSplat.TenantID)"
            Write-Verbose -Message "AppID: $($ConnectionSplat.AppID)"
            Write-Verbose -Message "Certificate : $($ConnectionSplat.Certificate)"
    
            
            Connect-MgGraph @ConnectionSplat -ErrorAction Stop

$script:azureappcert is pulled from a different part of our module but we are just grabbing the entire cert and passing it to -Certificate. Probably worth a shot. looks like in our scneario we are using localmachine too for this module. Probably one of our older setups.

I can see the local computer cert store and the current user cert store. Adding either snap-in to MMC still doesn’t allow me to manage the private keys.

I believe when the cert was created by our inhouse CA, they disabled the ability to export the private key without a password. Would that also effect the ability to use the private key when I call the object in powershell?

the $cert variable in my code is grabbing the cert as an object and trying to pass it to the -certificate parameter, as it states in the thread I linked. Thats when I get this error.

I’m doing pretty much the same thing, but without putting the info into a new array.

(Made an edit to the original contents of my post so it doesn’t look so chewed up)

Ah - for some reason I thought you were using a thumbprint, Your code looks very similar to what i’m referencing for us on github for getting the cert. I am not using that path but its still very similar:

#If multiple certs exist, we'll assume the latest expire date is the one we want.
$script:AzureAppCert = Get-ChildItem -Path "Cert:\LocalMachine\My" | 
Where-Object {$_.Subject -eq "$Subject"} | 
Sort-Object -Property NotAfter -Descending | Select-Object -First 1

I may just need to get back to a computer so I can test,. I definitely ran into this issue before and I believe the fix was to allow the account running the session to read the private key. There could be other differences in our environments I’m not thinking of that is making it so your account cannot.

We may be referencing the same like - this one?

Yep, I’ve used the same where-object method you’re referencing, I’ve used the thumbprint in the path. It’s feels like grabbing it as an object is the most powershell way to do it, but I can’t get past the error.

I’ve seen several posts mention giving the option to read the public key, but I can’t find anything on this.

I mentioned this in an earlier reply to matt, but I don’t have the ability to manage private keys unless they’re in the personal store.

I brought this article up to the security guy yesterday when we were working together, and it seemed to him like the certificate was deployed with the proper permissions granted to my admin account.

I just reproduced the error, and I was correct. If you’re getting that error, the account you are using isn’t able to read the private key, at least from the current PS context. Note you can be ‘an admin’ on a box but not run PS as an admin, and it will not work. You can test/confirm this in a number of ways.

The best move you can do is to grant the account read access to the private key. To do this, you find the cert in question in certlm.msc , right click → all tasks → manage private keys, add the account with read access. That’s enough to make it work.

FWIW - this also works if you run PS as an admin, as i previously stated, but its better to simply grant access to the key, and run it as a non-admin.

Also, i’m not sure you should be putting the cert in root but I dunno. Generally, trusted root is for root certificates. Any corticates signed by those roots are trusted by your computer. In this case, the cert you are using is used for nothing more than authentication, so I don’t think it’s the best place to deploy that cert.

If you can’t manage the private key, you need to have you Security person grant read access ot the account in question, if they can’t, I suggest re deploying the certificate to the personal (my) area instead under local computer.

I think if you don’t have the little ‘private key’ icon on the top left of the icon, you don’t actually have the ‘private’ key with the cert.

When in doubt, you can simply generate a self-signed cert as a test and you’ll see what i’m saying is true :slight_smile:

1 Like

Sorry, it was late when I replied and I missed the bit where you said you were deploying to LocalMachine\Root. Windows doesn’t expect certificates in the Root store to have a private key and you won’t see the Manage Private Keys menu option for that store, even if the key icon is there to show a private key is present.

You can’t use GPO to deploy certificates to the Personal store so you will need to install the certificate manually, or script it, to drop it in the personal store (CERT:\LocalMachine\My).

I sorta figured that was the case - but I hadn’t played around with it enough to confirm.