TLS/SSL を使用して通信する C#、.NET4.5.2 クライアント/サーバー システムを開発しました。証明書はファイルからロードされます。「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();
}
何人かの同僚に、自分の PC でプログラムを実行するように依頼しました。また、VM 内で実行してから、自分のラップトップで新しいユーザーをセットアップしてみました。すべての場合で約 0.1 秒で実行されましたが、通常のユーザー ログインでは 6 秒を超えて実行されました。
最初は証明書で「Reset()」を呼び出していなかったので、一時ファイルのどこかに問題があるのではないかと考え、procmon を使用して何が起こっているのかを調べました。次のディレクトリにいくつかの一時ファイルが作成されていることを確認しました (ただし、Reset() を呼び出さなくても、アプリケーションが終了すると整理されます)。
C:\Users\<username>\AppData\Roaming\Microsoft\Crypto\RSA\<SID>
念のため、このディレクトリ内のファイルを削除しようとしましたが、違いはありません。
procmon を使用すると、証明書の読み込み中にファイル/レジストリ アクティビティに 2 つのギャップがあり、読み込みが高速なシステムでは発生しないことがわかります。最初は「dpapi.dll」を使用しようとした直後です。2 番目は、次の「C:\Extend\$UsnJrnl:$J:$DATA」への読み取りの後です。DPAPI.dll は、Windows データ保護のインターフェイスです。後者のファイルは、ファイルの変更を記録する NTFS の USN ジャーナルです。私はどちらの専門家でもなく、どちらが関連しているかどうかもわかりません!
次に、API Monitor http://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 秒かからないようにユーザー ログインを修正する方法
- 自分のコードが正常であることを確認して、システムを使用している他の人に再発しないようにする方法
助けてくれてありがとう。