Search code examples
c#windows-10garbage-collectionx509certificate2certificate-store

C# X509Certificate2 private key disappear when GC occurs


I am experiencing a really weird encryption certificate problem. I install a .p12 file with 1 encryption cert into StoreName.MY, everything seems to work fine, I open the Certificate Manager and could see the cert installed and I can export it with the private key. However, after a while, the private key would disappear, and I can no longer export the cert with the private key anymore.

The code used to install the cert is really simple and straightforward:

using (X509Store store = new X509Store(StoreName.My))
{
    var collection = new X509Certificate2Collection();
    // the p12 only have 1 cert (encryption) inside
    byte[] data = Convert.FromBase64String(some.P12);
    collection.Import(data, password,
                      X509KeyStorageFlags.Exportable |
                      X509KeyStorageFlags.PersistKeySet |
                      X509KeyStorageFlags.UserKeySet);

    store.Open(OpenFlags.ReadWrite);
    collection.ToList().ForEach((cert) => store.Add(cert));
    store.Close();

    foreach(X509Certificate2 cert in collection)
    {
        cert.Reset();
        cert.Dispose();
    }
}

I even took the extra steps to go through the collection and Reset + Dispose every single cert that was installed. However, after some undetermined amount of time, garbage collect would happen and the private key as well as all other previously installed encryption certificate's private keys would ALL be gone... It doesn't matter if those encryption certificates were installed today, few days ago or few weeks, all of them would be gone.

I've been looking around on the interweb for the past day and couldn't find anything that could point me to the right direction, as you could see from the code snippet above, I even took extra step to close all the X509Store and Dispose of all the X509Certificate2 objects.

The flags (PersistKeySet) used for the import was also specifically chosen trying to solve the problem. Has anyone experienced this behavior? Is it caused by garbage collector?

Environment:

  • Windows 11, Windows 10, Windows 7
  • C#, .NET Core3.1 and .NET Framework 4.7

Solution

  • The root cause is due to some bug(s) in the old framework, I ran the same code using .NET 8 and the problem went away... however, since I cannot just switch over the old application to use .NET 8, I had to keep everything as-is under Core 3.1 and compile a new small executable containing only the piece of code under pure .NET 8, then run it via Process.Start() from the old application. It is not entirely elegant but it worked.