Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private key file not got deleted for X509Certificate2 after process exit correctly for .net core program #69549

Open
michaelkira opened this issue May 19, 2022 · 6 comments
Labels
area-System.Security question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@michaelkira
Copy link

We detected there is different behavior for X509Certificate2 with private key in .net core program and .net framework program.
We use same api in both platform
X509Certificate2 temp = new X509Certificate2(certBytes, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
The certBytes contains private key, so after X509Certificate2 object is created, there will be a private key file created in the system.
image
For both platfrom, we dont call dispose method of X509Certificate2.
Then we found, in .net framework, if the process exit correctly, even we dont call dispose, the private key file will also be deleted after process exit.
While in .net core platform, the private key file will stay in file system and got leaked.
Not sure if this behavior difference is by design or not, it already caused some prod outage in our side, so want to check with the platform team to see which one is expected.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-NetSDK untriaged New issue has not been triaged by the area owner labels May 19, 2022
@danmoseley danmoseley transferred this issue from dotnet/sdk May 19, 2022
@ghost
Copy link

ghost commented May 19, 2022

Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

We detected there is different behavior for X509Certificate2 with private key in .net core program and .net framework program.
We use same api in both platform
X509Certificate2 temp = new X509Certificate2(certBytes, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
The certBytes contains private key, so after X509Certificate2 object is created, there will be a private key file created in the system.
image
For both platfrom, we dont call dispose method of X509Certificate2.
Then we found, in .net framework, if the process exit correctly, even we dont call dispose, the private key file will also be deleted after process exit.
While in .net core platform, the private key file will stay in file system and got leaked.
Not sure if this behavior difference is by design or not, it already caused some prod outage in our side, so want to check with the platform team to see which one is expected.

Author: michaelkira
Assignees: -
Labels:

area-System.Security, untriaged

Milestone: -

@bartonjs
Copy link
Member

.NET Framework more eagerly runs garbage collection and finalization on process exit than .NET Core/.NET5+ does.

If the process exits without having already run the finalizer for the object (which would clean up the private key file) then it will be left on disk, the same as if it had been loaded with PersistKeySet or the process terminated abnormally.

For best results, use EphemeralKeySet when you can (e.g. when not using SslStream/HttpClient, or macOS); and always Dispose the X509Certificate(2) objects when they are no longer needed.

@bartonjs bartonjs added question Answer questions and provide assistance, not an issue with source code or documentation. and removed untriaged New issue has not been triaged by the area owner labels May 19, 2022
@bartonjs bartonjs added this to the Future milestone May 19, 2022
@michaelkira
Copy link
Author

.NET Framework more eagerly runs garbage collection and finalization on process exit than .NET Core/.NET5+ does.

If the process exits without having already run the finalizer for the object (which would clean up the private key file) then it will be left on disk, the same as if it had been loaded with PersistKeySet or the process terminated abnormally.

For best results, use EphemeralKeySet when you can (e.g. when not using SslStream/HttpClient, or macOS); and always Dispose the X509Certificate(2) objects when they are no longer needed.

Thanks for take a look at this issue.
I tested again, it is true that .NET Core some time will also call the garbage collection with process exit, but the possibility is very low while .net framework will call GC everytime in my case.
So for dispose, yes, I very agree that we should dispose the cert object once we finished using it.
But the real case we have is, we have a lot of singleton service which is created by dependency injection (DI), the cert is hosted as a member of these singleton service means we expect the lifecycle of these object is as long as the process running time. That's why we count on process exit will recycle these singleton service.
But it does not work on .net core which cause there will always be some leaked cert key file every time process restarted. Do we have any best practice to make sure all singleton service will be disposed when process exit?
Also can you provide more information about this statement?
use EphemeralKeySet when you can (e.g. when not using SslStream/HttpClient, or macOS)
We are considering using EphemeralKeySet but we did find there is behavior change when using it. (Like no plaintext export for EphemeralKeySet )

@bartonjs
Copy link
Member

Do we have any best practice to make sure all singleton service will be disposed when process exit?

You could add

GC.Collect();
GC.WaitForPendingFinalizers();

after everything has run, though I have no idea if the DI system will have released references to them, or not.

Alternatively, when you create them you could register them into a static field/collection and register for the AppDomain.CurrentDomain.ProcessExit event, and manually call Dispose on your certificate in that callback.

@michaelkira
Copy link
Author

Thanks, @bartonjs Also can you provider more information about this statement
use EphemeralKeySet when you can (e.g. when not using SslStream/HttpClient, or macOS)

@bartonjs
Copy link
Member

EphemeralKeySet means the key never gets written to disk. SslStream/HttpClient on Windows can't work with certs of that form (because of an OS limitation). macOS doesn't support certificate+key pairs that aren't associated with a Keychain, which is on-disk, so it can't load them at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Security question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

3 participants