创建 X509Certificate2 时防止创建文件?

问题描述 投票:0回答:5

我们在 ASP.NET 应用程序中创建一个 X509Certificate2 对象来定期进行传出连接。每次创建这些证书之一时,都会在以下位置创建一个新文件:

C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys

该文件夹现在有 400 万个文件从未被清理过。我尝试删除 Persist 标志

新 X509Certificate2(certBytes, p12Pwd, X509KeyStorageFlags.MachineKeySet);

//没有 X509KeyStorageFlags.PersistKeySet

但这并没有帮助——每次调用时仍然会获取 2Kb 文件。

当我看到这个答案时,我充满了希望,但这是一个2008 R2的服务器,并且临时文件不是0字节,所以它似乎是一个不同的情况。

如何在不填满磁盘的情况下使用X509Certificate2?

c# ssl certificate x509certificate x509certificate2
5个回答
6
投票

使用.NET 4.6

X509Certificate2 实现了 IDisposable 接口,开头为 .NET 框架 4.6;在以前版本的 .NET Framework 中, X509Certificate2 类未实现此接口,并且 因此 Dispose 方法不存在。


6
投票

我的服务器上也有同样的问题。我目前发现的最好方法是从我的代码中删除文件。

using System;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Byte[] bytes = File.ReadAllBytes(@"D:\tmp\111111111111.p12");
            X509Certificate2 x509 = new X509Certificate2(bytes, "qwerty", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
            var privateKey = x509.PrivateKey as RSACryptoServiceProvider;
            string uniqueKeyContainerName = privateKey.CspKeyContainerInfo.UniqueKeyContainerName;
            x509.Reset();

            File.Delete(string.Format(@"C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\{0}", uniqueKeyContainerName));
        }
    }
}

5
投票

为了重现您的问题,我创建了此示例代码。我的测试环境是 Windows 8.1 64 位,应用程序是用 .NET 4.5 编写的。

using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var certBytes = File.ReadAllBytes(@"c:\cert.p12");
            var p12Pwd = "somepassword";

            for (var i = 0; i < 1000; i++)
            {
                var cert = new X509Certificate2(certBytes, p12Pwd, X509KeyStorageFlags.MachineKeySet);

                // this line helped keep filesize from growing   
                // cert.Reset(); 
            }
        }
    }
}

令我震惊的是

C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
的文件大小竟然达到了2MB。然后应用程序退出,文件大小下降到 20K(这可能是它的起始大小)。

然后我添加了

cert.Reset();
(我已经在上面的代码中注释了它)。当您不再需要
X509Certificate2
实例时,应该调用此方法。之后,
C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
的文件大小在20K和22K之间波动。

所以我的建议是当您不再需要

cert.Reset();
实例时调用
X509Certificate2


2
投票

我的服务器上也有同样的问题。我目前发现的最好方法是通过脚本删除文件(有用的文件除外)。

例外列表:

  • Microsoft Internet 信息服务器 -> c2319c42033a5ca7f44e731bfd3fa2b5 ...
  • NetFrameworkConfigurationKey -> d6d986f09a1ee04e24c949879fdb506c ...
  • iisWasKey -> 76944fb33636aeddb9590521c2e8815a ...
  • WMSvc 证书密钥容器 -> bedbf0b4da5f8061b6444baedf4c00b1 ...
  • iisConfigurationKey -> 6de9cb26d2b98c01ec4e9e8b34824aa2 ...
  • MS IIS DCOM 服务器 -> 7a436fe806e483969f48a894af2fe9a1 ...
  • TSSecKeySet1 -> f686aace6942fb7f7ceb231212eef4a4 ...
  • https://forums.whirlpool.net.au/archive/1683713

通过 https://forums.iis.net/t/1224708.aspx?C+ProgramData+Microsoft+Crypto+RSA+MachineKeys+is+filling+my+disk+space

此解决方案仍然是一个补丁。最好不要在 MachineKeys 文件夹中生成文件。


0
投票

我尝试使用我在这里找到的建议,但就我而言,它最终失败了。 虽然文件夹开始变小,但还是随机留下了一些垃圾。

我的情况:我正在加载要在 HttpClient 中使用的证书,并且它似乎卡在 HttpClient 中。

我已经实现了所有内容的超类(X509Certificate2、HttpClientHandler、HttpClient)来简化工作,但不知何故,Dispose() 方法没有被调用。从来没有。

也许它与 HttpClient 池有关,但它似乎被设计为失败 - 无论你做什么,某些东西都会在另一个地方失败。

所以我决定编写一个服务,在一段时间后删除它们,最后我设法保持文件夹干净。

这里是:

public class AutoResetX509Certificate2 : X509Certificate2
{
    string autoGeneratedFile = "";
    const string keyFolder = @"C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys";

    public AutoResetX509Certificate2(byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) : base(rawData, password, keyStorageFlags)
    {
        var uniqueName = ((System.Security.Cryptography.RSACng)this.PrivateKey).Key.UniqueName;
        autoGeneratedFile = Path.Combine(keyFolder, uniqueName);
        AddFileToDeletion(autoGeneratedFile);
        StartAutoResetService();
    }

    public AutoResetX509Certificate2(byte[] rawData) : base(rawData)
    {
        var uniqueName = ((System.Security.Cryptography.RSACng)this.PrivateKey).Key.UniqueName;
        autoGeneratedFile = Path.Combine(keyFolder, uniqueName);
        AddFileToDeletion(autoGeneratedFile);
        StartAutoResetService();
    }

    protected override void Dispose(bool disposing)
    {
        StopAutoResetService();
        base.Dispose(disposing);
    }

    private void AddFileToDeletion(string path)
    {
        var folder = Path.Combine(keyFolder, "!deletionTrack");
        Directory.CreateDirectory(folder);
        var fi = new FileInfo(path);
        var file = Path.Combine(folder, fi.Name);
        File.WriteAllText(file, "");
    }

    //======================= AutoResetService

    static System.Timers.Timer timer = new() { Enabled = false, Interval = 5000 };

    static bool isTimerBusy = false;

    internal static void StartAutoResetService()
    {
        lock (timer)
        {
            if (!timer.Enabled)
            {
                timer.Elapsed += Timer_Elapsed;
                timer.Enabled = true;
            }
        }
    }

    internal static void StopAutoResetService()
    {
        lock (timer)
        {
            timer.Enabled = false;
            timer.Elapsed -= Timer_Elapsed;
        }
    }

    private static void RunAutoReset()
    {
        var folder = Path.Combine(keyFolder, "!deletionTrack");
        var di = new DirectoryInfo(folder);
        if (di.Exists)
        {
            var olders = di.GetFiles().Where(fi => DateTime.Now.Subtract(fi.CreationTime).TotalSeconds > 30).ToList();
            foreach (var old in olders)
            {
                var certPath = Path.Combine(keyFolder, old.Name);
                if (File.Exists(certPath)) File.Delete(certPath);
                old.Delete();
            }
        }
    }

    private static void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        if (isTimerBusy) return;
        isTimerBusy = true;
        try
        {
            RunAutoReset();
        }
        catch(Exception)
        {
            //implement your logger here if you need it.
        }
        finally
        {
            isTimerBusy = false;
        }
    }
}

您只需使用该类(AutoResetX509Certificate2)而不是 X509Certificate2 加载证书。

//example:
var clientCert = new AutoResetX509Certificate2(pfxBytes, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

注意我实现了我需要的两个构造函数,如果您需要另一个构造函数,您可以像这两个一样实现。

© www.soinside.com 2019 - 2024. All rights reserved.