2
private static X509Certificate2 FindCertificate(string certificateSubject)
{
    const StoreName StoreName = StoreName.My;
    const StoreLocation StoreLocation = StoreLocation.LocalMachine;

    var store = new X509Store(StoreName, StoreLocation); 
    try
    {
        store.Open(OpenFlags.ReadOnly);

        // Find with the FindBySubjectName does fetch all the certs partially matching the subject name.
        // Hence, further filter for the certs that match the exact subject name.
        List<X509Certificate2> clientCertificates =
            store.Certificates.Find(X509FindType.FindBySubjectName, certificateSubject, validOnly: true)
                .Cast<X509Certificate2>()
                .Where(c => string.Equals(
                    c.Subject.Split(',').First().Trim(),
                    string.Concat("CN=", certificateSubject).Trim(),
                    StringComparison.OrdinalIgnoreCase)).ToList();

        if (!clientCertificates.Any())
        {
            throw new InvalidDataException(
                string.Format(CultureInfo.InvariantCulture, "Certificate {0} not found in the store {1}.", certificateSubject, StoreLocation.LocalMachine));
        }

        X509Certificate2 result = null;
        foreach (X509Certificate2 cert in clientCertificates)
        {
            DateTime now = DateTime.Now;
            DateTime effectiveDate = DateTime.Parse(cert.GetEffectiveDateString(), CultureInfo.CurrentCulture);
            DateTime expirationDate = DateTime.Parse(cert.GetExpirationDateString(), CultureInfo.CurrentCulture);
            if (effectiveDate <= now && expirationDate.Subtract(now) >= TimeSpan.FromDays(1))
            {
                result = cert;
                break;
            }
        }

        return result;
    }
    finally
    {
        store.Close();
    }
}

ライブラリにこのコードがあり、新しいリクエストが作成されるたびにこのメソッドが呼び出されます。したがって、基本的には1秒あたりのリクエスト数が1000で、1000回呼び出されます。PerfView ツールを使用していたとき、CPU の 35% がこの方法で使用されていることに気付きました。最大の原因は、store.Open および store.Certificates.Find メソッドです。

他の誰かが自分のコードで同様の問題を発見しました。また、これによるパフォーマンスへの影響を解決するために何をしたかを共有できる場合。

4

1 に答える 1

4

ターゲット システムに圧倒的に多数の証明書がインストールされていない限り、X509Store の.Find()メソッドの呼び出しをスキップできます。私の経験では、それはあまりうまく機能せず、subjectName後でターゲットに必要なフィルタリングをすでに行っています。

X509Certificate2また、 2 回のコレクションをループしないでください。すべての基準を満たす最初の一致する証明書だけが必要な場合は、次のような単一の LINQ ステートメントに単純化できます。

X509Certificate2 cert =
    store.Certificates.Cast<X509Certificate2>()
        .FirstOrDefault(xc =>
            xc.Subject.Equals("CN=" + certificateSubject, StringComparison.OrdinalIgnoreCase)
            && xc.NotAfter >= DateTime.Now.AddDays(-1)
            && xc.NotBefore <= DateTime.Now);

(使用法と証明書によっては、元のコードのようにコンマで件名を分割するために上記を変更する必要がある場合とない場合があることに注意してください)。

最後に、Wiktor Zychla が述べたように、ターゲット マシンに多数の証明書がインストールされていない場合は、 を呼び出して証明書のリスト全体をキャッシュするか、検索する証明書store.Certificates.Cast<X509Certificate2>().ToList()の数が限られている場合は、より効率的かもしれません。 subjectNamessubjectName から派生したキーとプロパティに基づく有効期限を使用して、このメソッドの結果を単純にキャッシュしNotAfterます。

于 2015-11-05T21:42:28.880 に答える