X509证书构造函数采用>;为特定用户执行6秒

本文关键字:用户 6秒 执行 gt 构造函数 证书 X509 | 更新日期: 2023-09-27 17:57:55

我开发了一个C#、.NET4.5.2客户端/服务器系统,该系统使用TLS/SSL进行通信。证书是从文件中加载的。我使用"MakeCert"实用程序创建了证书文件,以创建.pvk和.cer文件,然后使用"pvk2pfx"实用程序将它们组合到.pfx中。

要使用证书,我使用X509Certificate2构造函数加载它们,重载后以字符串形式传入文件路径和密码:

X509Certificate2(string filePath, string password)

随着时间的推移,我注意到证书的加载变得非常缓慢。我不确定是否有"事件"使它们变慢,或者是逐渐的,但现在加载PFX文件需要大约6秒。加载CER文件没有问题,大约需要0.1秒。

我运行的是Windows 8.1,问题只针对我的用户在笔记本电脑上登录。

我编写了以下测试应用程序来验证问题:

    private const string filePath = @"c:'testcert.pfx";
    private const string password = "testpassword";
    static void Main(string[] args)
    {
        var stopwatch = new Stopwatch();
        try
        {
            Console.WriteLine("About to create certificate. Press Enter.");
            Console.ReadLine();
            stopwatch.Start();
            var cert = new X509Certificate(filePath, password);
            stopwatch.Stop();
            Console.WriteLine(stopwatch.Elapsed);
            Console.WriteLine("Certificate created. About to reset.  Press Enter.");
            Console.ReadLine();
            cert.Reset();
            Console.WriteLine("Certificate reset.  Press Enter.");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.ReadLine();
    }

我让一些同事在他们的电脑上运行这个程序。我还尝试在虚拟机中运行,然后在自己的笔记本电脑上设置一个新用户。在所有情况下,它运行约0.1秒,但对于我的普通用户登录,它运行时间>6秒。

起初,我没有在证书上调用"Reset()",所以我认为可能某个地方的一些临时文件有问题,所以我使用procmon来了解发生了什么。我发现一些临时文件正在以下目录中创建(尽管当应用程序退出时,即使没有调用Reset(),它们也会被清理):

C:'Users'<username>'AppData'Roaming'Microsoft'Crypto'RSA'<SID>

只是为了确保我已经尝试删除这个目录中的文件,但没有任何区别。

使用procmon,我可以看到在加载证书的过程中,文件/注册表活动有两个间隙,这在加载速度快的系统中是不会发生的。第一个是在它尝试使用"dpapi.dll"之后。第二个是在读取以下"C:''Extend''$UsnJrnl:$J:$DATA"之后。DPAPI.dll是Windows数据保护的接口。后一个文件是用于NTFS的USN日志,用于记录文件更改。我不是这两者的专家,我不确定这两者是否相关!

然后我尝试使用API Monitorhttp://www.rohitab.com/apimonitor以观察系统调用。再说一次,我不是专家,但我仔细查看了一下暂停前发生了什么。其中有很多我不明白的地方,可能是相关的,也可能不是相关的,我欢迎对其中的任何一个发表评论,以帮助集中精力解决问题。

在2秒间隔之前的最后一个调用是具有以下调用堆栈的memcpy:

#   Module  Address Offset  Location
1   RPCRT4.dll  0x74f6378b  0x2378b I_RpcSendReceive + 0x1bb
2   RPCRT4.dll  0x74f6367b  0x2367b I_RpcSendReceive + 0xab
3   RPCRT4.dll  0x74f594df  0x194df NdrServerInitializeNew + 0x83f
4   RPCRT4.dll  0x74f63619  0x23619 I_RpcSendReceive + 0x49
5   RPCRT4.dll  0x74f6398b  0x2398b NdrSendReceive + 0x2b

更高层次的潜在兴趣线似乎是:

#   Time of Day Thread  Module  API Return Value    Error   Duration
64922   6:38:44.348 AM  1   DPAPI.dll   SystemFunction040 ( 0x00ac5a30, 8, RTL_ENCRYPT_OPTION_SAME_LOGON )  STATUS_SUCCESS      0.0000402
64923   6:38:44.349 AM  1   CRYPTBASE.dll   RtlInitUnicodeString ( 0x0090e5a8, "'Device'KsecDD" )           0.0000004
64949   6:38:44.349 AM  1   RPCRT4.dll  RtlInitUnicodeString ( 0x0090e0b0, "'RPC Control'protected_storage" )           0.0000000

我发现很难跟踪调用堆栈,但我认为这些调用堆栈最终来自一个名为CryptQueryObject的函数。

我发现以下文章可能是相关的,但它不是Windows8.1。我删除了%windir%''Temp文件夹只是以防万一,但也没有帮助。

https://support.microsoft.com/en-gb/kb/931908

我记得在某个地方发现了一篇文章,认为延迟可能与CryptQueryObject的ActiveDirectory调用有关,但我找不到链接。

我真的在寻找:

  1. 如何修复我的用户登录,这样加载证书就不需要6秒
  2. 如何确保我的代码正常,这样它就不会再次发生或发生在其他使用该系统的人身上

谢谢你的帮助。

X509证书构造函数采用>;为特定用户执行6秒

这个问题现在已经解决了,我想我明白为什么了!

当您使用私钥加载X509Certificate时,Windows会将密钥存储到一个文件中,并使用"数据保护"对文件进行加密(因此,我监控了对DPAPI的调用)。

https://technet.microsoft.com/en-us/library/cc962112.aspx

用于加密文件的密钥称为"主密钥"。它基于您的用户登录,每当您更改使用密码时都会续订。它也会在90天后自动过期。

http://www.passcape.com/index.php?section=docsys&cmd=详细信息&id=28#33

如以上链接所述,主密钥存储在以下目录中:

%APPDATA%/Microsoft/Protect/%SID%

通过查看该目录中文件的"上次修改"日期,我可以看出我的MasterKey实际上已经超过90天了。因此,每当我加载证书时,它都试图将私钥存储在加密文件中,并注意到主密钥已过期,因此它会尝试更新它。

我重新运行了ProcMon,但停止了仅从测试应用程序中筛选活动。然后我可以看到,在"间隙"期间,一个名为lsass.exe的进程对我们的Active Directory服务器进行了多次UDP调用,该进程负责安全和密码更新(由于堆栈溢出限制,无法发布另一个链接,但易于搜索)。

我认为尝试更新主密钥的过程必须涉及到与ActiveDirectory服务的一些交互。

我远程工作,并通过VPN连接到我的工作网络。VPN不允许访问ActiveDirectory服务器,所以我认为延迟是由于尝试访问该服务器失败造成的。

我一直等到下一次去办公室,当我连接到网络并可以访问ActiveDirectory服务器时,加载证书的延迟就消失了。我确认我的MasterKey文件也已更新,不再过期。

我在通过VPN连接时重新测试了加载证书,延迟仍然没有,所以这似乎证实了只要主密钥没有过期就可以。

不幸的是,由于无法访问Active Directory服务的同样问题,我无法通过VPN更改密码。

我的问题解决了。我很感激它可能相当独特,但我希望我的调查细节在某个时候能帮助到其他人!