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调用有关,但我找不到链接。
我真的在寻找:
- 如何修复我的用户登录,这样加载证书就不需要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更改密码。
我的问题解决了。我很感激它可能相当独特,但我希望我的调查细节在某个时候能帮助到其他人!