2015-11-05 6 views
1
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(); 
    } 
} 

У меня есть этот код в моей библиотеке, и каждый раз, когда создается новый запрос, он вызывает этот метод. Так что в основном запросы в секунду составляют 1000, тогда это будет называться 1000 раз. Когда я использовал инструмент PerfView, я заметил, что этим методом используется 35% процессора. Самый крупный виновник - это магазин. Откройте и сохраните. Сертификаты. Метод.Эффект воздействия из-за store.Открыть и хранить. Сертификаты. Способы использования в X509Store

Никто другой не обнаружил подобные проблемы в своем коде. Кроме того, если вы можете поделиться тем, что вы сделали, чтобы решить проблему с производительностью из-за этого.

+1

Почему вы не читаете сертификаты один раз и не кэшируете их в словаре? –

+0

Да, это звучит как хорошее решение. Есть ли проблема безопасности, о которой я должен думать. Один из них также отслеживает дату истечения срока действия. – StackOverflowVeryHelpful

+0

Умный кеш может обрабатывать такие данные, как дата истечения срока действия. Кроме того, если это веб-приложение, пул перезапускается каждые 24 часа или около того, есть несколько случаев для обработки истекших сертификатов. –

ответ

2

До тех пор, пока целевая система не имеет в наличии огромного количества установленных сертификатов, вы можете пропустить вызов метода X509Store .Find(). По моему опыту он работает не очень хорошо, и вы уже делаете необходимую фильтрацию для своей цели subjectName.

Кроме того, не прокручивайте коллекцию X509Certificate2 дважды! Если вы просто хотите, первый сертификат соответствия, который отвечает всем вашим критериям, вы можете упростить вещи одной инструкции 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(), или если у вас есть ограниченное количество subjectNames, что вы искать , было бы более эффективно просто кэшировать результат этого метода с использованием ключа, полученного из subjectName, и истечения срока действия на основе свойства NotAfter.