1

When trying to get a X509Certificate2 object from the X509Store using the following code:

    private X509Certificate2 GetKey()
    {
        try
        {
            X509Store store = new X509Store("WebHosting", StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            var collection = store.Certificates.Find(X509FindType.FindBySubjectName, "xxxxxxx", true);

            if (collection.Count == 0)
            {
                throw new Exception("No keys matched");
            }

            if (collection.Count > 1)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("More than 1 key matched:\r\n");

                foreach (var cert in collection)
                {
                    sb.Append($"{cert.SubjectName} - {cert.Thumbprint}\r\n");
                }
                throw new Exception(sb.ToString());
            }

            return collection[0];
        }
        catch (Exception ex)
        {
            // something really bad happened, log it
            Logger.LogException(ex);
            throw;
        }
    }

I successfully get the key; however, when trying to get the private key within the object using key.PrivateKey, I get the following error: OpenCSP failed with error code 2148073494. Looking up the Windows error 2148073494, I get nte_bad_keyset. It looks like there have been errors around other situations that throw this same error here, but they closed that bug as fixed. When I run this bit of code in a console app, it works fine and it also works fine in my test environment running under IISExpress. When run in a production environment under IIS, I get this error every time. I've tried running under the context of an admin user just to make sure it wasn't an odd permission error, same thing. From what I understand about this Windows error, is that Windows gave me where the key lives, then told me there is nothing at that address. I'm using "System.Security.Cryptography.Algorithms": "4.3.0" for this.

EDIT: I should note that as part of my testing, I actually grabbed the exact certificate I'm looking for from the production environment onto my test environment and it loaded fine. I also ran the console app on the production environment pulling the same key and it worked fine.

Middas
  • 1,862
  • 1
  • 29
  • 44
  • How did the certificate get onto the production machine? If you used .NET to read a PFX and add it to the cert store, but didn't set the `X509KeyStorageFlags.PersistKeySet` flag when loading the PFX you'll eventually get this behavior (after the key gets "un-persisted"). – bartonjs Jan 05 '17 at 22:12
  • I don't use .NET to add the certificate to the store. The PFX file is added to the store and updated via an ACME application. This is an entirely separate process and is not part of my project. – Middas Jan 05 '17 at 22:35
  • Are you sure your IIS app pool user has access to the private key for this certificate? – Frode Nilsen Mar 30 '17 at 12:22

1 Answers1

0

Ultimately, the answer is "something deleted the private key after importing it to the certificate store" (or, maybe it's possible to confuse Windows into remembering where a key lives despite it not actually living there).

If you know that it, for example, works for a little while then stops:

> certutil -store my
...
================ Certificate 6 ================
Serial Number: 3451b93c10f9279348a949f729d1ff10
Issuer: CN=localhost
 NotBefore: 1/26/2015 2:19 PM
 NotAfter: 1/25/2020 4:00 PM
Subject: CN=localhost
Signature matches Public Key
Root Certificate: Subject matches Issuer
Template:
Cert Hash(sha1): 15 e3 4c d3 2d a7 54 99 a9 17 8f 17 26 25 63 25 8f 3a 94 28
  Key Container = IIS Express Development Certificate Container
  Unique container name: fad662b360941f26a1193357aab3c12d_1fcb2e07-cec4-4ba1-9c78-58a431e1aefd
  Provider = Microsoft RSA SChannel Cryptographic Provider
Encryption test passed
CertUtil: -store command completed successfully.

Seeing that it's in "Microsoft RSA SChannel Cryptographic Provider", turn to https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx and see that the keyfile will be located at %ALLUSERSPROFILE%\Application Data\Microsoft\Crypto\RSA\MachineKeys. The Unique container name happens to be the name of the file it'll have.

So, open that directory, right click on the file.

  • Properties
  • Security Tab
  • Advanced button
  • Auditing tab
  • Edit button
  • Add button
  • Type "Everyone", push "Check Names", pick "Everyone", hit OK, hit OK.
  • Check Delete: Success.
  • Hit OK to dismiss all the dialogs.

Later, after you start getting keyset errors, search the security log for an audit on the file delete (event 4663 from Security-Auditing):

An attempt was made to access an object.

Subject:
    Security ID:        SOMEDOMAIN\theaccount
    Account Name:       theaccount
    Account Domain:     SOMEDOMAIN
    Logon ID:       0xabcdef

Object:
    Object Server:  Security
    Object Type:    File
    Object Name:    C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\fad662b360941f26a1193357aab3c12d_1fcb2e07-cec4-4ba1-9c78-58a431e1aefd
    Handle ID:  0xef8

Process Information:
    Process ID: 0xf54
    Process Name:   C:\Windows\explorer.exe

Access Request Information:
    Accesses:   DELETE

    Access Mask:    0x10000

That will tell you what process/user did the delete operation... and maybe that'll be enough to identify what went wrong.

You could presumably do the private key file identification and audit registration more programmatically; but this was the quickest way I knew to explain it.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • I tried what you suggested, it made no difference and there wasn't a single event log entry around the certificate. I don't understand how the key can be deleted when at the same time the website can't access the key, my test application can. – Middas Jan 06 '17 at 04:39