In my application, a Certificate for Client-Authentication is programatically added to the MY
-Store using the following code:
//certData is a byte[]
//password is a SecureString
X509Certificate2 certificate = new X509Certificate2(certData, password, X509KeyStorageFlags.Exportable);
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
}
finally
{
store.Close();
}
With this code, the certificate was correctly imported into the MY
-Store (thumbprint and certification chain also correct) on all machines I tested.
But on some machines (Windows 7 Professional SP1 and Widnows Server 2008 R2 with local user account) the Certificate could afterwards not be used for client-authentication ("Could not establish trust relationship for the SSL/TLS secure channel"). On a Windows 8.1 Enterprise machine with domain user account, authentication worked sometimes but not always.
I desperatly tried a couple of things, and finally found a solution in adding X509KeyStorageFlags.PersistKeySet
to the storage flags.
So the first line is now:
X509Certificate2 certificate = new X509Certificate2(certData, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
With these flags, the certificate could be used on all devices. Even though I am happy that my application now works in the expected way, I would like to understand why? What exactly does the PersistKeySet-Flag do and why does it have an impact on when and by whom the certificate can be used?
MSDN was not very helpful in this case.
When importing a PFX the public certificate element is loaded into memory, and the private key material is squirreled away into a key storage provider. The default behavior in .NET is to delete the private key material when the X509Certificate2 object is Disposed (or its resources are being Finalized via Garbage Collection). The PersistKeySet
flag prevents this cleanup from happening.
If you're adding to a persisted certificate store, you always want to set PersistKeySet
. When not adding to a persisted store you very likely do not want it set.
If your importing process is long-lived then the behavior you'd see is that at an arbitrary time after the import new accesses to the private key start failing. If it's short-lived then it probably always failed to work.
In my understanding, the PersistKeySet
flag, if specified, it persists on disk the private key of the imported PFX at the same location from where certificate that was the source of the exported PFX (userkeyset or machinekeyset). If the PFX was generated by a tool (e.g. pvk2pfx.exe) then there is no source and default is used (userkeyset).
In this case, if the private key of certificate that was the source of the exported PFX was stored at machinekeyset, then the private key will be imported at machinekeyset, here: \ProgramData\Microsoft\Crypto\RSA\MachineKeys
.
Otherwise, it will be stored at userkeyset, here:
\Users\user\AppData\Roaming\Microsoft\Crypto\RSA\S-1-...
.
If you want your certificate to be available across the machine, disregarding the origin location of the PFX, you might want to consider using the MachineKeySet
instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With