1

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 呼び出しに関係している可能性があることを示唆する記事をどこかで見つけたのを覚えていますが、リンクが見つかりません。

私は本当に探しています:

  1. 証明書の読み込みに 6 秒かからないようにユーザー ログインを修正する方法
  2. 自分のコードが正常であることを確認して、システムを使用している他の人に再発しないようにする方法

助けてくれてありがとう。

4

1 に答える 1

1

この問題は現在解決されており、その理由が理解できたと思います。

秘密キーを使用して 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 経由でパスワードを変更できませんでした。

私の問題は解決しました。おそらくかなりユニークだったと思いますが、私の調査の詳細がいつか他の誰かに役立つことを願っています!

于 2016-08-03T07:22:39.697 に答える